Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pg] Fix jsonb field escaping data before insert #666

Closed
wants to merge 15 commits into from
5 changes: 3 additions & 2 deletions drizzle-orm/src/pg-core/columns/jsonb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ export class PgJsonb<T extends ColumnBaseConfig> extends PgColumn<PgJsonbHKT, T>
return 'jsonb';
}

override mapToDriverValue(value: T['data']): string {
return JSON.stringify(value);
override mapToDriverValue(value: T['data']): T['data'] {
return value;
//return JSON.stringify(value);
}

override mapFromDriverValue(value: T['data'] | string): T['data'] {
Expand Down
43 changes: 40 additions & 3 deletions integration-tests/tests/pg-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dotenv/config';
import type { TestFn } from 'ava';
import anyTest from 'ava';
import Docker from 'dockerode';
import { asc, eq, Name, placeholder, sql } from 'drizzle-orm';
import { asc, eq, Name, placeholder, sql, and } from 'drizzle-orm';
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { drizzle } from 'drizzle-orm/node-postgres';
import {
Expand Down Expand Up @@ -55,6 +55,15 @@ const publicUsersTable = pgTable('users', {
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});

type MetaData = {
foo: string;
bar: number;
}
const metaDataTable = pgTable('meta_data', {
id: serial('id').primaryKey(),
data: jsonb('data').$type<MetaData>(),
});

interface Context {
docker: Docker;
pgContainer: Docker.Container;
Expand Down Expand Up @@ -163,6 +172,14 @@ test.beforeEach(async (t) => {
)
`,
);
await ctx.db.execute(
sql`
create table meta_data (
id serial primary key,
data jsonb
)
`,
);
});

test.serial('select all fields', async (t) => {
Expand Down Expand Up @@ -338,6 +355,21 @@ test.serial('insert + select', async (t) => {
]);
});

test.serial('json object insert', async (t) => {
const { db } = t.context;

const bar = 33
const foo = 'bar';

await db.insert(metaDataTable).values({ data: {foo, bar} });
const result = await db.select({
id: metaDataTable.id,
data: metaDataTable.data,
}).from(metaDataTable).where(and(sql`data->'foo' = ${foo}`, sql`data->'bar' = ${bar}`));

t.deepEqual(result, [{ id: 1, data: {foo: 'bar', bar: 33}}]);
});

test.serial('json insert', async (t) => {
const { db } = t.context;

Expand All @@ -346,8 +378,13 @@ test.serial('json insert', async (t) => {
id: usersTable.id,
name: usersTable.name,
jsonb: usersTable.jsonb,
}).from(usersTable);

}).from(usersTable).where(
and(
eq(usersTable.name, 'John'),
sql.raw(`jsonb->>0 = 'foo'`),
)
);

t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]);
});

Expand Down
71 changes: 65 additions & 6 deletions integration-tests/tests/postgres.js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
timestamp,
uuid as pgUuid,
varchar,
//customType,
} from 'drizzle-orm/pg-core';
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import { drizzle } from 'drizzle-orm/postgres-js';
Expand All @@ -48,6 +49,25 @@ import { type Equal, Expect } from './utils';

const QUERY_LOGGING = false;

// const customJsonb = customType<{ data: any }>({
// dataType() {
// return 'jsonb';
// },
// toDriver(val) {
// val as any;
// },
// fromDriver(value) {
// if (typeof value === 'string') {
// try {
// return JSON.parse(value) as any;
// } catch {
// return value as any;
// }
// }
// return value as any;
// },
// });

const usersTable = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
Expand Down Expand Up @@ -92,6 +112,16 @@ const usersMigratorTable = pgTable('users12', {
email: text('email').notNull(),
});

type MetaData = {
foo: string;
bar: number;
}

const metaDataTable = pgTable('meta_data', {
id: serial('id').primaryKey(),
data: jsonb('data').$type<MetaData>(),
});

interface Context {
docker: Docker;
pgContainer: Docker.Container;
Expand Down Expand Up @@ -226,6 +256,14 @@ test.beforeEach(async (t) => {
)
`,
);
await ctx.db.execute(
sql`
create table meta_data (
id serial primary key,
data jsonb
)
`,
);
});

test.serial('select all fields', async (t) => {
Expand Down Expand Up @@ -401,6 +439,22 @@ test.serial('insert + select', async (t) => {
]);
});

test.serial('json object insert', async (t) => {
const { db } = t.context;

const bar = 33
const foo = 'bar';

await db.insert(metaDataTable).values({ data: {foo, bar} });
const result = await db.select({
id: metaDataTable.id,
data: metaDataTable.data,
}).from(metaDataTable).where(and(sql`data->'foo' = ${foo}`, sql`data->'bar' = ${bar}`));

t.deepEqual([result.at(0)], [{ id: 1, data: {foo: 'bar', bar: 33}}]);
});


test.serial('json insert', async (t) => {
const { db } = t.context;

Expand All @@ -409,8 +463,13 @@ test.serial('json insert', async (t) => {
id: usersTable.id,
name: usersTable.name,
jsonb: usersTable.jsonb,
}).from(usersTable);

}).from(usersTable).where(
and(
eq(usersTable.name, 'John'),
sql.raw(`jsonb->>0 = 'foo'`),
)
);

t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]);
});

Expand Down Expand Up @@ -812,7 +871,7 @@ test.serial('build query insert with onConflict do update', async (t) => {

t.deepEqual(query, {
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do update set "name" = $3',
params: ['John', '["foo","bar"]', 'John1'],
params: ['John', ["foo","bar"], 'John1'],
});
});

Expand All @@ -826,7 +885,7 @@ test.serial('build query insert with onConflict do update / multiple columns', a

t.deepEqual(query, {
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id","name") do update set "name" = $3',
params: ['John', '["foo","bar"]', 'John1'],
params: ['John', ["foo","bar"], 'John1'],
});
});

Expand All @@ -840,7 +899,7 @@ test.serial('build query insert with onConflict do nothing', async (t) => {

t.deepEqual(query, {
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict do nothing',
params: ['John', '["foo","bar"]'],
params: ['John', ["foo","bar"]],
});
});

Expand All @@ -854,7 +913,7 @@ test.serial('build query insert with onConflict do nothing + target', async (t)

t.deepEqual(query, {
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do nothing',
params: ['John', '["foo","bar"]'],
params: ['John', ["foo","bar"]],
});
});

Expand Down