Skip to content

Commit adf9bf1

Browse files
committed
Fixed SQL in `` (fixes #2388, rework of #2911 with handling in proper place)
1 parent 645a6f3 commit adf9bf1

10 files changed

Lines changed: 208 additions & 5 deletions

File tree

‎drizzle-orm/src/gel-core/dialect.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ export class GelDialect {
149149
return sql.join(columnNames.flatMap((colName, i) => {
150150
const col = tableColumns[colName]!;
151151

152-
const value = set[colName] ?? sql.param(col.onUpdateFn!(), col);
152+
const onUpdateFnResult = col.onUpdateFn?.();
153+
const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));
153154
const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
154155

155156
if (i < setSize - 1) {

‎drizzle-orm/src/mysql-core/dialect.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ export class MySqlDialect {
144144
return sql.join(columnNames.flatMap((colName, i) => {
145145
const col = tableColumns[colName]!;
146146

147-
const value = set[colName] ?? sql.param(col.onUpdateFn!(), col);
147+
const onUpdateFnResult = col.onUpdateFn?.();
148+
const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));
148149
const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
149150

150151
if (i < setSize - 1) {

‎drizzle-orm/src/pg-core/dialect.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ export class PgDialect {
160160
return sql.join(columnNames.flatMap((colName, i) => {
161161
const col = tableColumns[colName]!;
162162

163-
const value = set[colName] ?? sql.param(col.onUpdateFn!(), col);
163+
const onUpdateFnResult = col.onUpdateFn?.();
164+
const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));
164165
const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
165166

166167
if (i < setSize - 1) {

‎drizzle-orm/src/singlestore-core/dialect.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ export class SingleStoreDialect {
143143
return sql.join(columnNames.flatMap((colName, i) => {
144144
const col = tableColumns[colName]!;
145145

146-
const value = set[colName] ?? sql.param(col.onUpdateFn!(), col);
146+
const onUpdateFnResult = col.onUpdateFn?.();
147+
const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));
147148
const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
148149

149150
if (i < setSize - 1) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ export abstract class SQLiteDialect {
107107
return sql.join(columnNames.flatMap((colName, i) => {
108108
const col = tableColumns[colName]!;
109109

110-
const value = set[colName] ?? sql.param(col.onUpdateFn!(), col);
110+
const onUpdateFnResult = col.onUpdateFn?.();
111+
const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));
111112
const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
112113

113114
if (i < setSize - 1) {

‎integration-tests/tests/gel/gel.test.ts‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,12 @@ describe('some', async () => {
540540
create required property age: int32;
541541
create required property city: str;
542542
};" --tls-security=${tlsSecurity} --dsn=${dsn}`;
543+
544+
await $`gel query "CREATE TYPE default::users_on_update_sql {
545+
create required property id1: int16;
546+
create required property name: str;
547+
create required property updated_at: datetime;
548+
};" --tls-security=${tlsSecurity} --dsn=${dsn}`;
543549
});
544550

545551
afterEach(async () => {
@@ -555,6 +561,7 @@ describe('some', async () => {
555561
await $`gel query "DELETE default::users1;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
556562
await $`gel query "DELETE default::users2;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
557563
await $`gel query "DELETE default::jsontest;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
564+
await $`gel query "DELETE default::users_on_update_sql;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
558565
});
559566

560567
afterAll(async () => {
@@ -601,6 +608,7 @@ describe('some', async () => {
601608
await $`gel query "DROP TYPE default::users_with_names" --tls-security=${tlsSecurity} --dsn=${dsn}`;
602609
await $`gel query "DROP MODULE mySchema;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
603610
await $`gel query "DROP TYPE users_with_age;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
611+
await $`gel query "DROP TYPE default::users_on_update_sql;" --tls-security=${tlsSecurity} --dsn=${dsn}`;
604612
});
605613

606614
async function setupSetOperationTest(db: GelJsDatabase) {
@@ -4801,6 +4809,35 @@ describe('some', async () => {
48014809
expect(config2.enableRLS).toBeFalsy();
48024810
});
48034811

4812+
test('test $onUpdateFn and $onUpdate works with sql value', async (ctx) => {
4813+
const { db } = ctx.gel;
4814+
4815+
const users = gelTable('users_on_update_sql', {
4816+
id: integer('id1').notNull(),
4817+
name: text('name').notNull(),
4818+
updatedAt: timestamptz('updated_at').notNull().$onUpdate(() => sql`now()`),
4819+
});
4820+
4821+
const insertResp = await db.insert(users).values({
4822+
id: 1,
4823+
name: 'John',
4824+
}).returning({
4825+
updatedAt: users.updatedAt,
4826+
});
4827+
await new Promise((resolve) => setTimeout(resolve, 1000));
4828+
4829+
const now = Date.now();
4830+
await new Promise((resolve) => setTimeout(resolve, 1000));
4831+
const updateResp = await db.update(users).set({
4832+
name: 'John',
4833+
}).returning({
4834+
updatedAt: users.updatedAt,
4835+
});
4836+
4837+
expect(new Date(insertResp[0]?.updatedAt.toISOString() ?? 0).getTime()).lessThan(now);
4838+
expect(new Date(updateResp[0]?.updatedAt.toISOString() ?? 0).getTime()).greaterThan(now);
4839+
});
4840+
48044841
test('$count separate', async (ctx) => {
48054842
const { db } = ctx.gel;
48064843

‎integration-tests/tests/mysql/mysql-common.ts‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3844,6 +3844,47 @@ export function tests(driver?: string) {
38443844
await db.execute(sql`drop view ${newYorkers1}`);
38453845
});
38463846

3847+
test('test $onUpdateFn and $onUpdate works with sql value', async (ctx) => {
3848+
const { db } = ctx.mysql;
3849+
3850+
const users = mysqlTable('users', {
3851+
id: serial('id').primaryKey(),
3852+
name: text('name').notNull(),
3853+
updatedAt: timestamp('updated_at', {
3854+
fsp: 6,
3855+
})
3856+
.notNull()
3857+
.$onUpdate(() => sql`current_timestamp`),
3858+
});
3859+
3860+
await db.execute(sql`drop table if exists ${users}`);
3861+
await db.execute(
3862+
sql`
3863+
create table ${users} (
3864+
\`id\` serial primary key,
3865+
\`name\` text not null,
3866+
\`updated_at\` timestamp not null
3867+
)
3868+
`,
3869+
);
3870+
3871+
await db.insert(users).values({
3872+
name: 'John',
3873+
});
3874+
const insertResp = await db.select({ updatedAt: users.updatedAt }).from(users);
3875+
await new Promise((resolve) => setTimeout(resolve, 1000));
3876+
3877+
const now = Date.now();
3878+
await new Promise((resolve) => setTimeout(resolve, 1000));
3879+
await db.update(users).set({
3880+
name: 'John',
3881+
});
3882+
const updateResp = await db.select({ updatedAt: users.updatedAt }).from(users);
3883+
3884+
expect(insertResp[0]?.updatedAt.getTime() ?? 0).lessThan(now);
3885+
expect(updateResp[0]?.updatedAt.getTime() ?? 0).greaterThan(now);
3886+
});
3887+
38473888
test('$count separate', async (ctx) => {
38483889
const { db } = ctx.mysql;
38493890

‎integration-tests/tests/pg/pg-common.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5547,6 +5547,44 @@ export function tests() {
55475547
expect(config2.enableRLS).toBeFalsy();
55485548
});
55495549

5550+
test('test $onUpdateFn and $onUpdate works with sql value', async (ctx) => {
5551+
const { db } = ctx.pg;
5552+
5553+
const users = pgTable('users_on_update', {
5554+
id: serial('id').primaryKey(),
5555+
name: text('name').notNull(),
5556+
updatedAt: timestamp('updated_at', { mode: 'date' }).notNull().$onUpdate(() => sql`now()`),
5557+
});
5558+
5559+
await db.execute(
5560+
sql`
5561+
create table ${users} (
5562+
"id" serial primary key,
5563+
"name" text not null,
5564+
"updated_at" timestamp(3)
5565+
)
5566+
`,
5567+
);
5568+
5569+
const insertResp = await db.insert(users).values({
5570+
name: 'John',
5571+
}).returning({
5572+
updatedAt: users.updatedAt,
5573+
});
5574+
await new Promise((resolve) => setTimeout(resolve, 1000));
5575+
5576+
const now = Date.now();
5577+
await new Promise((resolve) => setTimeout(resolve, 1000));
5578+
const updateResp = await db.update(users).set({
5579+
name: 'John',
5580+
}).returning({
5581+
updatedAt: users.updatedAt,
5582+
});
5583+
5584+
expect(insertResp[0]?.updatedAt.getTime() ?? 0).lessThan(now);
5585+
expect(updateResp[0]?.updatedAt.getTime() ?? 0).greaterThan(now);
5586+
});
5587+
55505588
test('$count separate', async (ctx) => {
55515589
const { db } = ctx.pg;
55525590

‎integration-tests/tests/singlestore/singlestore-common.ts‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3854,6 +3854,45 @@ export function tests(driver?: string) {
38543854
]);
38553855
});
38563856

3857+
test('test $onUpdateFn and $onUpdate works with sql value', async (ctx) => {
3858+
const { db } = ctx.singlestore;
3859+
3860+
const users = singlestoreTable('users', {
3861+
id: serial('id').primaryKey(),
3862+
name: text('name').notNull(),
3863+
updatedAt: timestamp('updated_at')
3864+
.notNull()
3865+
.$onUpdate(() => sql`current_timestamp`),
3866+
});
3867+
3868+
await db.execute(sql`drop table if exists ${users}`);
3869+
await db.execute(
3870+
sql`
3871+
create table ${users} (
3872+
\`id\` serial primary key,
3873+
\`name\` text not null,
3874+
\`updated_at\` timestamp not null
3875+
)
3876+
`,
3877+
);
3878+
3879+
await db.insert(users).values({
3880+
name: 'John',
3881+
});
3882+
const insertResp = await db.select({ updatedAt: users.updatedAt }).from(users);
3883+
await new Promise((resolve) => setTimeout(resolve, 1000));
3884+
3885+
const now = Date.now();
3886+
await new Promise((resolve) => setTimeout(resolve, 1000));
3887+
await db.update(users).set({
3888+
name: 'John',
3889+
});
3890+
const updateResp = await db.select({ updatedAt: users.updatedAt }).from(users);
3891+
3892+
expect(insertResp[0]?.updatedAt.getTime() ?? 0).lessThan(now);
3893+
expect(updateResp[0]?.updatedAt.getTime() ?? 0).greaterThan(now);
3894+
});
3895+
38573896
test('all types', async (ctx) => {
38583897
const { db } = ctx.singlestore;
38593898

‎integration-tests/tests/sqlite/sqlite-common.ts‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,6 +3062,49 @@ export function tests() {
30623062
}
30633063
});
30643064

3065+
test('test $onUpdateFn and $onUpdate works with sql value', async (ctx) => {
3066+
const { db } = ctx.sqlite;
3067+
3068+
const users = sqliteTable('users', {
3069+
id: integer('id').primaryKey({ autoIncrement: true }),
3070+
name: text('name').notNull(),
3071+
updatedAt: integer('updated_at')
3072+
.notNull()
3073+
.$onUpdate(() =>
3074+
sql`(strftime('%s', 'now') * 1000) + (strftime('%f', 'now') - strftime('%S', 'now')) * 1000`
3075+
),
3076+
});
3077+
3078+
await db.run(sql`drop table if exists ${users}`);
3079+
await db.run(
3080+
sql`
3081+
create table ${users} (
3082+
\`id\` integer primary key autoincrement,
3083+
\`name\` text not null,
3084+
\`updated_at\` integer not null
3085+
)
3086+
`,
3087+
);
3088+
3089+
const insertResp = await db.insert(users).values({
3090+
name: 'John',
3091+
}).returning({
3092+
updatedAt: users.updatedAt,
3093+
});
3094+
await new Promise((resolve) => setTimeout(resolve, 1000));
3095+
3096+
const now = Date.now();
3097+
await new Promise((resolve) => setTimeout(resolve, 1000));
3098+
const updateResp = await db.update(users).set({
3099+
name: 'John',
3100+
}).returning({
3101+
updatedAt: users.updatedAt,
3102+
});
3103+
3104+
expect(insertResp[0]?.updatedAt ?? 0).lessThan(now);
3105+
expect(updateResp[0]?.updatedAt ?? 0).greaterThan(now);
3106+
});
3107+
30653108
test('$count separate', async (ctx) => {
30663109
const { db } = ctx.sqlite;
30673110

0 commit comments

Comments
 (0)