Skip to content

Commit 4b58b8a

Browse files
committed
fix(sqlite): use correct SQLite syntax for migrations table
The __drizzle_migrations table was being created with PostgreSQL syntax (SERIAL PRIMARY KEY) instead of proper SQLite syntax (integer PRIMARY KEY AUTOINCREMENT). While SQLite's flexible typing allowed the table to be created, the id column was stored with type "SERIAL" rather than "INTEGER", meaning it wouldn't behave as a proper auto-incrementing integer primary key. Added tests to verify the migrations table schema uses correct SQLite syntax in all SQLite test files. The tests were validated to indeed fail before this change.
1 parent a086f59 commit 4b58b8a

File tree

14 files changed

+244
-6
lines changed

14 files changed

+244
-6
lines changed

drizzle-orm/src/d1/migrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export async function migrate<TSchema extends Record<string, unknown>>(
1212

1313
const migrationTableCreate = sql`
1414
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
15-
id SERIAL PRIMARY KEY,
15+
id integer PRIMARY KEY AUTOINCREMENT,
1616
hash text NOT NULL,
1717
created_at numeric
1818
)

drizzle-orm/src/durable-sqlite/migrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export async function migrate<
5252

5353
const migrationTableCreate = sql`
5454
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
55-
id SERIAL PRIMARY KEY,
55+
id integer PRIMARY KEY AUTOINCREMENT,
5656
hash text NOT NULL,
5757
created_at numeric
5858
)

drizzle-orm/src/libsql/migrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export async function migrate<TSchema extends Record<string, unknown>>(
1212

1313
const migrationTableCreate = sql`
1414
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
15-
id SERIAL PRIMARY KEY,
15+
id integer PRIMARY KEY AUTOINCREMENT,
1616
hash text NOT NULL,
1717
created_at numeric
1818
)

drizzle-orm/src/sqlite-core/dialect.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ export class SQLiteSyncDialect extends SQLiteDialect {
843843

844844
const migrationTableCreate = sql`
845845
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
846-
id SERIAL PRIMARY KEY,
846+
id integer PRIMARY KEY AUTOINCREMENT,
847847
hash text NOT NULL,
848848
created_at numeric
849849
)
@@ -895,7 +895,7 @@ export class SQLiteAsyncDialect extends SQLiteDialect {
895895

896896
const migrationTableCreate = sql`
897897
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
898-
id SERIAL PRIMARY KEY,
898+
id integer PRIMARY KEY AUTOINCREMENT,
899899
hash text NOT NULL,
900900
created_at numeric
901901
)

drizzle-orm/src/sqlite-proxy/migrator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export async function migrate<TSchema extends Record<string, unknown>>(
1818

1919
const migrationTableCreate = sql`
2020
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
21-
id SERIAL PRIMARY KEY,
21+
id integer PRIMARY KEY AUTOINCREMENT,
2222
hash text NOT NULL,
2323
created_at numeric
2424
)

integration-tests/tests/sqlite/better-sqlite.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,32 @@ test('migrator', async () => {
4848
db.run(sql`drop table __drizzle_migrations`);
4949
});
5050

