Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions __fixtures__/plpgsql-generated/generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@
"plpgsql_deparser_fixes-54.sql": "-- Test 54: Bound cursor with explicit arguments (must emit the parameter list)\nCREATE FUNCTION test_bound_cursor_args() RETURNS void\nLANGUAGE plpgsql AS $$\nDECLARE\n c CURSOR (key int, label text) FOR SELECT * FROM users WHERE id = key AND name = label;\n r record;\nBEGIN\n OPEN c(42, 'x');\n FETCH c INTO r;\n CLOSE c;\nEND$$",
"plpgsql_deparser_fixes-55.sql": "-- Test 55: RAISE with a SQLSTATE condition code (must emit SQLSTATE 'xxxxx', not a bare number)\nCREATE FUNCTION test_raise_sqlstate() RETURNS void\nLANGUAGE plpgsql AS $$\nBEGIN\n RAISE SQLSTATE '22012';\nEND$$",
"plpgsql_deparser_fixes-56.sql": "-- Test 56: RAISE EXCEPTION with a named condition (must stay a bare identifier)\nCREATE FUNCTION test_raise_named_condition() RETURNS void\nLANGUAGE plpgsql AS $$\nBEGIN\n RAISE EXCEPTION division_by_zero;\nEND$$",
"plpgsql_deparser_fixes-57.sql": "-- Test 57: Bare RETURN NEXT with OUT parameters (retvarno points at out_param_varno; must stay bare)\nCREATE FUNCTION test_return_next_out_params(OUT x integer, OUT y text) RETURNS SETOF record\nLANGUAGE plpgsql AS $$\nBEGIN\n FOR i IN 1..5 LOOP\n x := i;\n y := 'item_' || i::text;\n RETURN NEXT;\n END LOOP;\n RETURN;\nEND$$",
"plpgsql_deparser_fixes-58.sql": "-- Test 58: RETURN NEXT with a variable (retvarno must be emitted as the variable name)\nCREATE FUNCTION test_return_next_var() RETURNS SETOF integer\nLANGUAGE plpgsql AS $$\nDECLARE\n r integer;\nBEGIN\n FOR r IN SELECT g FROM generate_series(1, 3) g LOOP\n RETURN NEXT r;\n END LOOP;\nEND$$",
"plpgsql_deparser_fixes-59.sql": "-- Test 59: Top-level block with EXCEPTION clause (compiler wraps it in a synthetic outer block; must not deparse a nested BEGIN)\nCREATE FUNCTION test_toplevel_exception(a numeric, b numeric) RETURNS numeric\nLANGUAGE plpgsql AS $$\nDECLARE\n v_result numeric;\nBEGIN\n v_result := a / b;\n RETURN v_result;\nEXCEPTION\n WHEN division_by_zero THEN\n RETURN NULL;\nEND$$",
"plpgsql_deparser_fixes-60.sql": "-- Test 60: Explicit nested block with EXCEPTION inside a top-level block (nesting must be preserved)\nCREATE FUNCTION test_explicit_nested_exception(p_id integer) RETURNS text\nLANGUAGE plpgsql AS $$\nDECLARE\n v_result text;\nBEGIN\n v_result := 'unknown';\n BEGIN\n SELECT status INTO v_result FROM items WHERE id = p_id;\n EXCEPTION\n WHEN no_data_found THEN\n v_result := 'not_found';\n END;\n RETURN v_result;\nEND$$",
"plpgsql_control-1.sql": "--\n-- Tests for PL/pgSQL control structures\n--\n\n-- integer FOR loop\n\ndo $$\nbegin\n -- basic case\n for i in 1..3 loop\n raise notice '1..3: i = %', i;\n end loop;\n -- with BY, end matches exactly\n for i in 1..10 by 3 loop\n raise notice '1..10 by 3: i = %', i;\n end loop;\n -- with BY, end does not match\n for i in 1..11 by 3 loop\n raise notice '1..11 by 3: i = %', i;\n end loop;\n -- zero iterations\n for i in 1..0 by 3 loop\n raise notice '1..0 by 3: i = %', i;\n end loop;\n -- REVERSE\n for i in reverse 10..0 by 3 loop\n raise notice 'reverse 10..0 by 3: i = %', i;\n end loop;\n -- potential overflow\n for i in 2147483620..2147483647 by 10 loop\n raise notice '2147483620..2147483647 by 10: i = %', i;\n end loop;\n -- potential overflow, reverse direction\n for i in reverse -2147483620..-2147483647 by 10 loop\n raise notice 'reverse -2147483620..-2147483647 by 10: i = %', i;\n end loop;\nend$$",
"plpgsql_control-2.sql": "-- BY can't be zero or negative\ndo $$\nbegin\n for i in 1..3 by 0 loop\n raise notice '1..3 by 0: i = %', i;\n end loop;\nend$$",
"plpgsql_control-3.sql": "do $$\nbegin\n for i in 1..3 by -1 loop\n raise notice '1..3 by -1: i = %', i;\n end loop;\nend$$",
Expand Down
52 changes: 52 additions & 0 deletions __fixtures__/plpgsql/plpgsql_deparser_fixes.sql
Original file line number Diff line number Diff line change
Expand Up @@ -702,3 +702,55 @@ LANGUAGE plpgsql AS $$
BEGIN
RAISE EXCEPTION division_by_zero;
END$$;

-- Test 57: Bare RETURN NEXT with OUT parameters (retvarno points at out_param_varno; must stay bare)
CREATE FUNCTION test_return_next_out_params(OUT x integer, OUT y text) RETURNS SETOF record
LANGUAGE plpgsql AS $$
BEGIN
FOR i IN 1..5 LOOP
x := i;
y := 'item_' || i::text;
RETURN NEXT;
END LOOP;
RETURN;
END$$;

-- Test 58: RETURN NEXT with a variable (retvarno must be emitted as the variable name)
CREATE FUNCTION test_return_next_var() RETURNS SETOF integer
LANGUAGE plpgsql AS $$
DECLARE
r integer;
BEGIN
FOR r IN SELECT g FROM generate_series(1, 3) g LOOP
RETURN NEXT r;
END LOOP;
END$$;

-- Test 59: Top-level block with EXCEPTION clause (compiler wraps it in a synthetic outer block; must not deparse a nested BEGIN)
CREATE FUNCTION test_toplevel_exception(a numeric, b numeric) RETURNS numeric
LANGUAGE plpgsql AS $$
DECLARE
v_result numeric;
BEGIN
v_result := a / b;
RETURN v_result;
EXCEPTION
WHEN division_by_zero THEN
RETURN NULL;
END$$;

-- Test 60: Explicit nested block with EXCEPTION inside a top-level block (nesting must be preserved)
CREATE FUNCTION test_explicit_nested_exception(p_id integer) RETURNS text
LANGUAGE plpgsql AS $$
DECLARE
v_result text;
BEGIN
v_result := 'unknown';
BEGIN
SELECT status INTO v_result FROM items WHERE id = p_id;
EXCEPTION
WHEN no_data_found THEN
v_result := 'not_found';
END;
RETURN v_result;
END$$;
2 changes: 1 addition & 1 deletion packages/parse/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"round-trip"
],
"dependencies": {
"@libpg-query/parser": "^17.6.10",
"@libpg-query/parser": "^17.8.0",
"@pgsql/types": "^17.6.2",
"pgsql-deparser": "workspace:*"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,25 +238,21 @@ END"

exports[`plpgsql-deparser bug fixes SQLSTATE exception conditions should emit SQLSTATE codes with the SQLSTATE keyword 1`] = `
"BEGIN
BEGIN
RETURN 1;
EXCEPTION
WHEN unique_violation OR SQLSTATE '23503' THEN
RETURN -1;
WHEN SQLSTATE 'P0001' THEN
RETURN -2;
END;
RETURN 1;
EXCEPTION
WHEN unique_violation OR SQLSTATE '23503' THEN
RETURN -1;
WHEN SQLSTATE 'P0001' THEN
RETURN -2;
END"
`;

exports[`plpgsql-deparser bug fixes bare RAISE re-throw should keep a bare RAISE bare (not RAISE EXCEPTION;) 1`] = `
"BEGIN
BEGIN
PERFORM 1;
EXCEPTION
WHEN others THEN
RAISE;
END;
PERFORM 1;
EXCEPTION
WHEN others THEN
RAISE;
END"
`;

Expand Down Expand Up @@ -370,17 +366,15 @@ END"

exports[`plpgsql-deparser bug fixes deep nesting and sequential blocks should handle block inside exception handler 1`] = `
"BEGIN
BEGIN
PERFORM risky();
EXCEPTION
WHEN others THEN
BEGIN
PERFORM log_error();
EXCEPTION
WHEN others THEN
RAISE NOTICE 'even logging failed';
END;
END;
PERFORM risky();
EXCEPTION
WHEN others THEN
BEGIN
PERFORM log_error();
EXCEPTION
WHEN others THEN
RAISE NOTICE 'even logging failed';
END;
END"
`;

Expand Down Expand Up @@ -548,6 +542,33 @@ BEGIN
END"
`;

exports[`plpgsql-deparser bug fixes top-level EXCEPTION wrapper block should not emit a nested BEGIN for a top-level block with EXCEPTION 1`] = `
"DECLARE
v_result numeric;
BEGIN
v_result := a / b;
RETURN v_result;
EXCEPTION
WHEN division_by_zero THEN
RETURN NULL;
END"
`;

exports[`plpgsql-deparser bug fixes top-level EXCEPTION wrapper block should preserve an explicit nested block with EXCEPTION 1`] = `
"DECLARE
v_result text;
BEGIN
v_result := 'unknown';
BEGIN
SELECT status INTO v_result FROM items WHERE id = p_id;
EXCEPTION
WHEN no_data_found THEN
v_result := 'not_found';
END;
RETURN v_result;
END"
`;

exports[`plpgsql-deparser bug fixes untested statement types should handle ASSERT statement 1`] = `
"BEGIN
ASSERT p_x > 0, 'x must be positive';
Expand Down
Loading
Loading