Skip to content

hstore transform, structured pg_raise, TRUNCATE/INSTEAD OF triggers, domain array/composite support#29

Merged
jdatcmd merged 1 commit into
masterfrom
plphp-parity-features
Jul 6, 2026
Merged

hstore transform, structured pg_raise, TRUNCATE/INSTEAD OF triggers, domain array/composite support#29
jdatcmd merged 1 commit into
masterfrom
plphp-parity-features

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Closes four PL/Ruby-parity gaps in PL/php. Every change was built and run against PostgreSQL 18 / PHP 8.3 in a container; the full suite is green (25 plphp tests, plus jsonb_plphp and hstore_plphp).

Features

1. hstore transform (hstore_plphp)

New sibling to jsonb_plphp. CREATE EXTENSION hstore_plphp adds TRANSFORM FOR TYPE hstore, mapping an hstore to a PHP associative array of string keys → string-or-null values and back (a PHP null value → hstore NULL). Converts through the hstore extension's own hstore_to_array/hstore(text[]) (OIDs resolved via pg_transform), so it never touches hstore internals. New regression suite.

2. Structured pg_raise

pg_raise(level, message [, detail [, hint [, sqlstate]]]) — attaches DETAIL, HINT, and (for error) a validated custom SQLSTATE, the equivalent of PL/pgSQL's RAISE ... USING. Fields are readable on a caught PgError, survive nested SPI propagation, and now appear on top-level uncaught errors too. That last part required a new field-stash bridge through plphp's zend_bailout reporting path, which also fixes a latent bug: uncaught database/pg_raise errors previously collapsed to SQLSTATE XX000 with no DETAIL/HINT.

3. TRUNCATE and INSTEAD OF triggers

The trigger handler used to elog(ERROR) on any event that wasn't INSERT/UPDATE/DELETE or any timing that wasn't BEFORE/AFTER — so TRUNCATE and INSTEAD OF triggers were unusable. Now $_TD['event'] can be 'TRUNCATE' and $_TD['when'] can be 'INSTEAD OF', and INSTEAD OF return values drive the row. WHEN(...) was already PostgreSQL-evaluated. New trigger2 regression test.

4. Domains over arrays and composites

Type classification now looks through domains via getBaseType, so a domain over an array arrives as (and can be returned as) a PHP array, and a domain over a composite as an associative array. The declared type's input function still runs, so domain CHECK constraints are enforced on results. Scalar domains were already transparent. New domains regression test.

Deferred

Applying transforms inside trigger/SETOF/composite/SPI rows (as PL/Ruby does) is intentionally not included: plphp currently applies transforms only to top-level scalar args/returns — jsonb_plphp has the same limitation — and extending it is a broad change to the text-based tuple I/O in both directions. Left as separate future work.

Testing

Built stock + patched against PG 18 / PHP 8.3; ran make installcheck for plphp, jsonb_plphp, and hstore_plphp. All expected-output files were generated from real runs. No existing expected output changed except the intentional pgerror additions.

🤖 Generated with Claude Code

…rs, domain array/composite support

Four PL/Ruby-parity features, each verified against PostgreSQL 18 with the
full regression suite (25 plphp tests + jsonb_plphp + hstore_plphp, all green):

- hstore_plphp: a TRANSFORM FOR TYPE hstore mapping hstore <-> PHP associative
  arrays (PHP null -> hstore NULL), companion to jsonb_plphp.

- pg_raise(level, message [, detail [, hint [, sqlstate]]]): attaches DETAIL,
  HINT and a custom SQLSTATE like RAISE ... USING. Fields are preserved on a
  caught PgError, through nested SPI propagation, and now at top-level uncaught
  errors via a new field-stash bridge through the zend_bailout path -- which
  also fixes uncaught errors previously collapsing to SQLSTATE XX000 and
  losing DETAIL/HINT.

- TRUNCATE (statement) and INSTEAD OF (view) triggers: previously the trigger
  handler errored on the unrecognized event/timing; $_TD now reports
  'TRUNCATE' / 'INSTEAD OF', and INSTEAD OF returns drive the row.

- Domains over array/composite: classified through getBaseType so an array
  domain arrives as (and returns) a PHP array; CHECK constraints still enforced.

Item deferred: applying transforms inside trigger/SETOF/composite/SPI rows
(plphp applies transforms only to top-level scalars today, as does jsonb) is a
broad tuple-I/O change left as separate future work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jdatcmd jdatcmd merged commit c46c17a into master Jul 6, 2026
12 checks passed
@jdatcmd jdatcmd deleted the plphp-parity-features branch July 6, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants