From 196709bcccb61b97d0c433ff03484ecd44e1d2f6 Mon Sep 17 00:00:00 2001 From: yan-vikng-dev Date: Fri, 24 Oct 2025 22:16:26 +0700 Subject: [PATCH] [Sqlite-kit]: Avoid duplicate CREATE INDEX during push table recreation\n\nFixes duplicate index creation for SQLite/D1 push by removing index creation from the recreate-table helper; indexes are created once from planned create_index statements. Adds regression test to ensure a single CREATE UNIQUE INDEX is emitted.\n\nRefs: #3574 --- .../src/cli/commands/libSqlPushUtils.ts | 34 +++++-------- .../src/cli/commands/sqlitePushUtils.ts | 44 ++++++----------- drizzle-kit/tests/push/sqlite.test.ts | 49 +++++++++++++++++++ 3 files changed, 76 insertions(+), 51 deletions(-) diff --git a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts index 31e90c8722..85066d1edf 100644 --- a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts +++ b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts @@ -4,7 +4,6 @@ import { JsonStatement } from 'src/jsonStatements'; import { findAddedAndRemoved, SQLiteDB } from 'src/utils'; import { SQLiteSchemaInternal, SQLiteSchemaSquashed, SQLiteSquasher } from '../../serializer/sqliteSchema'; import { - CreateSqliteIndexConvertor, fromJson, LibSQLModifyColumn, SQLiteCreateTableConvertor, @@ -86,28 +85,17 @@ export const _moveDataStatements = ( }), ); - // rename table - statements.push( - new SqliteRenameTableConvertor().convert({ - fromSchema: '', - tableNameFrom: newTableName, - tableNameTo: tableName, - toSchema: '', - type: 'rename_table', - }), - ); - - for (const idx of Object.values(json.tables[tableName].indexes)) { - statements.push( - new CreateSqliteIndexConvertor().convert({ - type: 'create_index', - tableName: tableName, - schema: '', - data: idx, - }), - ); - } - return statements; + // rename table + statements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + return statements; }; export const libSqlLogSuggestionsAndReturn = async ( diff --git a/drizzle-kit/src/cli/commands/sqlitePushUtils.ts b/drizzle-kit/src/cli/commands/sqlitePushUtils.ts index a18b369451..de3f82472c 100644 --- a/drizzle-kit/src/cli/commands/sqlitePushUtils.ts +++ b/drizzle-kit/src/cli/commands/sqlitePushUtils.ts @@ -2,7 +2,6 @@ import chalk from 'chalk'; import { SQLiteSchemaInternal, SQLiteSchemaSquashed, SQLiteSquasher } from '../../serializer/sqliteSchema'; import { - CreateSqliteIndexConvertor, fromJson, SQLiteCreateTableConvertor, SQLiteDropTableConvertor, @@ -13,11 +12,11 @@ import type { JsonStatement } from '../../jsonStatements'; import { findAddedAndRemoved, type SQLiteDB } from '../../utils'; export const _moveDataStatements = ( - tableName: string, - json: SQLiteSchemaSquashed, - dataLoss: boolean = false, + tableName: string, + json: SQLiteSchemaSquashed, + dataLoss: boolean = false, ) => { - const statements: string[] = []; + const statements: string[] = []; const newTableName = `__new_${tableName}`; @@ -73,29 +72,18 @@ export const _moveDataStatements = ( }), ); - // rename table - statements.push( - new SqliteRenameTableConvertor().convert({ - fromSchema: '', - tableNameFrom: newTableName, - tableNameTo: tableName, - toSchema: '', - type: 'rename_table', - }), - ); - - for (const idx of Object.values(json.tables[tableName].indexes)) { - statements.push( - new CreateSqliteIndexConvertor().convert({ - type: 'create_index', - tableName: tableName, - schema: '', - data: idx, - }), - ); - } - - return statements; + // rename table + statements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + + return statements; }; export const getOldTableName = ( diff --git a/drizzle-kit/tests/push/sqlite.test.ts b/drizzle-kit/tests/push/sqlite.test.ts index e2c85233a3..421f2c5b6c 100644 --- a/drizzle-kit/tests/push/sqlite.test.ts +++ b/drizzle-kit/tests/push/sqlite.test.ts @@ -1297,6 +1297,55 @@ test('add check constraint to table', async (t) => { expect(tablesToTruncate!.length).toBe(0); }); +test('recreate table with existing unique index does not duplicate index creation', async () => { + const client = new Database(':memory:'); + + // Base schema with a unique index on email + const schema1 = { + users: sqliteTable( + 'users', + { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + email: text('email').notNull().unique(), + }, + (t) => ({ + // also define an explicit unique index to cover both paths + emailIdx: uniqueIndex('users_email_unique').on(t.email), + }), + ), + }; + + // Change that forces table recreation (add check constraint) + const schema2 = { + users: sqliteTable( + 'users', + { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + email: text('email').notNull().unique(), + }, + (table) => ({ + emailIdx: uniqueIndex('users_email_unique').on(table.email), + someCheck: check('some_check', sql`${table.id} >= 0`), + }), + ), + }; + + const { sqlStatements } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + [], + ); + + // Ensure only one CREATE UNIQUE INDEX for users_email_unique exists + const createIdx = sqlStatements.filter((s) => + s.startsWith('CREATE UNIQUE INDEX `users_email_unique`') + ); + expect(createIdx.length).toBe(1); +}); + test('drop check constraint', async (t) => { const client = new Database(':memory:');