51+
test('migrator: migrations table has correct schema for SQLite', async () => {
52+
db.run(sql`drop table if exists another_users`);
53+
db.run(sql`drop table if exists users12`);
54+
db.run(sql`drop table if exists __drizzle_migrations`);
55+
56+
migrate(db, { migrationsFolder: './drizzle2/sqlite' });
57+
58+
// Verify the __drizzle_migrations table uses proper SQLite syntax
59+
// The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite),
60+
// not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax)
61+
const tableInfo = db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>(
62+
sql`PRAGMA table_info(__drizzle_migrations)`,
63+
);
64+
65+
const idColumn = tableInfo.find((col: { cid: number; name: string; type: string; notnull: number; pk: number }) => col.name === 'id');
66+
expect(idColumn).toBeDefined();
67+
// In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior
68+
// "SERIAL" is PostgreSQL syntax and should not be used
69+
expect(idColumn!.type.toLowerCase()).toBe('integer');
70+
expect(idColumn!.pk).toBe(1);
71+
72+
db.run(sql`drop table another_users`);
73+
db.run(sql`drop table users12`);
74+
db.run(sql`drop table __drizzle_migrations`);
75+
});
76+
5177
skipTests([
5278
/**
5379
* doesn't work properly:

integration-tests/tests/sqlite/d1.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,32 @@ test('migrator : migrate with custom table', async () => {
7777
await db.run(sql`drop table ${sql.identifier(customTable)}`);
7878
});
7979

80+
test('migrator: migrations table has correct schema for SQLite', async () => {
81+
await db.run(sql`drop table if exists another_users`);
82+
await db.run(sql`drop table if exists users12`);
83+
await db.run(sql`drop table if exists __drizzle_migrations`);
84+
85+
await migrate(db, { migrationsFolder: './drizzle2/sqlite' });
86+
87+
// Verify the __drizzle_migrations table uses proper SQLite syntax
88+
// The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite),
89+
// not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax)
90+
const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>(
91+
sql`PRAGMA table_info(__drizzle_migrations)`,
92+
);
93+
94+
const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id');
95+
expect(idColumn).toBeDefined();
96+
// In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior
97+
// "SERIAL" is PostgreSQL syntax and should not be used
98+
expect(idColumn!.type.toLowerCase()).toBe('integer');
99+
expect(idColumn!.pk).toBe(1);
100+
101+
await db.run(sql`drop table another_users`);
102+
await db.run(sql`drop table users12`);
103+
await db.run(sql`drop table __drizzle_migrations`);
104+
});
105+
80106
skipTests([
81107
// Cannot convert 49,50,55 to a BigInt
82108
'insert bigint values',

integration-tests/tests/sqlite/durable-objects/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,36 @@ export class MyDurableObject extends DurableObject {
225225
}
226226
}
227227

228+
async migratorSchemaTest(): Promise<void> {
229+
try {
230+
this.db.run(sql`drop table if exists another_users`);
231+
this.db.run(sql`drop table if exists users12`);
232+
this.db.run(sql`drop table if exists __drizzle_migrations`);
233+
234+
migrate(this.db, migrations);
235+
236+
// Verify the __drizzle_migrations table uses proper SQLite syntax
237+
// The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite),
238+
// not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax)
239+
const tableInfo = this.db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>(
240+
sql`PRAGMA table_info(__drizzle_migrations)`,
241+
);
242+
243+
const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id');
244+
expect(idColumn).to.not.be.undefined;
245+
// In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior
246+
// "SERIAL" is PostgreSQL syntax and should not be used
247+
expect(idColumn!.type.toLowerCase()).to.equal('integer');
248+
expect(idColumn!.pk).to.equal(1);
249+
250+
this.db.run(sql`drop table another_users`);
251+
this.db.run(sql`drop table users12`);
252+
this.db.run(sql`drop table __drizzle_migrations`);
253+
} catch {
254+
throw new Error('migratorSchemaTest has broken');
255+
}
256+
}
257+
228258
async beforeEach(): Promise<void> {
229259
this.db.run(sql`drop table if exists ${usersTable}`);
230260
this.db.run(sql`drop table if exists ${users2Table}`);

integration-tests/tests/sqlite/libsql-http.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,32 @@ test('migrator : migrate with custom table', async () => {
8989
await db.run(sql`drop table ${sql.identifier(customTable)}`);
9090
});
9191

92+
test('migrator: migrations table has correct schema for SQLite', async () => {
93+
await db.run(sql`drop table if exists another_users`);
94+
await db.run(sql`drop table if exists users12`);
95+
await db.run(sql`drop table if exists __drizzle_migrations`);
96+
97+
await migrate(db, { migrationsFolder: './drizzle2/sqlite' });
98+
99+
// Verify the __drizzle_migrations table uses proper SQLite syntax
100+
// The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite),
101+
// not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax)
102+
const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>(
103+
sql`PRAGMA table_info(__drizzle_migrations)`,
104+
);
105+
106+
const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id');
107+
expect(idColumn).toBeDefined();
108+
// In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior
109+
// "SERIAL" is PostgreSQL syntax and should not be used
110+
expect(idColumn!.type.toLowerCase()).toBe('integer');
111+
expect(idColumn!.pk).toBe(1);
112+
113+
await db.run(sql`drop table another_users`);
114+
await db.run(sql`drop table users12`);
115+
await db.run(sql`drop table __drizzle_migrations`);
116+
});
117+
92118
test('test $onUpdateFn and $onUpdate works as $default', async (ctx) => {
93119
const { db } = ctx.sqlite;
94120

integration-tests/tests/sqlite/libsql-node.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,32 @@ test('migrator : migrate with custom table', async () => {
8989
await db.run(sql`drop table ${sql.identifier(customTable)}`);
9090
});
9191

92+
test('migrator: migrations table has correct schema for SQLite', async () => {
93+
await db.run(sql`drop table if exists another_users`);
94+
await db.run(sql`drop table if exists users12`);
95+
await db.run(sql`drop table if exists __drizzle_migrations`);
96+
97+
await migrate(db, { migrationsFolder: './drizzle2/sqlite' });
98+
99+
// Verify the __drizzle_migrations table uses proper SQLite syntax
100+
// The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite),
101+
// not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax)
102+
const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>(
103+
sql`PRAGMA table_info(__drizzle_migrations)`,
104+
);
105+
106+
const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id');
107+
expect(idColumn).toBeDefined();
108+
// In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior
109+
// "SERIAL" is PostgreSQL syntax and should not be used
110+
expect(idColumn!.type.toLowerCase()).toBe('integer');
111+
expect(idColumn!.pk).toBe(1);
112+
113+
await db.run(sql`drop table another_users`);
114+
await db.run(sql`drop table users12`);
115+
await db.run(sql`drop table __drizzle_migrations`);
116+
});
117+
92118
skipTests([
93119
'delete with limit and order by',
94120
'update with limit and order by',

0 commit comments

Comments
 (0)