Skip to content

Commit 570c97b

Browse files
committed
Add optional chaining to prototype check in is()
Object.getPrototypeOf() returns null for objects created with Object.create(null). Without optional chaining, accessing .constructor on null throws a TypeError. This affects insert and update operations when a user passes an Object.create(null)-based value (e.g. for a JSON column), since the query builder calls is(colValue, SQL) on each value.
1 parent 273c780 commit 570c97b

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

drizzle-orm/src/entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function is<T extends DrizzleEntityClass<any>>(value: any, type: T): valu
2626
);
2727
}
2828

29-
let cls = Object.getPrototypeOf(value).constructor;
29+
let cls = Object.getPrototypeOf(value)?.constructor;
3030
if (cls) {
3131
// Traverse the prototype chain to find the entityKind
3232
while (cls) {

drizzle-orm/tests/is.test.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,56 @@
1+
import postgres from 'postgres';
12
import { describe, test } from 'vitest';
2-
import { Column, is } from '~/index.ts';
3-
import { PgArray, PgColumn, PgSerial, pgTable, serial } from '~/pg-core/index.ts';
3+
import { Column, is, SQL } from '~/index.ts';
4+
import { jsonb, PgArray, PgColumn, PgSerial, pgTable, serial, text } from '~/pg-core/index.ts';
5+
import { drizzle } from '~/postgres-js/index.ts';
6+
import { eq } from '~/sql/index.ts';
47

58
const pgExampleTable = pgTable('test', {
69
a: serial('a').array(),
710
});
811

12+
const nullProtoTable = pgTable('null_proto_test', {
13+
id: serial('id').primaryKey(),
14+
name: text('name').notNull(),
15+
data: jsonb('data'),
16+
});
17+
18+
const db = drizzle(postgres(''));
19+
920
describe.concurrent('is', () => {
1021
test('Column', ({ expect }) => {
1122
expect(is(pgExampleTable.a, Column)).toBe(true);
1223
expect(is(pgExampleTable.a, PgColumn)).toBe(true);
1324
expect(is(pgExampleTable.a, PgArray)).toBe(true);
1425
expect(is(pgExampleTable.a, PgSerial)).toBe(false);
1526
});
27+
28+
test('Object.create(null) value does not throw', ({ expect }) => {
29+
const nullProtoValue = Object.create(null);
30+
expect(is(nullProtoValue, SQL)).toBe(false);
31+
expect(is(nullProtoValue, Column)).toBe(false);
32+
});
33+
34+
test('insert with Object.create(null) value', ({ expect }) => {
35+
const jsonValue = Object.create(null);
36+
jsonValue.key = 'value';
37+
38+
const query = db
39+
.insert(nullProtoTable)
40+
.values({ name: 'test', data: jsonValue });
41+
42+
expect(query.toSQL()).toBeTruthy();
43+
});
44+
45+
test('update with Object.create(null) value', ({ expect }) => {
46+
const jsonValue = Object.create(null);
47+
jsonValue.key = 'value';
48+
49+
const query = db
50+
.update(nullProtoTable)
51+
.set({ data: jsonValue })
52+
.where(eq(nullProtoTable.id, 1));
53+
54+
expect(query.toSQL()).toBeTruthy();
55+
});
1656
});

0 commit comments

Comments
 (0)