Repository navigation
[2.1.0] Docs audit: fix factual errors, stale text, broken links, and security model - #13
OffgridwithJD wants to merge 2 commits into
Conversation
Every changed behavioural claim was re-run against PostgreSQL 18 with plx 2.0.0 installed. Factual errors: - Non-boolean conditions are coerced through text at run time (0, 1, 'yes' accepted), not always an error; drop the broken `!x.nil?` advice. - RAISE detail/hint options and assert messages get no NULL fallback; a NULL there fails the RAISE or loses the message. - Recreating a plx body as plpgsql still depends on plx when it uses the string builder. - COMMIT/ROLLBACK work in procedures (all dialects but plxtsql); add a procedures section to the user guide. - README example: distinct function names, real stored plpgsql including the sentinel and base64 trailer; benchmark summary matches the table. - USERGUIDE: note where dialect forms differ (WHEN OTHERS, SQLSTATE P0001); injection guidance applies to execute(), not query/perform/fetch_one. - SECURITY DEFINER caveat on "runs with the caller's privileges". - Pasteable snippets: comment syntax, undeclared COBOL RESULT, bare found(), reserved `limit`, a finally example that never ran. - Per-dialect corrections: value-position `||`, Python `//`, plxgo range over slices, len, perform, Sprintf lone `%`, multi-assign; plxplsql condition names, RAW(n), c%NOTFOUND, run-time (not CREATE-time) failures; plxcobol `||`/`::`, COMP-1/COMP-2, MOVE OLD TO NEW, class conditions, intrinsic functions; plxtsql signature example; plxts generics; "not supported" lists split into rejected vs not translated; Ruby `and`/`or` work in value position; Python loop labels exist. - Document the known issue that an interpolated top-level initializer is hoisted into DECLARE and evaluated early. - TRANSPILER.md banner lists the design choices that were not adopted. Stale or inconsistent: - Versions and file names (CHANGELOG header, index upgrade command, plx--2.0.0.sql header, install script names in README/ARCHITECTURE). - Test commands (differentialcheck, metacheck, fuzz), CI matrix, suite list, dialect lists in DEBUGGING and BENCHMARKS, PARITY plxtsql reachability. Broken links: cookbook pages linked to themselves instead of the chapter. Gaps: security model (superuser install, PUBLIC USAGE, plx USAGE implies plpgsql, nesting limits), search_path dependency of the string builder, upgrade instructions, dump/restore under 2.0.0. Style: remove all remaining em-dashes from doc/*.md; rename the Transpiler nav entry to mark it historical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jdatcmd
left a comment
There was a problem hiding this comment.
Adversarial review. Each claim below was run on PostgreSQL 18.4 against a 2.0.0 (master) build.
Verified
- The README three-dialect example runs, and the stored body matches the listing.
- A non-boolean condition is coerced through text:
if (x)accepts 0 and 1, and 2 raisesinvalid input syntax for type boolean. - Procedures can commit in plxruby, plxpython3 and plxts.
- A NULL interpolated into
detail:makes the plxrubyraisefail withRAISE statement option cannot be null. - The plpgsql bypass: after
REVOKE USAGE ON LANGUAGE plpgsql FROM PUBLIC, an ordinary role is refusedDO LANGUAGE plpgsqlbut can runDO LANGUAGE plxplsql. - The CI matrix description matches
.github/workflows/ci.yml.
Must fix
1. Transaction control does not work in plxgo
The PR body says commit and rollback work "in every dialect except plxtsql", and doc/plxtsql.md says "The other dialects support COMMIT and ROLLBACK in a procedure". In plxgo, commit() is accepted when the procedure is created and fails when it runs:
CREATE PROCEDURE go_c() LANGUAGE plxgo AS $$ commit() $$;
CALL go_c();
-- ERROR: function commit() does not existPlease name plxgo as unsupported (or say which dialects are supported) in plxtsql.md, the USERGUIDE procedures section and plxgo.md.
2. Hardcoded dialect counts
The house rule is never to state a dialect count in prose. This PR adds two:
plx--2.0.0.sql: "registers the nine plx dialect languages"doc/COMPATIBILITY.md: "the nine dialect files"
3. A released install script is edited
The PR body says "Released install scripts are untouched", but plx--2.0.0.sql is the 2.0.0 install script that has already been released. The change is only a comment, but please either drop it or correct the PR body.
Should fix
4. bench/BENCHMARKS.md prose still disagrees with its table
The PR fixed 14 ms to 15 ms. These mismatches in the same file remain:
| prose | table |
|---|---|
| plpython3u branch 0.72x | 0.68x |
| native plruby call 2.97x | 3.03x |
| iter: embedded PLs "2.8x to 6.0x" slower | 2.69x to 5.61x |
| arith: "plpython3u is slowest" | plruby is slowest (1.79x vs 1.31x) |
| branch: "plpython3u is faster" | plphp is fastest (0.40x) |
The last row also undercuts the README's "competitive on ... branching".
5. The security section misses the search_path attack
The new text treats the unqualified plx_strbuild / plx_sb_append only as a setup requirement ("the extension's schema must be on the search_path"). It does not warn that a role can create its own plx_sb_append in a schema earlier on the search_path, and a SECURITY DEFINER function would then run it with the owner's rights. Please tell readers to pin SET search_path on such functions, with the extension's schema and pg_temp last, as the PostgreSQL manual advises for SECURITY DEFINER.
Merge order
…rch_path - plxgo has no transaction control: commit() is accepted at CREATE time and fails at CALL (function commit() does not exist). Name the dialects that support it in plxtsql.md, USERGUIDE and LIMITATIONS, list it under plxgo.md "Not supported", and qualify the PARITY claim for plxgo. - Drop hardcoded dialect counts (COMPATIBILITY "nine dialect files", PARITY "all five column dialects"). - Revert the comment edit to the released plx--2.0.0.sql install script. - bench/BENCHMARKS.md prose now matches its table (iter range, arith slowest, branch fastest, plruby call 3.03x), and the README no longer calls plpgsql competitive on branching, where plphp runs in 0.40x. - Trust and security: the unqualified plx_strbuild/plx_sb_append lookup is also an attack surface for SECURITY DEFINER functions (a planted plx_sb_append earlier on the path); advise pinning search_path with trusted schemas and pg_temp last, as the PostgreSQL manual does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. All five points are addressed in f6c3d36:
|
jdatcmd
left a comment
There was a problem hiding this comment.
Re-review of f6c3d36. Every finding from my previous review is fixed:
- plxgo transaction control: plxgo is now named as unsupported in
plxtsql.md, the USERGUIDE, LIMITATIONS,plxgo.mdand PARITY. That matches what I measured:commit()is accepted byCREATE PROCEDUREand fails atCALL. - Dialect counts: none remain in
doc/or the README. plx--2.0.0.sql: the released script is back to its original text.git diff master -- plx--2.0.0.sqlis empty.BENCHMARKS.md: the prose matches the table now. I checked each figure: iter 2.7x to 5.6x, plruby slowest on arith at 1.79x, then plpython3u at 1.31x, branch figures 0.40x, 0.68x, 0.90x and 0.95x, and call 3.03x. The README no longer claims plx is competitive on branching.search_path: the security section now describes the planted-function risk and how to pin the path.
No em-dashes in the added text. Approving.
If #17 merges, the search_path paragraphs will need updating, as the #17 description says.
Summary
A full documentation audit against the 2.0.0 code. Every SQL block in the README, USERGUIDE, MIGRATION, DEBUGGING, the nine dialect chapters and the nine cookbooks was extracted and run on PostgreSQL 18.4. Stated output and stored plpgsql were compared with the real
prosrc, and every corrected behavioural claim was tested before it was written down.Already correct: all nine cookbooks, the
examples/*.sql, the mkdocs nav, and the 1.0 → 2.0.0 upgrade path.Factual errors fixed
CREATE FUNCTION gradewith the same signature), and its listing did not match the real output. It now usesgrade_rb/grade_php/grade_js, shows the real stored body, and explains the/*plx:v1:...*/sentinel and the base64 trailer. The performance sentence now matchesbench/BENCHMARKS.md(the worst cases are 1.17x and 1.32x, not "within 11 percent").0/1/yespass silently.commit()/rollback()work inCREATE PROCEDUREin plxruby, plxphp, plxjs, plxts, plxpython3, plxcobol and plxplsql. They don't work in plxtsql (rejected) or plxgo (fails atCALL). A new USERGUIDE section covers procedures.SECURITY DEFINER.executeconcatenates.query/perform/fetch_onebind interpolated values as variables, unless the call also passes bind arguments, in which case they concatenate too.detail:/hint:makes the RAISE itself fail.--comments in Ruby, undeclared COBOLRESULT,limitas a parameter name, barefound()).||outside conditions; Python//; Go||andlen/rangeover parameters).ZERO_DIVIDE,DUP_VAL_ON_INDEX).DECLARE.IS DISTINCT FROM,IS NOT TRUE, COALESCE interpolation) and marks the chapters as authoritative. The nav entry is renamed "Transpiler design (historical)".Stale or inconsistent
1.0,1.2,plx--1.0.sql) are updated.differentialcheck,metacheckand the fuzzer.Broken links
Gaps filled
REVOKE USAGE ON LANGUAGE plpgsql.plx_strbuild/plx_sb_appendwithout a schema, so the extension's schema must be onsearch_path.ALTER EXTENSION plx UPDATE, and the fact that existing functions keep their stored plpgsql. Also, a dump restored into 2.0.0 keeps the old NULL-as-empty behaviour.Style
All 73 remaining em-dashes in
doc/are removed, following the project convention.Review follow-up
Addressed in f6c3d36:
plx--2.0.0.sqlis reverted.bench/BENCHMARKS.mdprose now matches its table, and the README no longer claims plpgsql is competitive on branching.search_pathimpostor attack onSECURITY DEFINERfunctions and recommends pinningsearch_path(trusted schemas,pg_templast). [2.1.0] Schema-qualify plx's own objects in generated code; make plx non-relocatable #17 fixes this in code for 2.1.0.Interaction with #12
That PR caps finally-in-finally nesting at 8 levels. Once it merges, the security section should mention the cap; this PR only describes the general
nested too deeplylimit. The note thatreturninsidetryskipsfinallystays true after #12.🤖 Generated with Claude Code