Skip to content

Commit 2769aa6

Browse files
committed
fix(plpgsql-deparser): normalize whitespace after INTO insertion
The parser strips 'INTO <target>' from the query but leaves whitespace behind. This fix normalizes the leading whitespace after the insertion point to avoid large gaps like 'SELECT x INTO y FROM z'.
1 parent f791150 commit 2769aa6

File tree

2 files changed

+19
-12
lines changed

2 files changed

+19
-12
lines changed

packages/plpgsql-deparser/__tests__/__snapshots__/deparser-fixes.test.ts.snap

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ exports[`plpgsql-deparser bug fixes INTO clause depth-aware scanner should handl
44
"DECLARE
55
v_id integer;
66
BEGIN
7-
SELECT id INTO STRICT v_id FROM users WHERE email = 'test@example.com';
7+
SELECT id INTO STRICT v_id FROM users WHERE email = 'test@example.com';
88
RETURN v_id;
99
END"
1010
`;
@@ -16,7 +16,7 @@ BEGIN
1616
WITH totals AS (
1717
SELECT sum(amount) as total FROM orders
1818
)
19-
SELECT total INTO v_total FROM totals;
19+
SELECT total INTO v_total FROM totals;
2020
RETURN v_total;
2121
END"
2222
`;
@@ -25,7 +25,7 @@ exports[`plpgsql-deparser bug fixes INTO clause depth-aware scanner should handl
2525
"DECLARE
2626
v_count integer;
2727
BEGIN
28-
SELECT count(*) INTO v_count FROM (
28+
SELECT count(*) INTO v_count FROM (
2929
SELECT id FROM users
3030
UNION ALL
3131
SELECT id FROM admins
@@ -38,7 +38,7 @@ exports[`plpgsql-deparser bug fixes INTO clause depth-aware scanner should handl
3838
"DECLARE
3939
v_result text;
4040
BEGIN
41-
SELECT $tag$some FROM text$tag$ INTO v_result FROM dual;
41+
SELECT $tag$some FROM text$tag$ INTO v_result FROM dual;
4242
RETURN v_result;
4343
END"
4444
`;
@@ -47,7 +47,7 @@ exports[`plpgsql-deparser bug fixes INTO clause depth-aware scanner should handl
4747
"DECLARE
4848
v_name text;
4949
BEGIN
50-
SELECT "user-name" INTO v_name FROM "my-schema"."user-table" WHERE id = 1;
50+
SELECT "user-name" INTO v_name FROM "my-schema"."user-table" WHERE id = 1;
5151
RETURN v_name;
5252
END"
5353
`;
@@ -56,7 +56,7 @@ exports[`plpgsql-deparser bug fixes INTO clause depth-aware scanner should inser
5656
"DECLARE
5757
v_count integer;
5858
BEGIN
59-
SELECT count(*) INTO v_count FROM users;
59+
SELECT count(*) INTO v_count FROM users;
6060
RETURN v_count;
6161
END"
6262
`;
@@ -65,7 +65,7 @@ exports[`plpgsql-deparser bug fixes INTO clause depth-aware scanner should not i
6565
"DECLARE
6666
v_result integer;
6767
BEGIN
68-
SELECT (SELECT max(id) FROM orders) INTO v_result FROM users WHERE id = 1;
68+
SELECT (SELECT max(id) FROM orders) INTO v_result FROM users WHERE id = 1;
6969
RETURN v_result;
7070
END"
7171
`;
@@ -103,7 +103,7 @@ END"
103103

104104
exports[`plpgsql-deparser bug fixes Record field qualification (recfield) should handle SELECT INTO with record fields 1`] = `
105105
"BEGIN
106-
SELECT is_active INTO new.is_active FROM users WHERE id = NEW.user_id;
106+
SELECT is_active INTO new.is_active FROM users WHERE id = NEW.user_id;
107107
RETURN NEW;
108108
END"
109109
`;
@@ -148,7 +148,7 @@ exports[`plpgsql-deparser bug fixes combined scenarios should handle SELECT INTO
148148
"DECLARE
149149
v_count integer;
150150
BEGIN
151-
SELECT count(*) INTO v_count FROM orders WHERE user_id = NEW.user_id;
151+
SELECT count(*) INTO v_count FROM orders WHERE user_id = NEW.user_id;
152152
IF v_count > 100 THEN
153153
NEW.is_premium := true;
154154
END IF;

packages/plpgsql-deparser/src/plpgsql-deparser.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,10 +1065,17 @@ export class PLpgSQLDeparser {
10651065
// Only insert INTO at depth 0 (not inside subqueries)
10661066
const insertPos = this.findIntoInsertionPoint(sql);
10671067
if (insertPos !== -1) {
1068-
sql = sql.slice(0, insertPos) + intoClause + sql.slice(insertPos);
1068+
// The parser strips "INTO <target>" from the query but leaves whitespace behind.
1069+
// We need to normalize the leading whitespace after the insertion point to avoid
1070+
// large gaps like "SELECT x INTO y FROM z"
1071+
const before = sql.slice(0, insertPos);
1072+
let after = sql.slice(insertPos);
1073+
// Collapse leading whitespace (but preserve a single space before the next keyword)
1074+
after = after.replace(/^[ \t]+/, ' ');
1075+
sql = before + intoClause + after;
10691076
} else {
1070-
// Fallback: append at end if no suitable position found
1071-
sql = sql + intoClause;
1077+
// -1 means INTO already exists at depth 0, don't add another one
1078+
// (this shouldn't happen in practice since the parser strips INTO)
10721079
}
10731080
}
10741081

0 commit comments

Comments
 (0)