Skip to content

Commit 0beaa36

Browse files
plxclaude
andcommitted
Release 1.3.1: transpiler audit memory-safety fixes (code-only patch)
Code-only patch (no catalog changes) carrying the fixes from the full-repo transpiler audit (PR #1): the T_EOF token-array capacity guard, the int64 sb_ensure() growth/overflow clamp, check_stack_depth() at the recursion guards, the Python indent-stack limit error, raw single-quoted strings for Ruby/PHP, PHP ${name} interpolation, non-decimal integer literals rewritten to decimal (with a clean out-of-range error), and the -Wmissing-variable-declarations cleanup. Bumps default_version and META.json to 1.3.1, adds plx--1.3.1.sql (identical to 1.3.0) and the plx--1.3.0--1.3.1.sql version-advancing upgrade script. Verified against PG18.4: clean build, all 13 installcheck tests pass, and the 1.3.0 -> 1.3.1 ALTER EXTENSION UPDATE applies cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e88d68d commit 0beaa36

6 files changed

Lines changed: 257 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,43 @@ All notable changes to plx are recorded here. The format follows
44
[Keep a Changelog](https://keepachangelog.com/), and plx uses the extension
55
version in `plx.control` (currently `1.0`).
66

7+
## [1.3.1] - 2026-07-16
8+
9+
Code-only patch release (no catalog changes) carrying the memory-safety and
10+
robustness fixes from the full-repo transpiler audit (#1). Upgrade with
11+
`ALTER EXTENSION plx UPDATE TO '1.3.1'` after installing the new module.
12+
13+
### Fixed
14+
15+
- Missing capacity guard on the trailing `T_EOF` token write in `lex()`: a source
16+
that lexed to exactly `cap-1` tokens overflowed the token array by one
17+
(heap overflow / SIGSEGV). All token writes are now guarded.
18+
- `plx_strbuild` `sb_ensure()` doubled its capacity in `int32`, which could wrap
19+
negative near ~1GB and spin forever while passing a bogus size to `repalloc`.
20+
Growth is now computed in 64-bit and clamped to `MaxAllocSize`, so an
21+
oversized request raises the standard allocation error cleanly.
22+
- The recursion-guarded transpiler entry points now call `check_stack_depth()`,
23+
so deeply nested hostile input raises a clean error honoring `max_stack_depth`
24+
instead of overflowing the C stack and crashing the backend.
25+
- The Python lexer raises a clean "indentation nested too deeply" error at the
26+
indent-stack limit instead of emitting an unbalanced `T_INDENT` that mis-nested
27+
every enclosing block.
28+
- Raw single-quoted strings in Ruby and PHP keep backslashes literal (`'\n'`,
29+
`'C:\temp'`); only `\\` and `\'` are special. Double-quoted and Python/JS
30+
single-quoted escapes are unchanged.
31+
- PHP `${name}` curly interpolation in double-quoted strings is now recognized
32+
(was emitted verbatim).
33+
- Non-decimal integer literals (`0x`/`0o`/`0b`, with `_` group separators) are
34+
lexed as a single token and rewritten to decimal, keeping generated plpgsql
35+
portable to PG13-15 (which accept `0x/0o/0b` only on PG16+). A literal that
36+
overflows 64 bits raises a clean "integer literal out of range" error rather
37+
than emitting invalid SQL.
38+
39+
### Other
40+
41+
- Declare the built-in dialect descriptors in `plx.h`, clearing
42+
`-Wmissing-variable-declarations` across the dialect translation units.
43+
744
## [1.3.0] - 2026-07-15
845

946
Trigger row mutation across dialects, plus the cookbook and limitations

META.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
"name": "plx",
33
"abstract": "Write PostgreSQL functions in Ruby, PHP, JavaScript, TypeScript, Python, Go, COBOL, Oracle PL/SQL, or Transact-SQL, compiled to plpgsql",
44
"description": "plx is a dialect-pluggable procedural language for PostgreSQL. A function body written in a Ruby, PHP, JavaScript, TypeScript, Python, Go, COBOL, Oracle PL/SQL, or Transact-SQL (SQL Server) dialect is transpiled to plpgsql at CREATE FUNCTION time and stored in pg_proc.prosrc, then executed by the standard plpgsql interpreter. There is no separate language runtime in the backend, the generated plpgsql is visible in the catalog, and every plpgsql construct is reachable from every dialect. plx also ships plx_strbuild, an expanded-object string builder with amortized-O(1) append that fixes plpgsql's quadratic in-loop string concatenation.",
5-
"version": "1.3.0",
5+
"version": "1.3.1",
66
"maintainer": [
77
"Command Prompt, Inc. <pgxn@commandprompt.com>"
88
],
99
"license": "mit",
1010
"provides": {
1111
"plx": {
1212
"abstract": "Dialect-pluggable procedural language over plpgsql (Ruby, PHP, JavaScript, TypeScript, Python, Go, COBOL, Oracle PL/SQL, Transact-SQL)",
13-
"file": "plx--1.3.0.sql",
13+
"file": "plx--1.3.1.sql",
1414
"docfile": "README.md",
15-
"version": "1.3.0"
15+
"version": "1.3.1"
1616
}
1717
},
1818
"prereqs": {

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ MODULE_big = plx
33
OBJS = src/plx_core.o src/plx_transpile.o src/plx_strbuild.o src/plx_dialect_ruby.o src/plx_dialect_php.o src/plx_dialect_js.o src/plx_dialect_python.o src/plx_dialect_cobol.o src/plx_dialect_plsql.o src/plx_dialect_ts.o src/plx_dialect_tsql.o src/plx_dialect_go.o
44

55
EXTENSION = plx
6-
DATA = plx--1.0.sql plx--1.1.sql plx--1.1.1.sql plx--1.2.sql plx--1.2.1.sql plx--1.2.2.sql plx--1.3.0.sql plx--1.0--1.1.sql plx--1.1--1.1.1.sql plx--1.1.1--1.2.sql plx--1.2--1.2.1.sql plx--1.2.1--1.2.2.sql plx--1.2.2--1.3.0.sql
6+
DATA = plx--1.0.sql plx--1.1.sql plx--1.1.1.sql plx--1.2.sql plx--1.2.1.sql plx--1.2.2.sql plx--1.3.0.sql plx--1.3.1.sql plx--1.0--1.1.sql plx--1.1--1.1.1.sql plx--1.1.1--1.2.sql plx--1.2--1.2.1.sql plx--1.2.1--1.2.2.sql plx--1.2.2--1.3.0.sql plx--1.3.0--1.3.1.sql
77

88
# pg_regress suite (make installcheck). test/run_corpus.py is an additional
99
# Ruby corpus runner.

plx--1.3.0--1.3.1.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/* plx 1.3.0 -> 1.3.1: no catalog changes.
2+
*
3+
* 1.3.1 is a code-only patch: memory-safety and robustness fixes in the shared
4+
* transpiler and string builder (loadable module $libdir/plx). No SQL objects
5+
* the extension defines change, so this update script only advances the recorded
6+
* version. Installing the matching $libdir/plx is what delivers the fixes. */

plx--1.3.1.sql

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/* plx 1.0: registers the plx dialect languages and the string-builder type */
2+
3+
\echo Use "CREATE EXTENSION plx" to load this file. \quit
4+
5+
/*
6+
* plx_strbuild: an expanded-object string builder. Its flattened form is a
7+
* text-compatible varlena, so it casts to and from text without a function.
8+
* plx_sb_append grows the buffer in place across a loop (amortized O(1)),
9+
* avoiding the quadratic cost of `s := s || 'x'` on plain text.
10+
*/
11+
CREATE TYPE plx_strbuild;
12+
13+
CREATE FUNCTION plx_sb_in(cstring) RETURNS plx_strbuild
14+
AS '$libdir/plx', 'plx_sb_in' LANGUAGE C IMMUTABLE STRICT;
15+
CREATE FUNCTION plx_sb_out(plx_strbuild) RETURNS cstring
16+
AS '$libdir/plx', 'plx_sb_out' LANGUAGE C IMMUTABLE STRICT;
17+
18+
CREATE TYPE plx_strbuild (
19+
INPUT = plx_sb_in,
20+
OUTPUT = plx_sb_out,
21+
LIKE = text,
22+
STORAGE = extended
23+
);
24+
25+
CREATE FUNCTION plx_sb_append_support(internal) RETURNS internal
26+
AS '$libdir/plx', 'plx_sb_append_support' LANGUAGE C;
27+
28+
CREATE FUNCTION plx_sb_append(plx_strbuild, text) RETURNS plx_strbuild
29+
AS '$libdir/plx', 'plx_sb_append' LANGUAGE C IMMUTABLE
30+
SUPPORT plx_sb_append_support;
31+
32+
-- plx_strbuild is implicitly usable as text (so it works in RETURN, string
33+
-- functions, and concatenation). text becomes a builder only in assignment
34+
-- context, which avoids operator ambiguity such as `builder || text`.
35+
CREATE CAST (plx_strbuild AS text) WITHOUT FUNCTION AS IMPLICIT;
36+
CREATE CAST (text AS plx_strbuild) WITHOUT FUNCTION AS ASSIGNMENT;
37+
38+
/*
39+
* Runtime call handler = plpgsql's OWN handler. Binding a fresh pg_proc row to
40+
* plpgsql.so's exported symbol is legal (CreateProceduralLanguage only checks
41+
* the handler RETURNS language_handler). Execution is therefore 100% stock
42+
* plpgsql; plx contributes zero call-path C.
43+
*/
44+
CREATE FUNCTION plx_call_handler()
45+
RETURNS language_handler
46+
AS '$libdir/plpgsql', 'plpgsql_call_handler'
47+
LANGUAGE C;
48+
49+
/* Ruby dialect validator + inline handler live in plx.so */
50+
CREATE FUNCTION plx_ruby_validator(oid)
51+
RETURNS void
52+
AS '$libdir/plx', 'plx_ruby_validator'
53+
LANGUAGE C STRICT;
54+
55+
CREATE FUNCTION plx_ruby_inline_handler(internal)
56+
RETURNS void
57+
AS '$libdir/plx', 'plx_ruby_inline_handler'
58+
LANGUAGE C;
59+
60+
CREATE TRUSTED LANGUAGE plxruby
61+
HANDLER plx_call_handler
62+
INLINE plx_ruby_inline_handler
63+
VALIDATOR plx_ruby_validator;
64+
65+
COMMENT ON LANGUAGE plxruby IS 'plx Ruby dialect (transpiles to plpgsql)';
66+
67+
/* PHP dialect */
68+
CREATE FUNCTION plx_php_validator(oid)
69+
RETURNS void
70+
AS '$libdir/plx', 'plx_php_validator'
71+
LANGUAGE C STRICT;
72+
73+
CREATE FUNCTION plx_php_inline_handler(internal)
74+
RETURNS void
75+
AS '$libdir/plx', 'plx_php_inline_handler'
76+
LANGUAGE C;
77+
78+
CREATE TRUSTED LANGUAGE plxphp
79+
HANDLER plx_call_handler
80+
INLINE plx_php_inline_handler
81+
VALIDATOR plx_php_validator;
82+
83+
COMMENT ON LANGUAGE plxphp IS 'plx PHP dialect (transpiles to plpgsql)';
84+
85+
/* JavaScript dialect */
86+
CREATE FUNCTION plx_js_validator(oid)
87+
RETURNS void
88+
AS '$libdir/plx', 'plx_js_validator'
89+
LANGUAGE C STRICT;
90+
91+
CREATE FUNCTION plx_js_inline_handler(internal)
92+
RETURNS void
93+
AS '$libdir/plx', 'plx_js_inline_handler'
94+
LANGUAGE C;
95+
96+
CREATE TRUSTED LANGUAGE plxjs
97+
HANDLER plx_call_handler
98+
INLINE plx_js_inline_handler
99+
VALIDATOR plx_js_validator;
100+
101+
COMMENT ON LANGUAGE plxjs IS 'plx JavaScript dialect (transpiles to plpgsql)';
102+
103+
/* Python dialect */
104+
CREATE FUNCTION plx_py_validator(oid)
105+
RETURNS void
106+
AS '$libdir/plx', 'plx_py_validator'
107+
LANGUAGE C STRICT;
108+
109+
CREATE FUNCTION plx_py_inline_handler(internal)
110+
RETURNS void
111+
AS '$libdir/plx', 'plx_py_inline_handler'
112+
LANGUAGE C;
113+
114+
CREATE TRUSTED LANGUAGE plxpython3
115+
HANDLER plx_call_handler
116+
INLINE plx_py_inline_handler
117+
VALIDATOR plx_py_validator;
118+
119+
COMMENT ON LANGUAGE plxpython3 IS 'plx Python dialect (transpiles to plpgsql)';
120+
121+
/* COBOL dialect (ISO/IEC 1989:2023) validator + inline handler live in plx.so */
122+
CREATE FUNCTION plx_cob_validator(oid)
123+
RETURNS void
124+
AS '$libdir/plx', 'plx_cob_validator'
125+
LANGUAGE C STRICT;
126+
127+
CREATE FUNCTION plx_cob_inline_handler(internal)
128+
RETURNS void
129+
AS '$libdir/plx', 'plx_cob_inline_handler'
130+
LANGUAGE C;
131+
132+
CREATE TRUSTED LANGUAGE plxcobol
133+
HANDLER plx_call_handler
134+
INLINE plx_cob_inline_handler
135+
VALIDATOR plx_cob_validator;
136+
137+
COMMENT ON LANGUAGE plxcobol IS 'plx COBOL dialect (ISO/IEC 1989:2023, transpiles to plpgsql)';
138+
139+
/* PL/SQL dialect (Oracle) validator + inline handler live in plx.so */
140+
CREATE FUNCTION plx_plsql_validator(oid)
141+
RETURNS void
142+
AS '$libdir/plx', 'plx_plsql_validator'
143+
LANGUAGE C STRICT;
144+
145+
CREATE FUNCTION plx_plsql_inline_handler(internal)
146+
RETURNS void
147+
AS '$libdir/plx', 'plx_plsql_inline_handler'
148+
LANGUAGE C;
149+
150+
CREATE TRUSTED LANGUAGE plxplsql
151+
HANDLER plx_call_handler
152+
INLINE plx_plsql_inline_handler
153+
VALIDATOR plx_plsql_validator;
154+
155+
COMMENT ON LANGUAGE plxplsql IS 'plx Oracle PL/SQL dialect (transpiles to plpgsql)';
156+
157+
/* TypeScript dialect validator + inline handler live in plx.so */
158+
CREATE FUNCTION plx_ts_validator(oid)
159+
RETURNS void
160+
AS '$libdir/plx', 'plx_ts_validator'
161+
LANGUAGE C STRICT;
162+
163+
CREATE FUNCTION plx_ts_inline_handler(internal)
164+
RETURNS void
165+
AS '$libdir/plx', 'plx_ts_inline_handler'
166+
LANGUAGE C;
167+
168+
CREATE TRUSTED LANGUAGE plxts
169+
HANDLER plx_call_handler
170+
INLINE plx_ts_inline_handler
171+
VALIDATOR plx_ts_validator;
172+
173+
COMMENT ON LANGUAGE plxts IS 'plx TypeScript dialect (transpiles to plpgsql)';
174+
175+
/* T-SQL dialect (Transact-SQL) validator + inline handler live in plx.so */
176+
CREATE FUNCTION plx_tsql_validator(oid)
177+
RETURNS void
178+
AS '$libdir/plx', 'plx_tsql_validator'
179+
LANGUAGE C STRICT;
180+
181+
CREATE FUNCTION plx_tsql_inline_handler(internal)
182+
RETURNS void
183+
AS '$libdir/plx', 'plx_tsql_inline_handler'
184+
LANGUAGE C;
185+
186+
CREATE TRUSTED LANGUAGE plxtsql
187+
HANDLER plx_call_handler
188+
INLINE plx_tsql_inline_handler
189+
VALIDATOR plx_tsql_validator;
190+
191+
COMMENT ON LANGUAGE plxtsql IS 'plx Transact-SQL (SQL Server) dialect (transpiles to plpgsql)';
192+
193+
/* Go dialect validator + inline handler live in plx.so */
194+
CREATE FUNCTION plx_go_validator(oid)
195+
RETURNS void
196+
AS '$libdir/plx', 'plx_go_validator'
197+
LANGUAGE C STRICT;
198+
199+
CREATE FUNCTION plx_go_inline_handler(internal)
200+
RETURNS void
201+
AS '$libdir/plx', 'plx_go_inline_handler'
202+
LANGUAGE C;
203+
204+
CREATE TRUSTED LANGUAGE plxgo
205+
HANDLER plx_call_handler
206+
INLINE plx_go_inline_handler
207+
VALIDATOR plx_go_validator;
208+
209+
COMMENT ON LANGUAGE plxgo IS 'plx Go dialect (transpiles to plpgsql)';

plx.control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
comment = 'plx (plExcellent): dialect-pluggable procedural language over plpgsql'
2-
default_version = '1.3.0'
2+
default_version = '1.3.1'
33
module_pathname = '$libdir/plx'
44
relocatable = true

0 commit comments

Comments
 (0)