Skip to content

Commit 9b63d0f

Browse files
plxclaude
andcommitted
Release 1.3.2: plxgo fmt.Sprintf fix (code-only patch)
Code-only patch (no catalog changes) carrying the plxgo fix for fmt.Sprintf emitting a format string SQL format() rejects, plus the differential check that found it. In expression position fmt.Sprintf passed its Go format string straight into SQL format(), which understands only %s, %I, %L and %%. Every other verb raised "unrecognized format() type specifier" when the function was called, so fmt.Sprintf("%d", n), the ordinary way to format an integer in Go, transpiled cleanly and then failed at run time. Go's verbs now become the %s that format() understands, and a % that starts no directive is escaped rather than passed through. Bumps default_version and META.json to 1.3.2, adds plx--1.3.2.sql (identical to 1.3.1) and the plx--1.3.1--1.3.2.sql version-advancing upgrade script. Verified against PG18.4: clean build with no warnings, all 13 installcheck tests pass, differentialcheck clean at 374 matching and 14 documented, the 1.3.1 -> 1.3.2 ALTER EXTENSION UPDATE applies and carries the fix, and a fresh CREATE EXTENSION installs 1.3.2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d27cc9e commit 9b63d0f

6 files changed

Lines changed: 238 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ 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-
## [Unreleased]
7+
## [1.3.2] - 2026-08-24
8+
9+
Code-only patch release (no catalog changes) carrying a plxgo fix for
10+
`fmt.Sprintf` and the differential check that found it. Upgrade with
11+
`ALTER EXTENSION plx UPDATE TO '1.3.2'` after installing the new module.
812

913
### Fixed
1014

@@ -14,9 +18,15 @@ version in `plx.control` (currently `1.0`).
1418
transpile succeeded and the failure only appeared at run time, which made
1519
`fmt.Sprintf("%d", n)`, the ordinary way to format an integer in Go, produce
1620
a function that could not run. Go's verbs are now rewritten to the `%s` that
17-
`format()` understands, keeping a `-` flag and a width and dropping the
18-
flags and precision `format()` has no equivalent for. Found by the new
19-
differential check.
21+
`format()` understands, keeping a `-` flag and a width and dropping the flags
22+
and precision `format()` has no equivalent for. A `%` that starts no
23+
directive is escaped rather than passed through, since a lone `%` is itself
24+
an error to `format()`. Found by the new differential check.
25+
26+
Note that the verbs which change an operand's representation rather than its
27+
padding render what `%s` renders, so `fmt.Sprintf("%x", 255)` yields `255`
28+
and not `ff`. Convert explicitly where the representation matters. This is
29+
recorded in `doc/plxgo.md` and `doc/LIMITATIONS.md`.
2030

2131
### Added
2232

@@ -26,6 +36,9 @@ version in `plx.control` (currently `1.0`).
2636
SQLSTATEs. Intended divergences are recorded per case with a reason and
2737
reported separately, and the check fails if a recorded divergence stops
2838
happening, so a documented limitation cannot outlive its documentation.
39+
- `doc/MIGRATION.md`, a migration-led page comparing plx against rewriting by
40+
hand, an embedded PL, `ora2pg`, and leaving the logic in the application,
41+
including how to leave plx while keeping the generated plpgsql.
2942

3043
### Changed
3144

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.1",
5+
"version": "1.3.2",
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.1.sql",
13+
"file": "plx--1.3.2.sql",
1414
"docfile": "README.md",
15-
"version": "1.3.1"
15+
"version": "1.3.2"
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 src/plx_parse_brace.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.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
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.3.2.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 plx--1.3.1--1.3.2.sql
77

88
# pg_regress suite (make installcheck). test/run_corpus.py is an additional
99
# Ruby corpus runner, and test/differential.py checks each dialect against a

plx--1.3.1--1.3.2.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* plx 1.3.1 -> 1.3.2: no catalog changes.
2+
*
3+
* 1.3.2 is a code-only patch: a plxgo fix in the shared transpiler (loadable
4+
* module $libdir/plx) for fmt.Sprintf emitting a format string SQL format()
5+
* rejects. No SQL objects the extension defines change, so this update script
6+
* only advances the recorded version. Installing the matching $libdir/plx is
7+
* what delivers the fix. */

plx--1.3.2.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.1'
2+
default_version = '1.3.2'
33
module_pathname = '$libdir/plx'
44
relocatable = true

0 commit comments

Comments
 (0)