diff --git a/integration-tests/tests/libsql.test.ts b/integration-tests/tests/libsql.test.ts index b8e224e1b..590c02cbc 100644 --- a/integration-tests/tests/libsql.test.ts +++ b/integration-tests/tests/libsql.test.ts @@ -6,6 +6,7 @@ import anyTest from 'ava'; import { asc, eq, + getTableColumns, gt, gte, inArray, @@ -55,6 +56,16 @@ const usersTable = sqliteTable('users', { createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`strftime('%s', 'now')`), }); +const usersOnUpdate = sqliteTable('users_on_update', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), + updatedAt: integer('updated_at', { mode: 'timestamp_ms' }).$onUpdate(() => new Date()), + // uppercaseName: text('uppercase_name').$onUpdateFn(() => + // sql`upper(s.name)` + // ), This doesn't seem to be supported in sqlite +}); + const users2Table = sqliteTable('users2', { id: integer('id').primaryKey(), name: text('name').notNull(), @@ -2423,3 +2434,87 @@ test.serial('set operations (mixed all) as function with subquery', async (t) => ).orderBy(asc(sql`id`)); }); }); + +test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { + const { db } = t.context; + + await db.run(sql`drop table if exists ${usersOnUpdate}`); + + await db.run( + sql` + create table ${usersOnUpdate} ( + id integer primary key autoincrement, + name text not null, + update_counter integer default 1 not null, + updated_at integer + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + t.deepEqual(response, [ + { name: 'John', id: 1, updateCounter: 1 }, + { name: 'Jane', id: 2, updateCounter: 1 }, + { name: 'Jack', id: 3, updateCounter: 1 }, + { name: 'Jill', id: 4, updateCounter: 1 }, + ]); + const msDelay = 100; + + for (const eachUser of justDates) { + t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); // This test might fail if db read is too slow. Is there a better way to test Date.now()? + } +}); + +test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { + const { db } = t.context; + + await db.run(sql`drop table if exists ${usersOnUpdate}`); + + await db.run( + sql` + create table ${usersOnUpdate} ( + id integer primary key autoincrement, + name text not null, + update_counter integer default 1 not null, + updated_at integer + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + t.deepEqual(response, [ + { name: 'Angel', id: 1, updateCounter: 2 }, + { name: 'Jane', id: 2, updateCounter: 1 }, + { name: 'Jack', id: 3, updateCounter: 1 }, + { name: 'Jill', id: 4, updateCounter: 1 }, + ]); + const msDelay = 100; + + for (const eachUser of justDates) { + t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); // This test might fail if db read is too slow. Is there a better way to test Date.now()? + } +}); diff --git a/integration-tests/tests/mysql.test.ts b/integration-tests/tests/mysql.test.ts index 3b545fcd8..71bfc628c 100644 --- a/integration-tests/tests/mysql.test.ts +++ b/integration-tests/tests/mysql.test.ts @@ -7,6 +7,7 @@ import { asc, DefaultLogger, eq, + getTableColumns, gt, gte, inArray, @@ -79,6 +80,14 @@ const citiesTable = mysqlTable('cities', { name: text('name').notNull(), }); +const usersOnUpdate = mysqlTable('users_on_update', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + updateCounter: int('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), + updatedAt: datetime('updated_at', { mode: 'date', fsp: 3 }).$onUpdate(() => new Date()), + uppercaseName: text('uppercase_name').$onUpdateFn(() => sql`upper(name)`), +}); + const datesTable = mysqlTable('datestable', { date: date('date'), dateAsString: date('date_as_string', { mode: 'string' }), @@ -2654,3 +2663,92 @@ test.serial('set operations (mixed all) as function with subquery', async (t) => ); }); }); + +test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { + const { db } = t.context; + + await db.execute(sql`drop table if exists ${usersOnUpdate}`); + + await db.execute( + sql` + create table ${usersOnUpdate} ( + id serial not null primary key, + name text not null, + update_counter integer default 1 not null, + updated_at datetime(3), + uppercase_name text + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate); + + const response = await db.select({ ...rest }).from(usersOnUpdate); + + t.deepEqual(response, [ + { name: 'John', id: 1, updateCounter: 1, uppercaseName: 'JOHN' }, + { name: 'Jane', id: 2, updateCounter: 1, uppercaseName: 'JANE' }, + { name: 'Jack', id: 3, updateCounter: 1, uppercaseName: 'JACK' }, + { name: 'Jill', id: 4, updateCounter: 1, uppercaseName: 'JILL' }, + ]); + const msDelay = 100; + + for (const eachUser of justDates) { + t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); // This test might fail if db read is too slow. Is there a better way to test Date.now()? + } +}); + +test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { + const { db } = t.context; + + await db.execute(sql`drop table if exists ${usersOnUpdate}`); + + await db.execute( + sql` + create table ${usersOnUpdate} ( + id serial not null primary key, + name text not null, + update_counter integer default 1 not null, + updated_at datetime(3), + uppercase_name text + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + const initial = await db.select({ updatedAt }).from(usersOnUpdate); + + await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate); + + const response = await db.select({ ...rest }).from(usersOnUpdate); + + t.deepEqual(response, [ + { name: 'Angel', id: 1, updateCounter: 2, uppercaseName: 'ANGEL' }, + { name: 'Jane', id: 2, updateCounter: 1, uppercaseName: 'JANE' }, + { name: 'Jack', id: 3, updateCounter: 1, uppercaseName: 'JACK' }, + { name: 'Jill', id: 4, updateCounter: 1, uppercaseName: 'JILL' }, + ]); + const msDelay = 100; + + t.assert(initial[0]?.updatedAt?.valueOf() !== justDates[0]?.updatedAt?.valueOf()); + + for (const eachUser of justDates) { + t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); // This test might fail if db read is too slow. Is there a better way to test Date.now()? + } +}); diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 38fd1a8a3..e0bc092d2 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -10,6 +10,7 @@ import { arrayOverlaps, asc, eq, + getTableColumns, gt, gte, inArray, @@ -42,6 +43,7 @@ import { macaddr, macaddr8, type PgColumn, + pgEnum, pgMaterializedView, pgTable, pgTableCreator, @@ -56,7 +58,6 @@ import { uniqueKeyName, uuid as pgUuid, varchar, - pgEnum, } from 'drizzle-orm/pg-core'; import getPort from 'get-port'; import pg from 'pg'; @@ -75,6 +76,14 @@ const usersTable = pgTable('users', { createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), }); +const usersOnUpdate = pgTable('users_on_update', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), + updatedAt: timestamp('updated_at', { mode: 'date', precision: 3 }).$onUpdate(() => new Date()), + // uppercaseName: text('uppercase_name').$onUpdateFn(() => sql`upper(name)`), looks like this is not supported in pg +}); + const citiesTable = pgTable('cities', { id: serial('id').primaryKey(), name: text('name').notNull(), @@ -3151,3 +3160,92 @@ test.serial('set operations (mixed all) as function', async (t) => { ).orderBy(asc(sql`id`)); }); }); + +test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { + const { db } = t.context; + + await db.execute(sql`drop table if exists ${usersOnUpdate}`); + + await db.execute( + sql` + create table ${usersOnUpdate} ( + id serial primary key, + name text not null, + update_counter integer default 1 not null, + updated_at timestamp(3) + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + t.deepEqual(response, [ + { name: 'John', id: 1, updateCounter: 1 }, + { name: 'Jane', id: 2, updateCounter: 1 }, + { name: 'Jack', id: 3, updateCounter: 1 }, + { name: 'Jill', id: 4, updateCounter: 1 }, + ]); + const msDelay = 100; + + for (const eachUser of justDates) { + t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); // This test might fail if db read is too slow. Is there a better way to test Date.now()? + } +}); + +test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { + const { db } = t.context; + + await db.execute(sql`drop table if exists ${usersOnUpdate}`); + + await db.execute( + sql` + create table ${usersOnUpdate} ( + id serial primary key, + name text not null, + update_counter integer default 1 not null, + updated_at timestamp(3) + ) + `, + ); + + await db.insert(usersOnUpdate).values([ + { name: 'John' }, + { name: 'Jane' }, + { name: 'Jack' }, + { name: 'Jill' }, + ]); + + const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); + const initial = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); + + const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); + + t.deepEqual(response, [ + { name: 'Angel', id: 1, updateCounter: 2 }, + { name: 'Jane', id: 2, updateCounter: 1 }, + { name: 'Jack', id: 3, updateCounter: 1 }, + { name: 'Jill', id: 4, updateCounter: 1 }, + ]); + const msDelay = 100; + + t.assert(initial[0]?.updatedAt?.valueOf() !== justDates[0]?.updatedAt?.valueOf()); + + for (const eachUser of justDates) { + t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); // This test might fail if db read is too slow. Is there a better way to test Date.now()? + } +});