need help with drizzle transactions and foreign key constraints
I have a circular database schema:
Since drizzle does not support sqlite deferred foreign key constraints, I've manually added
The error:
Related code:
Any ideas on why my deferrable fk's arent working? normally SQLite should only check the fks at the end of the transaction when
serviceservice references one service_generationservice_generation (as the latest_generation_idlatest_generation_id) and service_generationservice_generation references service.idservice.id, which poses an issue when trying to insert. To get around this, I am trying to use deferrable fk constraints + transactions.Since drizzle does not support sqlite deferred foreign key constraints, I've manually added
DEFERRABLE INITIALLY DEFERREDDEFERRABLE INITIALLY DEFERRED to the FOREIGN KEYFOREIGN KEY line in the migration.The error:
[2;30m22:17:46.594Z[0m [2;34mdebug[0m [2;36mdatabase:[0m[2;32m[0m PRAGMA defer_foreign_keys = true;
[2;30m22:17:46.594Z[0m [2;34mdebug[0m [2;32m[2;36mdatabase:[0m[2;32m[0m insert into "service" ("id", "name", "project_id", "latest_generation_id", "redeploy_secret", "deployed_generation_id", "created_at") values ((uuid_generate_v7()), ?, ?, ?, ?, null, CURRENT_TIMESTAMP) returning "id"
[2;30m22:17:46.596Z[0m [2;31merror[0m [2;36mtrpc:server:[0m Internal server error on [2;31mmutation[0m: [2;31mprojects.services.create[0m FOREIGN KEY constraint failed
SqliteError: FOREIGN KEY constraint failed
at PreparedQuery.values (/home/derock/Documents/Code/hostforge/node_modules/.pnpm/drizzle-orm@0.30.10_@libsql+client@0.6.0_@types+better-sqlite3@7.6.8_@types+react@18.2.46_bet_2qanqj7n56pb6fe7hpfsaedbzq/node_modules/src/better-sqlite3/session.ts:156:26)
[2;30m... 4 lines matching cause stack trace ...[0m
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
[stack]: [2;32m'SqliteError: FOREIGN KEY constraint failed\n' [0m+[0m[2;32m
' at PreparedQuery.values (node_modules/.pnpm/drizzle-orm@0.30.10_@libsql+client@0.6.0_@types+better-sqlite3@7.6.8_@types+react@18.2.46_bet_2qanqj7n56pb6fe7hpfsaedbzq/node_modules/src/better-sqlite3/session.ts:156:26)\n' [0m+[0m[2;32m
' at PreparedQuery.all (node_modules/.pnpm/drizzle-orm@0.30.10_@libsql+client@0.6.0_@types+better-sqlite3@7.6.8_@types+react@18.2.46_bet_2qanqj7n56pb6fe7hpfsaedbzq/node_modules/src/better-sqlite3/session.ts:124:21)\n' [0m+[0m[2;32m
' at QueryPromise.all (node_modules/.pnpm/drizzle-orm@0.30.10_[..truncated..]/node_modules/src/sqlite-core/query-builders/insert.ts:355:26)\n' [0m+[0m[2;32m
' at QueryPromise.execute (node_modules/.pnpm/drizzle-orm@0.30.10_[..truncated..]/node_modules/src/sqlite-core/query-builders/insert.ts:367:40)\n' [0m+[0m[2;32m
' at QueryPromise.then (node_modules/.pnpm/drizzle-orm@0.30.10_[..truncated..]/node_modules/src/query-promise.ts:31:15)\n' [0m+[0m[2;32m
' at process.processTicksAndRejections (node:internal/process/task_queues:95:5)'[0m[2;30m22:17:46.594Z[0m [2;34mdebug[0m [2;36mdatabase:[0m[2;32m[0m PRAGMA defer_foreign_keys = true;
[2;30m22:17:46.594Z[0m [2;34mdebug[0m [2;32m[2;36mdatabase:[0m[2;32m[0m insert into "service" ("id", "name", "project_id", "latest_generation_id", "redeploy_secret", "deployed_generation_id", "created_at") values ((uuid_generate_v7()), ?, ?, ?, ?, null, CURRENT_TIMESTAMP) returning "id"
[2;30m22:17:46.596Z[0m [2;31merror[0m [2;36mtrpc:server:[0m Internal server error on [2;31mmutation[0m: [2;31mprojects.services.create[0m FOREIGN KEY constraint failed
SqliteError: FOREIGN KEY constraint failed
at PreparedQuery.values (/home/derock/Documents/Code/hostforge/node_modules/.pnpm/drizzle-orm@0.30.10_@libsql+client@0.6.0_@types+better-sqlite3@7.6.8_@types+react@18.2.46_bet_2qanqj7n56pb6fe7hpfsaedbzq/node_modules/src/better-sqlite3/session.ts:156:26)
[2;30m... 4 lines matching cause stack trace ...[0m
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
[stack]: [2;32m'SqliteError: FOREIGN KEY constraint failed\n' [0m+[0m[2;32m
' at PreparedQuery.values (node_modules/.pnpm/drizzle-orm@0.30.10_@libsql+client@0.6.0_@types+better-sqlite3@7.6.8_@types+react@18.2.46_bet_2qanqj7n56pb6fe7hpfsaedbzq/node_modules/src/better-sqlite3/session.ts:156:26)\n' [0m+[0m[2;32m
' at PreparedQuery.all (node_modules/.pnpm/drizzle-orm@0.30.10_@libsql+client@0.6.0_@types+better-sqlite3@7.6.8_@types+react@18.2.46_bet_2qanqj7n56pb6fe7hpfsaedbzq/node_modules/src/better-sqlite3/session.ts:124:21)\n' [0m+[0m[2;32m
' at QueryPromise.all (node_modules/.pnpm/drizzle-orm@0.30.10_[..truncated..]/node_modules/src/sqlite-core/query-builders/insert.ts:355:26)\n' [0m+[0m[2;32m
' at QueryPromise.execute (node_modules/.pnpm/drizzle-orm@0.30.10_[..truncated..]/node_modules/src/sqlite-core/query-builders/insert.ts:367:40)\n' [0m+[0m[2;32m
' at QueryPromise.then (node_modules/.pnpm/drizzle-orm@0.30.10_[..truncated..]/node_modules/src/query-promise.ts:31:15)\n' [0m+[0m[2;32m
' at process.processTicksAndRejections (node:internal/process/task_queues:95:5)'[0mRelated code:
await db.transaction(async (trx) => {
// mark all deferrable
// error is same with and without this line
trx.run(sql`PRAGMA defer_foreign_keys = true;`);
// create the service
const [data] = await trx
.insert(service)
.values({
latestGenerationId: "",
[...]
})
.returning({
id: serviceGeneration.id,
});
assert(data?.id, "Expected service data to be returned");
// create initial generation
trx.run(sql`PRAGMA defer_foreign_keys = true;`);
const [generation] = await trx
.insert(serviceGeneration)
.values({
serviceId: data.id,
[...]
})
.returning({
id: serviceGeneration.id,
});
assert(generation?.id, "Expected generation data to be returned");
// update the service with the generation id
await trx
.update(service)
.set({
latestGenerationId: generation.id,
})
.where(eq(service.id, data.id))
.execute();
});await db.transaction(async (trx) => {
// mark all deferrable
// error is same with and without this line
trx.run(sql`PRAGMA defer_foreign_keys = true;`);
// create the service
const [data] = await trx
.insert(service)
.values({
latestGenerationId: "",
[...]
})
.returning({
id: serviceGeneration.id,
});
assert(data?.id, "Expected service data to be returned");
// create initial generation
trx.run(sql`PRAGMA defer_foreign_keys = true;`);
const [generation] = await trx
.insert(serviceGeneration)
.values({
serviceId: data.id,
[...]
})
.returning({
id: serviceGeneration.id,
});
assert(generation?.id, "Expected generation data to be returned");
// update the service with the generation id
await trx
.update(service)
.set({
latestGenerationId: generation.id,
})
.where(eq(service.id, data.id))
.execute();
});Any ideas on why my deferrable fk's arent working? normally SQLite should only check the fks at the end of the transaction when
commit;commit; is ran. PRAGMA defer_foreign_keys = true;PRAGMA defer_foreign_keys = true; should also completely disable fk checks until the end of the transaction.