diff --git a/drizzle-orm/src/d1/migrator.ts b/drizzle-orm/src/d1/migrator.ts index 2259516bf8..5c26e0166f 100644 --- a/drizzle-orm/src/d1/migrator.ts +++ b/drizzle-orm/src/d1/migrator.ts @@ -12,7 +12,7 @@ export async function migrate>( const migrationTableCreate = sql` CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} ( - id SERIAL PRIMARY KEY, + id integer PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric ) diff --git a/drizzle-orm/src/durable-sqlite/migrator.ts b/drizzle-orm/src/durable-sqlite/migrator.ts index 8410b2900f..5e5aed29cd 100644 --- a/drizzle-orm/src/durable-sqlite/migrator.ts +++ b/drizzle-orm/src/durable-sqlite/migrator.ts @@ -52,7 +52,7 @@ export async function migrate< const migrationTableCreate = sql` CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} ( - id SERIAL PRIMARY KEY, + id integer PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric ) diff --git a/drizzle-orm/src/libsql/migrator.ts b/drizzle-orm/src/libsql/migrator.ts index 373a8aab4a..755341ba00 100644 --- a/drizzle-orm/src/libsql/migrator.ts +++ b/drizzle-orm/src/libsql/migrator.ts @@ -12,7 +12,7 @@ export async function migrate>( const migrationTableCreate = sql` CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} ( - id SERIAL PRIMARY KEY, + id integer PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric ) diff --git a/drizzle-orm/src/sqlite-core/dialect.ts b/drizzle-orm/src/sqlite-core/dialect.ts index 317c8df12c..ad172df50c 100644 --- a/drizzle-orm/src/sqlite-core/dialect.ts +++ b/drizzle-orm/src/sqlite-core/dialect.ts @@ -843,7 +843,7 @@ export class SQLiteSyncDialect extends SQLiteDialect { const migrationTableCreate = sql` CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} ( - id SERIAL PRIMARY KEY, + id integer PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric ) @@ -895,7 +895,7 @@ export class SQLiteAsyncDialect extends SQLiteDialect { const migrationTableCreate = sql` CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} ( - id SERIAL PRIMARY KEY, + id integer PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric ) diff --git a/drizzle-orm/src/sqlite-proxy/migrator.ts b/drizzle-orm/src/sqlite-proxy/migrator.ts index cc4a7f71c3..27c60b9fe3 100644 --- a/drizzle-orm/src/sqlite-proxy/migrator.ts +++ b/drizzle-orm/src/sqlite-proxy/migrator.ts @@ -18,7 +18,7 @@ export async function migrate>( const migrationTableCreate = sql` CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} ( - id SERIAL PRIMARY KEY, + id integer PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric ) diff --git a/integration-tests/tests/sqlite/better-sqlite.test.ts b/integration-tests/tests/sqlite/better-sqlite.test.ts index 53feee15f2..cd76d1313a 100644 --- a/integration-tests/tests/sqlite/better-sqlite.test.ts +++ b/integration-tests/tests/sqlite/better-sqlite.test.ts @@ -48,6 +48,32 @@ test('migrator', async () => { db.run(sql`drop table __drizzle_migrations`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + db.run(sql`drop table if exists another_users`); + db.run(sql`drop table if exists users12`); + db.run(sql`drop table if exists __drizzle_migrations`); + + migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { cid: number; name: string; type: string; notnull: number; pk: number }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + db.run(sql`drop table another_users`); + db.run(sql`drop table users12`); + db.run(sql`drop table __drizzle_migrations`); +}); + skipTests([ /** * doesn't work properly: diff --git a/integration-tests/tests/sqlite/d1.test.ts b/integration-tests/tests/sqlite/d1.test.ts index 6878b127f6..e167638d7e 100644 --- a/integration-tests/tests/sqlite/d1.test.ts +++ b/integration-tests/tests/sqlite/d1.test.ts @@ -77,6 +77,32 @@ test('migrator : migrate with custom table', async () => { await db.run(sql`drop table ${sql.identifier(customTable)}`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + skipTests([ // Cannot convert 49,50,55 to a BigInt 'insert bigint values', diff --git a/integration-tests/tests/sqlite/durable-objects/index.ts b/integration-tests/tests/sqlite/durable-objects/index.ts index b67534d4ee..f9b8e86b47 100644 --- a/integration-tests/tests/sqlite/durable-objects/index.ts +++ b/integration-tests/tests/sqlite/durable-objects/index.ts @@ -225,6 +225,36 @@ export class MyDurableObject extends DurableObject { } } + async migratorSchemaTest(): Promise { + try { + this.db.run(sql`drop table if exists another_users`); + this.db.run(sql`drop table if exists users12`); + this.db.run(sql`drop table if exists __drizzle_migrations`); + + migrate(this.db, migrations); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = this.db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).to.not.be.undefined; + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).to.equal('integer'); + expect(idColumn!.pk).to.equal(1); + + this.db.run(sql`drop table another_users`); + this.db.run(sql`drop table users12`); + this.db.run(sql`drop table __drizzle_migrations`); + } catch { + throw new Error('migratorSchemaTest has broken'); + } + } + async beforeEach(): Promise { this.db.run(sql`drop table if exists ${usersTable}`); this.db.run(sql`drop table if exists ${users2Table}`); diff --git a/integration-tests/tests/sqlite/libsql-http.test.ts b/integration-tests/tests/sqlite/libsql-http.test.ts index 576d8d48a4..bd7f588778 100644 --- a/integration-tests/tests/sqlite/libsql-http.test.ts +++ b/integration-tests/tests/sqlite/libsql-http.test.ts @@ -89,6 +89,32 @@ test('migrator : migrate with custom table', async () => { await db.run(sql`drop table ${sql.identifier(customTable)}`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + test('test $onUpdateFn and $onUpdate works as $default', async (ctx) => { const { db } = ctx.sqlite; diff --git a/integration-tests/tests/sqlite/libsql-node.test.ts b/integration-tests/tests/sqlite/libsql-node.test.ts index bfe2c1574d..0ab7d5ec46 100644 --- a/integration-tests/tests/sqlite/libsql-node.test.ts +++ b/integration-tests/tests/sqlite/libsql-node.test.ts @@ -89,6 +89,32 @@ test('migrator : migrate with custom table', async () => { await db.run(sql`drop table ${sql.identifier(customTable)}`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + skipTests([ 'delete with limit and order by', 'update with limit and order by', diff --git a/integration-tests/tests/sqlite/libsql-sqlite3.test.ts b/integration-tests/tests/sqlite/libsql-sqlite3.test.ts index 8d8419f178..e5072d4f0f 100644 --- a/integration-tests/tests/sqlite/libsql-sqlite3.test.ts +++ b/integration-tests/tests/sqlite/libsql-sqlite3.test.ts @@ -85,6 +85,32 @@ test('migrator : migrate with custom table', async () => { await db.run(sql`drop table ${sql.identifier(customTable)}`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + skipTests([ 'delete with limit and order by', 'update with limit and order by', diff --git a/integration-tests/tests/sqlite/libsql-ws.test.ts b/integration-tests/tests/sqlite/libsql-ws.test.ts index 86810a36f7..e3487fe73d 100644 --- a/integration-tests/tests/sqlite/libsql-ws.test.ts +++ b/integration-tests/tests/sqlite/libsql-ws.test.ts @@ -89,6 +89,32 @@ test('migrator : migrate with custom table', async () => { await db.run(sql`drop table ${sql.identifier(customTable)}`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + test('test $onUpdateFn and $onUpdate works as $default', async (ctx) => { const { db } = ctx.sqlite; diff --git a/integration-tests/tests/sqlite/libsql.test.ts b/integration-tests/tests/sqlite/libsql.test.ts index 8d68496584..6782df27b8 100644 --- a/integration-tests/tests/sqlite/libsql.test.ts +++ b/integration-tests/tests/sqlite/libsql.test.ts @@ -97,6 +97,32 @@ test('migrator : migrate with custom table', async () => { await db.run(sql`drop table ${sql.identifier(customTable)}`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + await db.run(sql`drop table if exists another_users`); + await db.run(sql`drop table if exists users12`); + await db.run(sql`drop table if exists __drizzle_migrations`); + + await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = await db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + await db.run(sql`drop table another_users`); + await db.run(sql`drop table users12`); + await db.run(sql`drop table __drizzle_migrations`); +}); + skipTests([ 'delete with limit and order by', 'update with limit and order by', diff --git a/integration-tests/tests/sqlite/sql-js.test.ts b/integration-tests/tests/sqlite/sql-js.test.ts index 4c733835fb..2cf82887e5 100644 --- a/integration-tests/tests/sqlite/sql-js.test.ts +++ b/integration-tests/tests/sqlite/sql-js.test.ts @@ -50,6 +50,32 @@ test('migrator', async () => { db.run(sql`drop table __drizzle_migrations`); }); +test('migrator: migrations table has correct schema for SQLite', async () => { + db.run(sql`drop table if exists another_users`); + db.run(sql`drop table if exists users12`); + db.run(sql`drop table if exists __drizzle_migrations`); + + migrate(db, { migrationsFolder: './drizzle2/sqlite' }); + + // Verify the __drizzle_migrations table uses proper SQLite syntax + // The id column should be "integer PRIMARY KEY" (which auto-increments in SQLite), + // not "SERIAL PRIMARY KEY" (which is PostgreSQL syntax) + const tableInfo = db.all<{ cid: number; name: string; type: string; notnull: number; pk: number }>( + sql`PRAGMA table_info(__drizzle_migrations)`, + ); + + const idColumn = tableInfo.find((col: { name: string }) => col.name === 'id'); + expect(idColumn).toBeDefined(); + // In SQLite, the type should be "integer" (case-insensitive) for proper auto-increment behavior + // "SERIAL" is PostgreSQL syntax and should not be used + expect(idColumn!.type.toLowerCase()).toBe('integer'); + expect(idColumn!.pk).toBe(1); + + db.run(sql`drop table another_users`); + db.run(sql`drop table users12`); + db.run(sql`drop table __drizzle_migrations`); +}); + skipTests([ /** * doesn't work properly: