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
2 changes: 2 additions & 0 deletions __fixtures__/plpgsql-generated/generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@
"plpgsql_deparser_fixes-58.sql": "CREATE 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": "CREATE 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": "CREATE 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_deparser_fixes-61.sql": "CREATE FUNCTION test_out_param_bare_return(\n IN a text,\n IN b uuid,\n OUT result uuid\n) RETURNS uuid\nLANGUAGE plpgsql AS $$\nDECLARE\n v_id uuid;\nBEGIN\n v_id := b;\n SELECT v_id INTO result;\n RETURN;\nEND$$",
"plpgsql_deparser_fixes-62.sql": "CREATE FUNCTION test_multi_out_bare_return(\n IN a integer,\n OUT x integer,\n OUT y text\n)\nLANGUAGE plpgsql AS $$\nBEGIN\n x := a;\n y := 'ok';\n RETURN;\nEND$$",
"plpgsql_control-1.sql": "do $$\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": "do $$\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
29 changes: 29 additions & 0 deletions __fixtures__/plpgsql/plpgsql_deparser_fixes.sql
Original file line number Diff line number Diff line change
Expand Up @@ -754,3 +754,32 @@ BEGIN
END;
RETURN v_result;
END$$;

-- Test 61: Bare RETURN in function whose single OUT param is not the first datum
-- (libpg-query 18 omits out_param_varno, so the OUT datum must be resolved by name)
CREATE FUNCTION test_out_param_bare_return(
IN a text,
IN b uuid,
OUT result uuid
) RETURNS uuid
LANGUAGE plpgsql AS $$
DECLARE
v_id uuid;
BEGIN
v_id := b;
SELECT v_id INTO result;
RETURN;
END$$;

