Releases: commandprompt/plx
Release list
plx 2.0.1
Packaging only. The extension is unchanged at 2.0.0, so there is no ALTER EXTENSION plx UPDATE for this release and nothing to install if you are already running 2.0.0. Only the distribution metadata changed, which is why the distribution version moved and the extension version did not.
Fixed
-
META.jsonlisted the tagpl/sql, which PGXN rejects, since a Tag may not contain a slash:Field /tags[11]: "pl/sql" is not a valid tag [Spec v1.0.0]It is now
plsql. No other tag was at fault.
Added
-
make metacheck(test/check_meta.py) validatesMETA.jsonagainst the PGXN Meta Specification v1, and runs in CI. Previously the only thing validating the metadata was PGXN itself, at upload time, which is after a release has been tagged and published.Worth recording, because the specification's prose and its schema disagree and the prose is the trap. The text says a Tag may contain no "slash, backslash, control, or space" characters, but that sentence describes a Term. The Tag schema is
^[^/\\\p{Cntrl}]{2,}$, which permits spaces. Sosql serverandstring builderare valid tags that would be invalid terms, and the slash was the only real fault. The validator implements the schema rather than the prose.
Verification
CI green on PostgreSQL 13 through 18, with the new metadata check running ahead of the regression suite. On 18.4: 13/13 installcheck.
plx 2.0.0
Major release for one behaviour change. Upgrade with ALTER EXTENSION plx UPDATE TO '2.0.0' after installing the new module.
Interpolating a NULL now propagates it
An interpolated value is concatenated as-is, so the whole string becomes NULL the way SQL || does. Previously every interpolated value was wrapped in COALESCE((x)::text, ''), so a missing value silently became a plausible-looking one:
-- return "#{country}-#{region}-#{code}" with a NULL region
-- before: 'US--123' a string that inserts, indexes and joins like real data
-- now: NULLThis is the wrong default for a tool whose promise is that a body becomes the plpgsql you would have written by hand. Nobody writes that COALESCE. The damage case is not a log line but a value that gets stored, where there is no error at any stage and every test that does not specifically exercise NULL passes.
Affects plxruby, plxphp, plxjs, plxts, plxpython3 and plxgo. plxplsql and plxtsql already propagated. plxcobol builds strings through the plx_strbuild accumulator, whose append treats a NULL as nothing to append by design, and is unchanged.
A message built for RAISE is the exception and keeps each interpolated value's empty-string fallback, so one NULL cannot swallow a diagnostic and the literal text of the message survives.
plxgo's fmt.Sprintf is now lowered to a SQL concatenation rather than format(), which is what allows the propagation, since format() renders a NULL operand as empty and cannot be made to do otherwise. Observable output is otherwise unchanged: verbs render their operand as text, a - flag and a width still pad, other flags and the precision field are still dropped, %% is still a literal percent.
Upgrading
The update does not change any function that already exists. plx transpiles at CREATE FUNCTION time and stores the result in pg_proc.prosrc, so a catalogued function keeps the plpgsql it was created with. The new behaviour arrives the next time that function's DDL is run, which for most installations means the next deployment.
To find the functions a redeployment will change:
SELECT p.oid::regprocedure
FROM pg_proc p JOIN pg_language l ON l.oid = p.prolang
WHERE l.lanname LIKE 'plx%'
AND EXISTS (SELECT 1
FROM regexp_split_to_table(p.prosrc, E'\n') AS ln
WHERE ln LIKE '%COALESCE((%)::text, ''''%'
AND ln NOT LIKE '%RAISE %')
ORDER BY 1;Each result interpolates a value that will become NULL when the value is NULL. Where the old rendering was wanted, make it explicit with coalesce(x, '') in the body.
Verification
CI green on PostgreSQL 13 through 18. On 18.4: clean build with no warnings, 13/13 installcheck, and the differential check at 554 matching, 17 documented, 0 unexplained. The upgrade was exercised from a real 1.3.2 install, and the claim that catalogued functions are unaffected was verified by building 1.3.2, creating a function, installing 2.0.0, and confirming the existing function still returned the old value while a recreation returned NULL.
plx 1.3.2
Code-only patch release (no catalog changes). Upgrade with ALTER EXTENSION plx UPDATE TO '1.3.2' after installing the new module.
Fixed
-
plxgo
fmt.Sprintf. In expression positionfmt.Sprintfpassed its Go format string straight into SQLformat(), which understands only%s,%I,%Land%%. Every other verb raisedunrecognized format() type specifierwhen the function was called. The transpile succeeded and the failure only appeared at run time, sofmt.Sprintf("%d", n), the ordinary way to format an integer in Go, produced a function that could not run. Go's verbs are now rewritten to the%sthatformat()understands, keeping a-flag and a width. A%that starts no directive is escaped rather than passed through, since a lone%is itself an error toformat().The verbs that change an operand's representation rather than its padding render what
%srenders, sofmt.Sprintf("%x", 255)yields255and notff. Convert explicitly where the representation matters. Recorded in the Go chapter and in the limitations page.
Added
make differentialcheck. Each case is one small program written once as a plpgsql reference and once per dialect, then called with the same arguments. The reference is the plpgsql a PostgreSQL developer would have written for the same logic, so a disagreement is a defect rather than a difference of opinion. Values are compared by text form with NULL distinguished from the empty string, and a case that raises must raise the same SQLSTATE. Intended divergences are recorded per case with a reason, and the check fails if a recorded divergence stops happening, so a documented limitation cannot outlive its documentation. This is what found theSprintfbug above.- A migration page, comparing plx against rewriting by hand, an embedded PL,
ora2pg, and leaving the logic in the application, including how to leave plx while keeping the generated plpgsql: https://commandprompt.github.io/plx/MIGRATION/
Changed
- The internal transpiler refactor behind a
PlxSurface.parse_bodyvtable, and its follow-up cleanups. No functional change; generated plpgsql is byte-identical.
Verification
CI green on PostgreSQL 13 through 18. On 18.4: clean build with no warnings, 13/13 installcheck, differentialcheck at 374 matching and 14 documented. ALTER EXTENSION plx UPDATE TO '1.3.2' from 1.3.1 applies and carries the fix, and a fresh CREATE EXTENSION plx installs 1.3.2.
plx 1.3.1
Code-only patch release (no catalog changes) carrying the memory-safety and robustness fixes from the full-repo transpiler audit (#1). Upgrade with ALTER EXTENSION plx UPDATE TO '1.3.1' after installing the new module.
Fixed
- Missing capacity guard on the trailing
T_EOFtoken write inlex()(heap overflow / SIGSEGV when a source lexed to exactlycap-1tokens). plx_strbuildsb_ensure()int32doubling could wrap negative near ~1GB and spin while passing a bogus size torepalloc; growth is now 64-bit and clamped toMaxAllocSize.- The recursion-guarded transpiler entries now call
check_stack_depth(), so deeply nested input raises a clean error (honoringmax_stack_depth) instead of crashing the backend. - The Python lexer raises a clean "indentation nested too deeply" error at the indent-stack limit instead of emitting an unbalanced
T_INDENT. - Raw single-quoted strings in Ruby and PHP keep backslashes literal (only
\\and\'are special). - PHP
${name}curly interpolation in double-quoted strings is now recognized. - Non-decimal integer literals (
0x/0o/0b, with_separators) are lexed as one token and rewritten to decimal (portable to PG13-15); a >64-bit literal raises a clean "integer literal out of range" error.
Other
- Declare the built-in dialect descriptors in
plx.h, clearing-Wmissing-variable-declarations.
Verified against PostgreSQL 18.4: clean build, all 13 installcheck tests pass, and the 1.3.0 → 1.3.1 upgrade applies cleanly.
plx 1.3.0
plx 1.3.0 makes trigger row mutation available across the dialects and ships a verified cookbook for every dialect.
Added
- plxtsql trigger row mutation. A Transact-SQL trigger can now assign to
NEWfields withSET NEW.col = e, which lowers toNEW.col := e, so a trigger can rewrite the row and not only validate it. ASETwith a qualified target and a top-level=is an assignment;SET NOCOUNT ONand other session options are still ignored. With the plxphp arrow assignment added in 1.2.2, assigning to a trigger'sNEWfields is now supported across the dialects, each in its own idiom. - Cookbooks and limitations docs. A verified, runnable cookbook for each of the nine dialects (scalar functions, loops, string building, query loops, set-returning functions, error handling, triggers, dynamic SQL, and dialect idioms), and a consolidated gaps-and-limitations page. Both are on the documentation site.
No catalog changes. All 13 regression suites pass on PostgreSQL 13 through 18, plus 19beta and 20devel built from source.
Upgrade
Install the new module (make && make install), then:
ALTER EXTENSION plx UPDATE TO '1.3.0';A fresh CREATE EXTENSION plx lands on 1.3.0 directly. The PGXN distribution zip (plx-1.3.0.zip) is attached.
- Documentation: https://commandprompt.github.io/plx/
- Full changelog: https://github.com/commandprompt/plx/blob/v1.3.0/CHANGELOG.md
plx 1.2.2
A code-only patch release.
Fixed
- plxphp: assigning to a record field with the arrow form (
$NEW->col = e), the idiomatic PHP spelling, previously raised "unsupported operator in statement". It now lowers toNEW.col := e, so a trigger function can stampNEWfields with$NEW->col = e. The array-element form ($NEW['col'] = e) continues to work.
No catalog changes. The full 13-suite regression passes on PostgreSQL 13 through 18, plus 19beta2 and 20devel built from source.
Upgrade
Install the new module (make && make install), then:
ALTER EXTENSION plx UPDATE TO '1.2.2';A fresh CREATE EXTENSION plx lands on 1.2.2 directly. The PGXN distribution zip (plx-1.2.2.zip) is attached.
See CHANGELOG.md.
plx 1.2.1
A code-only patch release: plx now builds on PostgreSQL 19 and 20.
Fixed
- Build on PostgreSQL 19 and 20 with a C23 toolchain (for example gcc 15). There, PostgreSQL defines
pg_noreturnas the standard[[noreturn]]attribute, whose placement is strict. plx wrote it after the storage class (static pg_noreturn void ...), which C23 rejects, so plx did not compile on 19/20. It is now the first token of each declaration (pg_noreturn static void ...), matching PostgreSQL's own convention, and still compiles on 13 through 18.
The full 13-suite regression passes on PostgreSQL 13 through 18, plus 19beta2 and 20devel built from source. No catalog changes: the only change is in the loadable module.
Upgrade
Install the new module (make && make install), then:
ALTER EXTENSION plx UPDATE TO '1.2.1';A fresh CREATE EXTENSION plx lands on 1.2.1 directly. The PGXN distribution zip (plx-1.2.1.zip) is attached.
See CHANGELOG.md.
plx 1.2
plx 1.2 makes it nine dialects. Write a PostgreSQL function body in the language you already know; plx transpiles it to plpgsql at CREATE FUNCTION time and the standard plpgsql interpreter runs it. No language runtime is loaded into the backend, and the generated plpgsql is visible in the catalog.
Dialects (9)
plxruby · plxphp · plxjs · plxts (TypeScript) · plxpython3 · plxgo (Go) · plxcobol · plxplsql (Oracle PL/SQL) · plxtsql (Transact-SQL / SQL Server)
New since 1.1
- Four new dialects: Oracle PL/SQL, Transact-SQL (SQL Server), Go, and TypeScript. The SQL-family and Go dialects are restructuring front ends (own tokenizers/parsers); PL/SQL and TypeScript build on the near-plpgsql and JavaScript paths. See each chapter under
doc/. - COBOL tables:
OCCURS nmaps aWORKING-STORAGEitem to a PostgreSQL array, withWS-ARR(i)subscripts andPERFORM v OVER ARRAYiteration. - plpgsql completeness audit across dialects (OUT/INOUT/RETURNS TABLE/STRICT), a worked
examples/directory, anddoc/DEBUGGING.md+ aplx_source()helper. - Whole-project hardening: a fresh-eyes review of every front end plus mutation fuzzing of all nine dialects fixed real backend crashes, hangs, and wrong/invalid-output cases. The fuzzer (
test/fuzz.py) and corpus now cover all nine dialects and are crash- and hang-clean.
Compatibility
All 13 regression suites pass on PostgreSQL 13 through 18 (CI + local matrix). plx_strbuild's in-place fast path (amortized-O(1) string append) is accelerated on PostgreSQL 18; correct but unaccelerated on 13–17.
Install
Build from source (make && make install), then CREATE EXTENSION plx;. Upgrade from an earlier install with ALTER EXTENSION plx UPDATE TO '1.2';. A PGXN distribution zip (plx-1.2.0.zip) is attached.
See CHANGELOG.md for the full list.
plx 1.1.1
plx 1.1.1 is a code-only patch release (no catalog changes). It restores
compilation on PostgreSQL 13-15 and fixes several plxcobol bugs found in review.
Upgrade an existing install with:
ALTER EXTENSION plx UPDATE TO '1.1.1';Fixed
- Compilation on PostgreSQL 13, 14, and 15.
plx_strbuild.cincluded
varatt.hunconditionally, but that header was only split out ofpostgres.h
in PostgreSQL 16, so 1.1 did not build on 13-15. The full 9-suite regression
now passes on PostgreSQL 13 through 18 (verified in CI). - plxcobol crash (out-of-bounds read) on a body truncated at
PERFORM VARYING ... UNTIL; it now errors cleanly. - plxcobol
ADD a b GIVING cand other multi-addendADD/SUBTRACTforms
were rejected; they are parsed as operand lists now. - plxcobol multi-argument function calls (
mod(a, b)) were broken because
the tokenizer stripped commas everywhere; commas inside parentheses are kept. - plxcobol
GREATER/LESS ... OR EQUALat the end of a condition, aPICTURE
repeat-count overflow, and unterminated string handling.
Added
- Continuous integration (GitHub Actions) running the full suite on PostgreSQL
13 through 18 (green). - plxcobol coverage in the fuzzer and corpus runner.
- A PGXN
META.json, and theplx-1.1.1.zipdistribution attached to this
release.
Compatibility
Builds and passes the full regression suite on PostgreSQL 13, 14, 15, 16, 17,
and 18.
plx 1.1
plx 1.1 adds plxcobol, a COBOL dialect (ISO/IEC 1989:2023, COBOL 2023, free
format), at full plpgsql construct parity. As with the other dialects, a COBOL
function body is transpiled to plpgsql at CREATE FUNCTION time and runs on the
standard plpgsql interpreter, with no separate language runtime in the backend.
plxcobol
COBOL is verb-driven and free-format, so it has its own front end (tokenizer and
recursive-descent parser). Data names map to plpgsql identifiers (lower-cased,
hyphens to underscores). Coverage:
WORKING-STORAGEdeclarations, withPICTUREmapped to SQL types, plusTYPE
(for%ROWTYPE,RECORD,refcursor) andCONSTANT AS.MOVE/COMPUTEand theADD/SUBTRACT/MULTIPLY/DIVIDEverbs;
**is exponent and%is modulo.IF/ELSE/END-IF;EVALUATE(simple and searchedEVALUATE TRUE).PERFORMin theUNTIL,VARYING,TIMES, inline, query (OVER "sql") and
array (OVER ARRAY) forms;EXIT PERFORM [CYCLE].GOBACK RETURNING,RETURN-NEXT,RETURN-QUERY;EXECUTEwithUSING/INTO;
cursors (OPEN-CURSOR/FETCH-CURSOR/MOVE-CURSOR/CLOSE-CURSOR).- Exception handling (
BEGIN-TRY/WHEN/END-TRY) with stacked diagnostics via
GET;GET ROW-COUNT;FOUND. STRING-APPEND <expr> TO <var>, which lowers to theplx_strbuildstring
builder (amortized-O(1) in-loop string building).RAISE,DISPLAY,ASSERT,CALL,COMMIT/ROLLBACK.
See doc/plxcobol.md
and the parity matrix in
doc/PARITY.md.
Performance
plxcobol transpiles to plpgsql like the other dialects, so it matches plpgsql on
the arithmetic, iteration, string-building, and call workloads; see
bench/BENCHMARKS.md.
Upgrading
Existing 1.0 installations upgrade in place:
ALTER EXTENSION plx UPDATE TO '1.1';A fresh CREATE EXTENSION plx installs 1.1 directly.
Compatibility
Tested against PostgreSQL 13 through 18; the full regression suite (nine suites,
including plxcobol) passes on each.