Skip to content

Commit f58e918

Browse files
committed
[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
1 parent 11ff664 commit f58e918

File tree

3 files changed

+76
-51
lines changed

3 files changed

+76
-51
lines changed

‎drizzle-kit/src/cli/commands/libSqlPushUtils.ts‎

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { JsonStatement } from 'src/jsonStatements';
44
import { findAddedAndRemoved, SQLiteDB } from 'src/utils';
55
import { SQLiteSchemaInternal, SQLiteSchemaSquashed, SQLiteSquasher } from '../../serializer/sqliteSchema';
66
import {
7-
CreateSqliteIndexConvertor,
87
fromJson,
98
LibSQLModifyColumn,
109
SQLiteCreateTableConvertor,
@@ -86,28 +85,17 @@ export const _moveDataStatements = (
8685
}),
8786
);
8887

89-
// rename table
90-
statements.push(
91-
new SqliteRenameTableConvertor().convert({
92-
fromSchema: '',
93-
tableNameFrom: newTableName,
94-
tableNameTo: tableName,
95-
toSchema: '',
96-
type: 'rename_table',
97-
}),
98-
);
99-
100-
for (const idx of Object.values(json.tables[tableName].indexes)) {
101-
statements.push(
102-
new CreateSqliteIndexConvertor().convert({
103-
type: 'create_index',
104-
tableName: tableName,
105-
schema: '',
106-
data: idx,
107-
}),
108-
);
109-
}
110-
return statements;
88+
// rename table
89+
statements.push(
90+
new SqliteRenameTableConvertor().convert({
91+
fromSchema: '',
92+
tableNameFrom: newTableName,
93+
tableNameTo: tableName,
94+
toSchema: '',
95+
type: 'rename_table',
96+
}),
97+
);
98+
return statements;
11199
};
112100

113101
export const libSqlLogSuggestionsAndReturn = async (

‎drizzle-kit/src/cli/commands/sqlitePushUtils.ts‎

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import chalk from 'chalk';
22

33
import { SQLiteSchemaInternal, SQLiteSchemaSquashed, SQLiteSquasher } from '../../serializer/sqliteSchema';
44
import {
5-
CreateSqliteIndexConvertor,
65
fromJson,
76
SQLiteCreateTableConvertor,
87
SQLiteDropTableConvertor,
@@ -13,11 +12,11 @@ import type { JsonStatement } from '../../jsonStatements';
1312
import { findAddedAndRemoved, type SQLiteDB } from '../../utils';
1413

1514
export const _moveDataStatements = (
16-
tableName: string,
17-
json: SQLiteSchemaSquashed,
18-
dataLoss: boolean = false,
15+
tableName: string,
16+
json: SQLiteSchemaSquashed,
17+
dataLoss: boolean = false,
1918
) => {
20-
const statements: string[] = [];
19+
const statements: string[] = [];
2120

2221
const newTableName = `__new_${tableName}`;
2322

@@ -73,29 +72,18 @@ export const _moveDataStatements = (
7372
}),
7473
);
7574

76-
// rename table
77-
statements.push(
78-
new SqliteRenameTableConvertor().convert({
79-
fromSchema: '',
80-
tableNameFrom: newTableName,
81-
tableNameTo: tableName,
82-
toSchema: '',
83-
type: 'rename_table',
84-
}),
85-
);
86-
87-
for (const idx of Object.values(json.tables[tableName].indexes)) {
88-
statements.push(
89-
new CreateSqliteIndexConvertor().convert({
90-
type: 'create_index',
91-
tableName: tableName,
92-
schema: '',
93-
data: idx,
94-
}),
95-
);
96-
}
97-
98-
return statements;
75+
// rename table
76+
statements.push(
77+
new SqliteRenameTableConvertor().convert({
78+
fromSchema: '',
79+
tableNameFrom: newTableName,
80+
tableNameTo: tableName,
81+
toSchema: '',
82+
type: 'rename_table',
83+
}),
84+
);
85+
86+
return statements;
9987
};
10088

10189
export const getOldTableName = (

‎drizzle-kit/tests/push/sqlite.test.ts‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,55 @@ test('add check constraint to table', async (t) => {
12971297
expect(tablesToTruncate!.length).toBe(0);
12981298
});
12991299

1300+
test('recreate table with existing unique index does not duplicate index creation', async () => {
1301+
const client = new Database(':memory:');
1302+
1303+
// Base schema with a unique index on email
1304+
const schema1 = {
1305+
users: sqliteTable(
1306+
'users',
1307+
{
1308+
id: int('id').primaryKey({ autoIncrement: false }),
1309+
name: text('name'),
1310+
email: text('email').notNull().unique(),
1311+
},
1312+
(t) => ({
1313+
// also define an explicit unique index to cover both paths
1314+
emailIdx: uniqueIndex('users_email_unique').on(t.email),
1315+
}),
1316+
),
1317+
};
1318+
1319+
// Change that forces table recreation (add check constraint)
1320+
const schema2 = {
1321+
users: sqliteTable(
1322+
'users',
1323+
{
1324+
id: int('id').primaryKey({ autoIncrement: false }),
1325+
name: text('name'),
1326+
email: text('email').notNull().unique(),
1327+
},
1328+
(table) => ({
1329+
emailIdx: uniqueIndex('users_email_unique').on(table.email),
1330+
someCheck: check('some_check', sql`${table.id} >= 0`),
1331+
}),
1332+
),
1333+
};
1334+
1335+
const { sqlStatements } = await diffTestSchemasPushSqlite(
1336+
client,
1337+
schema1,
1338+
schema2,
1339+
[],
1340+
);
1341+
1342+
// Ensure only one CREATE UNIQUE INDEX for users_email_unique exists
1343+
const createIdx = sqlStatements.filter((s) =>
1344+
s.startsWith('CREATE UNIQUE INDEX `users_email_unique`')
1345+
);
1346+
expect(createIdx.length).toBe(1);
1347+
});
1348+
13001349
test('drop check constraint', async (t) => {
13011350
const client = new Database(':memory:');
13021351

0 commit comments

Comments
 (0)