Debugging plx functions¶
A plx function is transpiled to plpgsql at CREATE FUNCTION time and executed by
the plpgsql interpreter. Runtime errors are therefore reported by plpgsql, with a
line number and CONTEXT that refer to the generated plpgsql stored in
pg_proc.prosrc, not to your dialect source. This page explains how to correlate
the two.
See the generated plpgsql¶
The generated body is ordinary, readable plpgsql, one construct per source construct. View it with:
The first line is a plx sentinel comment (/*plx:v1:<dialect>:<hash>*/), then the
DECLARE block, BEGIN, the body, and a trailing comment that carries your
original source (base64-encoded). When an error says line 8 at RAISE, line 8 is
counted from that sentinel line.
Recover your original source¶
plx embeds the original dialect source in the function, so you can always get it back. Define this helper once:
CREATE FUNCTION plx_source(f regprocedure) RETURNS text
LANGUAGE sql STABLE AS $$
SELECT convert_from(
decode((regexp_match(prosrc, 'plx-orig:b64[$]([^$]*)[$]plx-orig'))[1],
'base64'),
'UTF8')
FROM pg_proc WHERE oid = f;
$$;
Then:
returns exactly the Ruby / PHP / JavaScript / Python / COBOL body you wrote.
Why the error line differs from your source line¶
plpgsql requires every local variable to be declared in a DECLARE block before
BEGIN. plx therefore hoists your declarations to the top of the generated
function. That, plus the one-line sentinel and the BEGIN, shifts the body down
relative to your source, so the reported line does not equal your source line.
To locate a failing statement:
- Read the reported line and construct (
line N at RAISE,at assignment, …). - Print the generated body numbered and look at line N:
SELECT string_agg(n || ': ' || l, E'\n')
FROM regexp_split_to_table(
(SELECT prosrc FROM pg_proc WHERE oid = 'my_func(int)'::regprocedure),
E'\n') WITH ORDINALITY AS x(l, n)
WHERE l NOT LIKE '%plx-orig%';
- The plpgsql at line N maps one-to-one to the statement you wrote; compare it to
plx_source('my_func(int)').
Because the generated plpgsql mirrors your logic statement for statement, the
construct name in the CONTEXT (RAISE, assignment, FOR over SELECT, …)
plus the surrounding lines is usually enough to pinpoint the source statement.
Keeping functions small keeps this trivial.
Compile-time errors¶
Errors raised at CREATE FUNCTION time (a construct outside the supported subset,
a type that cannot be inferred, a missing END-IF, …) come from the plx
transpiler and carry your dialect's name and the source line, for example:
These already point at your source, so no correlation is needed.