-- Test 62: Bare RETURN with multiple OUT params (unnamed-row OUT datum)
CREATE FUNCTION test_multi_out_bare_return(
IN a integer,
OUT x integer,
OUT y text
)
LANGUAGE plpgsql AS $$
BEGIN
x := a;
y := 'ok';
RETURN;
END$$;
2 changes: 1 addition & 1 deletion config/versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"npmTag": "pg17"
},
"18": {
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"pgsql-parser": "18.0.0",
"pgsql-deparser": "18.0.0",
"@pgsql/types": "18.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/deparser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"database"
],
"devDependencies": {
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"makage": "^0.1.8"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/parse/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
],
"dependencies": {
"@pgsql/types": "^18.0.0",
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"pgsql-deparser": "workspace:*"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
],
"dependencies": {
"@pgsql/types": "^18.0.0",
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"pgsql-deparser": "workspace:*"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/pgsql-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"test:watch": "jest --watch"
},
"devDependencies": {
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"makage": "^0.1.8"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plpgsql-deparser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"database"
],
"devDependencies": {
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"makage": "^0.1.8"
},
"dependencies": {
Expand Down
22 changes: 22 additions & 0 deletions packages/plpgsql-deparser/src/plpgsql-deparser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export type ReturnInfoKind = 'void' | 'setof' | 'trigger' | 'scalar' | 'out_para

export interface ReturnInfo {
kind: ReturnInfoKind;
/** Names of OUT/INOUT/TABLE parameters, used to locate the OUT-param datum */
outParamNames?: string[];
}

export interface PLpgSQLDeparserContext {
Expand Down Expand Up @@ -2077,6 +2079,26 @@ export class PLpgSQLDeparser {
return func.out_param_varno;
}
if (func.datums) {
const outNames = returnInfo?.outParamNames;
if (returnInfo?.kind === 'out_params' && outNames && outNames.length === 1) {
const varIdx = func.datums.findIndex(
d => 'PLpgSQL_var' in d && d.PLpgSQL_var.refname === outNames[0]
);
if (varIdx >= 0) {
return varIdx;
}
}
if (returnInfo?.kind === 'out_params' && outNames && outNames.length > 1) {
const rowIdx = func.datums.findIndex(d => {
if (!('PLpgSQL_row' in d) || d.PLpgSQL_row.refname !== '(unnamed row)') return false;
const fields = d.PLpgSQL_row.fields || [];
return fields.length === outNames.length &&
fields.every((f, i) => f.name === outNames[i]);
});
if (rowIdx >= 0) {
return rowIdx;
}
}
const rowIdx = func.datums.findIndex(
d => 'PLpgSQL_row' in d && d.PLpgSQL_row.refname === '(unnamed row)' && d.PLpgSQL_row.lineno === -1
);
Expand Down
39 changes: 36 additions & 3 deletions packages/plpgsql-deparser/test-utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { parsePlPgSQL, parsePlPgSQLSync } from 'libpg-query';
import { deparseSync, PLpgSQLParseResult } from '../src';
import { parsePlPgSQL, parsePlPgSQLSync, parseSync } from 'libpg-query';
import { deparseSync, PLpgSQLParseResult, ReturnInfo } from '../src';
import { readFileSync, readdirSync, existsSync } from 'fs';
import * as path from 'path';
import { diff } from 'jest-diff';
Expand All @@ -13,6 +13,39 @@ export interface PLpgSQLTestCase {
functionBody: string;
}

/**
* Derive ReturnInfo from a CREATE FUNCTION/PROCEDURE statement's signature
* so the deparser can distinguish bare RETURN (OUT params) from RETURN <expr>.
*/
export function deriveReturnInfo(sql: string): ReturnInfo | undefined {
let stmts: any[];
try {
stmts = (parseSync(sql) as any).stmts || [];
} catch {
return undefined;
}
for (const s of stmts) {
const fn = s?.stmt?.CreateFunctionStmt;
if (!fn) continue;
if (fn.is_procedure) return { kind: 'void' };
const outParamNames: string[] = (fn.parameters || [])
.map((p: any) => p?.FunctionParameter)
.filter((fp: any) => fp && (fp.mode === 'FUNC_PARAM_OUT' || fp.mode === 'FUNC_PARAM_INOUT' || fp.mode === 'FUNC_PARAM_TABLE'))
.map((fp: any) => fp.name)
.filter((n: any): n is string => typeof n === 'string');
if (outParamNames.length > 0) return { kind: 'out_params', outParamNames };
const names = (fn.returnType?.names || [])
.map((n: any) => n?.String?.sval)
.filter((v: any): v is string => typeof v === 'string');
const typeName = (names[names.length - 1] || '').toLowerCase();
if (fn.returnType?.setof) return { kind: 'setof' };
if (typeName === 'void' || !fn.returnType) return { kind: 'void' };
if (typeName === 'trigger') return { kind: 'trigger' };
return { kind: 'scalar' };
}
return undefined;
}

export function extractFunctionBodies(sql: string): string[] {
const bodies: string[] = [];
const dollarQuoteRegex = /\$\$([^]*?)\$\$/g;
Expand Down Expand Up @@ -368,7 +401,7 @@ export class PLpgSQLTestUtils {
throw createParseError('PARSE_FAILED', testName, sql);
}

const deparsedBody = deparseSync(originalAst);
const deparsedBody = deparseSync(originalAst, undefined, deriveReturnInfo(sql));

if (!deparsedBody || deparsedBody.trim().length === 0) {
throw createParseError('DEPARSE_FAILED', testName, sql, deparsedBody);
Expand Down
2 changes: 1 addition & 1 deletion packages/plpgsql-parse/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
],
"dependencies": {
"@pgsql/types": "^18.0.0",
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"pgsql-deparser": "workspace:*",
"pgsql-parse": "workspace:*",
"plpgsql-deparser": "workspace:*"
Expand Down
6 changes: 3 additions & 3 deletions packages/plpgsql-parser/__tests__/return-info.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,23 @@ describe('getReturnInfo', () => {
CREATE FUNCTION test_out(IN x integer, OUT result integer)
LANGUAGE plpgsql AS $$ BEGIN result := x * 2; END; $$
`);
expect(getReturnInfo(stmt)).toEqual({ kind: 'out_params' });
expect(getReturnInfo(stmt)).toEqual({ kind: 'out_params', outParamNames: ['result'] });
});

it('should return out_params for INOUT parameters', () => {
const stmt = parseCreateFunction(`
CREATE FUNCTION test_inout(INOUT x integer)
LANGUAGE plpgsql AS $$ BEGIN x := x * 2; END; $$
`);
expect(getReturnInfo(stmt)).toEqual({ kind: 'out_params' });
expect(getReturnInfo(stmt)).toEqual({ kind: 'out_params', outParamNames: ['x'] });
});

it('should return out_params for RETURNS TABLE', () => {
const stmt = parseCreateFunction(`
CREATE FUNCTION test_table() RETURNS TABLE (id integer, name text)
LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT 1, 'test'; END; $$
`);
expect(getReturnInfo(stmt)).toEqual({ kind: 'out_params' });
expect(getReturnInfo(stmt)).toEqual({ kind: 'out_params', outParamNames: ['id', 'name'] });
});
});

Expand Down
2 changes: 1 addition & 1 deletion packages/plpgsql-parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"dependencies": {
"@pgsql/traverse": "workspace:*",
"@pgsql/types": "^18.0.0",
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"pgsql-deparser": "workspace:*",
"plpgsql-deparser": "workspace:*"
}
Expand Down
17 changes: 14 additions & 3 deletions packages/plpgsql-parser/src/return-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,27 @@ export function getReturnInfo(createFunctionStmt: any): ReturnInfo {

// Check for OUT/INOUT/TABLE parameters - these indicate out_params return type
if (createFunctionStmt.parameters && Array.isArray(createFunctionStmt.parameters)) {
const outParamNames = createFunctionStmt.parameters
.map((param: any) => param?.FunctionParameter)
.filter((fp: any) => {
if (!fp) return false;
const mode = fp.mode;
return mode === 'FUNC_PARAM_OUT' ||
mode === 'FUNC_PARAM_INOUT' ||
mode === 'FUNC_PARAM_TABLE';
})
.map((fp: any) => fp.name)
.filter((name: string | undefined): name is string => typeof name === 'string');
const hasOutParams = createFunctionStmt.parameters.some((param: any) => {
const fp = param?.FunctionParameter;
if (!fp) return false;
const mode = fp.mode;
return mode === 'FUNC_PARAM_OUT' ||
mode === 'FUNC_PARAM_INOUT' ||
return mode === 'FUNC_PARAM_OUT' ||
mode === 'FUNC_PARAM_INOUT' ||
mode === 'FUNC_PARAM_TABLE';
});
if (hasOutParams) {
return { kind: 'out_params' };
return { kind: 'out_params', outParamNames };
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/transform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"devDependencies": {
"@pgsql/parser": "^1.2.1",
"@pgsql/types": "^18.0.0",
"libpg-query": "18.1.1",
"libpg-query": "18.1.2",
"makage": "^0.1.8",
"pg-proto-parser": "workspace:*",
"pgsql-deparser": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/13/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/13/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/14/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/14/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/15/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/15/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/16/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/16/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/17/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/17/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/18/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/transform/src/18/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/asts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/runtime-schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/wrapped.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file was automatically generated by pg-proto-parser@1.30.6.
* This file was automatically generated by pg-proto-parser@1.31.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
* and run the pg-proto-parser generate command to regenerate this file.
*/
Expand Down
Loading
Loading