Skip to content

Commit 969d923

Browse files
authored
fix(pg-sql2): use same placeholder for same sql.value node (#705)
1 parent d2770a5 commit 969d923

2 files changed

Lines changed: 50 additions & 4 deletions

File tree

packages/pg-sql2/__tests__/general.test.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,47 @@ describe("sql.compile", () => {
9494
expect(sql.compile(node)).toEqual({ text: "select 1", values: [] });
9595
});
9696

97-
it("with values", () => {
97+
it("with value", () => {
9898
const node = sql.query`select ${sql.value(1)}::integer`;
9999
expect(sql.compile(node)).toEqual({
100100
text: "select $1::integer",
101101
values: [1],
102102
});
103103
});
104104

105+
it("with two values", () => {
106+
const node = sql.query`select ${sql.value(1)}::integer + ${sql.value(
107+
2
108+
)}::integer`;
109+
expect(sql.compile(node)).toEqual({
110+
text: "select $1::integer + $2::integer",
111+
values: [1, 2],
112+
});
113+
});
114+
115+
it("with similar value twice", () => {
116+
// This _should not_ use the same placeholder as it makes the compiled SQL
117+
// dependent on the values used; better for the SQL to be stable.
118+
const node = sql.query`select ${sql.value(1)}::integer + ${sql.value(
119+
1
120+
)}::integer`;
121+
expect(sql.compile(node)).toEqual({
122+
text: "select $1::integer + $2::integer",
123+
values: [1, 1],
124+
});
125+
});
126+
127+
it("with exact same value node twice", () => {
128+
// This _should_ use the same placeholder because we've explicitly used the
129+
// same `sql.value` node.
130+
const sqlValue = sql.value(1);
131+
const node = sql.query`select ${sqlValue}::integer + ${sqlValue}::integer`;
132+
expect(sql.compile(node)).toEqual({
133+
text: "select $1::integer + $1::integer",
134+
values: [1],
135+
});
136+
});
137+
105138
it("with sub-sub-sub query", () => {
106139
const node = sql.query`select ${sql.query`1 ${sql.query`from ${sql.query`foo`}`}`}`;
107140
expect(sql.compile(node)).toEqual({

packages/pg-sql2/src/index.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ export function compile(sql: SQLQuery | SQLNode): QueryConfig {
102102
// compile time.
103103
const values = [];
104104

105+
// If we use the exact same `sql.value` node more than once, we should use
106+
// the same placeholder for both (for performance and efficiency)
107+
const valueNodeToPlaceholder = new Map();
108+
105109
// When we come accross a symbol in our identifier, we create a unique
106110
// alias for it that shouldn’t be in the users schema. This helps maintain
107111
// sanity when constructing large Sql queries with many aliases.
@@ -145,10 +149,19 @@ export function compile(sql: SQLQuery | SQLNode): QueryConfig {
145149
);
146150
break;
147151
}
148-
case "VALUE":
149-
values.push(item.value);
150-
sqlFragments.push(`$${values.length}`);
152+
case "VALUE": {
153+
let placeholder = valueNodeToPlaceholder.get(item);
154+
155+
// If there's no placeholder for this value node, create one
156+
if (!placeholder) {
157+
values.push(item.value);
158+
placeholder = `$${values.length}`;
159+
valueNodeToPlaceholder.set(item, placeholder);
160+
}
161+
162+
sqlFragments.push(placeholder);
151163
break;
164+
}
152165
default:
153166
// This cannot happen
154167
}

0 commit comments

Comments
 (0)