Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 32 additions & 32 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ All notable changes to PL/php are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and the project aims to follow [Semantic Versioning](https://semver.org/).

## [Unreleased]
## [2.4.0] - 2026-07-06

### Fixed

- **Per-function memory contexts.** Each compiled function's descriptor and
subsidiary data (fmgr info records included) now live in their own memory
context, deleted wholesale on redefinition implementing a FIXME as old
context, deleted wholesale on redefinition, implementing a FIXME as old
as the file. This also closes a use-after-free window: the compiled-
function cache used to briefly point at freed memory during recompilation,
which an unluckily timed statement cancel could have hit.
Expand Down Expand Up @@ -39,7 +39,7 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
fields of composite arguments, and composites inside arrays now arrive as
associative PHP arrays (recursively, arrays included), and PHP arrays
convert back to composite values on return, in `return_next` rows, and
through trigger `MODIFY` completing the structural-conversion work begun
through trigger `MODIFY`, completing the structural-conversion work begun
with array columns in 2.3.0.
- `anycompatible` and `anycompatiblearray` are accepted as polymorphic
argument/return types on PostgreSQL 13 and newer.
Expand All @@ -55,16 +55,16 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
(`doc/benchmarks.md`) comparing PL/php with PL/pgSQL and PL/Perl: within a
few percent on scalar and string work, 1.75× PL/Perl on SPI row loops.

## [2.3.0] 2026-07-06
## [2.3.0] - 2026-07-06

### Added

- **Continuous integration** a GitHub Actions matrix building and running
- **Continuous integration**: a GitHub Actions matrix building and running
the full regression suite (core + `jsonb_plphp`) on PostgreSQL 11-18 and
PHP 8.1/8.2/8.3/8.4 for every pull request.
- **VARIADIC parameters.** A variadic function's collected arguments arrive
as a single PHP array, matching every other PL (`VARIADIC "any"` remains
unsupported and is rejected with a clear error). Previously any VARIADIC
unsupported and is rejected with an error). Previously any VARIADIC
declaration failed.

### Changed
Expand All @@ -86,7 +86,7 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
several are installed (see INSTALL for a packaging caveat about shared
SONAMEs).

## [2.2.0] 2026-07-05
## [2.2.0] - 2026-07-05

### Added

Expand All @@ -99,24 +99,24 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
errors inside `subtransaction()` callbacks are now catchable too.
- **Whole-array set-returning functions.** An SRF may return the entire result
set as one array with one element per row (PL/Perl's "return a reference to
an array" form), instead of or in addition to calling `return_next`.
an array" form), instead of or in addition to calling `return_next`.
This had been PL/php 1.x behavior that 2.0 silently broke (such functions
returned zero rows).
- **`spi_each(query, callable)`** invoke a callback once per row, streaming
- **`spi_each(query, callable)`**: invoke a callback once per row, streaming
over a cursor; returning `false` stops early. The inline-loop equivalent of
PL/Tcl's `spi_exec -array a $query { body }`.
- **`plphp.on_init`** a snippet of PHP source executed when the interpreter
- **`plphp.on_init`**: a snippet of PHP source executed when the interpreter
is first initialized in a session, before modules and `plphp.start_proc`;
the counterpart of `plperl.on_init`.
- **`jsonb_plphp` transform extension** with `TRANSFORM FOR TYPE jsonb`,
- **`jsonb_plphp` transform extension**: with `TRANSFORM FOR TYPE jsonb`,
jsonb arguments arrive as native PHP values (arrays/int/float/bool/null)
and PHP values convert straight back to jsonb, like `jsonb_plperl` (which
PL/Tcl has no equivalent of). PL/php core gained the `TRANSFORM FOR TYPE`
protocol support this builds on.

- **A tested cookbook** (`doc/cookbook.md`): practical recipes — `filter_var`
- **A tested cookbook** (`doc/cookbook.md`): `filter_var`
CHECK constraints, bcrypt passwords, HMAC tokens, recursive JSON reshaping,
regex set-returning functions, a generic JSON-diff audit trigger, batch
regex set-returning functions, a JSON-diff audit trigger, batch
processing with periodic commits, streaming scans that stop early, CSV and
XML shredding, and zlib compression. Every recipe in the "Tested" section
runs in the new `cookbook` regression test.
Expand All @@ -131,7 +131,7 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
- **INOUT parameters in procedures work.** `CALL` on a PL/php procedure with
INOUT parameters used to fail ("function returning record called in
context that cannot accept type record"): a procedure's result is always a
record even with a single INOUT parameter which broke both the
record (even with a single INOUT parameter), which broke both the
single-OUT scalar-return shortcut and the record descriptor lookup (which
needed a `ReturnSetInfo` that `CALL` never supplies; it is now derived
from the parameter declarations via `get_call_result_type`). The usual
Expand All @@ -151,11 +151,11 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
of the old "value starts with `{`" heuristic, so a `text` argument whose
value happens to start with a brace is no longer misparsed as an array.

## [2.1.0] 2026-07-05
## [2.1.0] - 2026-07-05

### Added

- **Cursor-streaming SPI** `spi_query(query)` opens a cursor and returns its
- **Cursor-streaming SPI**: `spi_query(query)` opens a cursor and returns its
name, `spi_fetchrow(cursor)` fetches one row at a time (returning `false`
and closing the cursor at exhaustion), and `spi_cursor_close(cursor)`
abandons a cursor early. Large result sets can now be scanned in constant
Expand All @@ -170,25 +170,25 @@ and the project aims to follow [Semantic Versioning](https://semver.org/).
result resource); code that relied on the alias should call
`spi_exec_prepared` instead.

## [2.0.0] 2026-07-01
## [2.0.0] - 2026-07-01

A ground-up modernization of PL/php (the previous release, 1.4, dates from 2010)
for current software. Tested on **PostgreSQL 11 through 18** with **PHP 8.3**
(embed SAPI, non-thread-safe).

### Added

- **Anonymous `DO` blocks** `DO $$ ... $$ LANGUAGE plphp`, via an inline
- **Anonymous `DO` blocks**: `DO $$ ... $$ LANGUAGE plphp`, via an inline
handler.
- **Event trigger functions** `RETURNS event_trigger`, with `$_TD['event']`
- **Event trigger functions**: `RETURNS event_trigger`, with `$_TD['event']`
and `$_TD['tag']`.
- **Prepared statements** `spi_prepare`, `spi_exec_prepared`,
- **Prepared statements**: `spi_prepare`, `spi_exec_prepared`,
`spi_query_prepared`, and `spi_freeplan`.
- **Transaction control** in procedures `spi_commit` and `spi_rollback`.
- **Explicit subtransactions** `subtransaction(callable, ...)`.
- **Quoting helpers** `quote_literal`, `quote_nullable`, `quote_ident`.
- **Transaction control** in procedures: `spi_commit` and `spi_rollback`.
- **Explicit subtransactions**: `subtransaction(callable, ...)`.
- **Quoting helpers**: `quote_literal`, `quote_nullable`, `quote_ident`.
- **`elog(level, message)`** supporting `DEBUG`/`LOG`/`INFO`/`NOTICE`/`WARNING`/`ERROR`.
- **Session initialization** module autoloading from a `plphp_modules` table
- **Session initialization**: module autoloading from a `plphp_modules` table
and a `plphp.start_proc` configuration setting.
- Packaging as a first-class extension (`CREATE EXTENSION plphp`) and a
regression test for every new feature.
Expand Down Expand Up @@ -228,21 +228,21 @@ for current software. Tested on **PostgreSQL 11 through 18** with **PHP 8.3**
the `TRUSTED` attribute; only superusers may install the extension or create
PL/php functions.

## [1.4] 2010-07-12
## [1.4] - 2010-07-12

### Added

- Support for PostgreSQL 8.4 and 9.0.
- Support for PHP 5.3.

## [1.3.5-beta1] 2007-10-15
## [1.3.5-beta1] - 2007-10-15

### Added

- Support for parameter names.
- Support for PostgreSQL 8.3.

## [1.3.3] 2007-03-29
## [1.3.3] - 2007-03-29

### Added

Expand All @@ -258,31 +258,31 @@ for current software. Tested on **PostgreSQL 11 through 18** with **PHP 8.3**
- Several memory leaks.
- Bugs in argument handling.

## [1.3.2] 2007-03-01
## [1.3.2] - 2007-03-01

### Changed

- Link against the PHP embed SAPI instead of Apache's `mod_php`, making the
build far more robust against internal PHP changes.
build robust against internal PHP changes.

### Added

- `configure` support for detecting required utilities and libraries.

## [1.3.1] 2006-12-01
## [1.3.1] - 2006-12-01

### Changed

- Minor Makefile cleanups.

## [1.2] 2005-12-13
## [1.2] - 2005-12-13

### Added

- Set-returning functions.
- Support for PostgreSQL 8.0.

## [1.1] 2005-12-05
## [1.1] - 2005-12-05

Supports PostgreSQL 8.0 and 8.1.

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ MODULE_big = plphp
OBJS = plphp.o plphp_io.o plphp_spi.o

EXTENSION = plphp
DATA = plphp--2.3.sql plphp--2.0--2.1.sql plphp--2.1--2.2.sql plphp--2.2--2.3.sql
DATA = plphp--2.4.sql plphp--2.0--2.1.sql plphp--2.1--2.2.sql plphp--2.2--2.3.sql plphp--2.3--2.4.sql

# PHP embed SAPI compile/link flags, discovered via php-config.
PHP_CONFIG ?= php-config
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

PL/php is a procedural-language handler that lets you write database functions in
**PHP**, stored and executed inside PostgreSQL. You get the convenience of PHP's
standard library with the full power of a native PostgreSQL function plain
standard library as a native PostgreSQL function: plain
functions, set-returning functions, triggers, event triggers, and procedures
with transaction control.

Expand Down Expand Up @@ -123,7 +123,7 @@ regression suite with `make installcheck`.
> [!WARNING]
> **PL/php is an untrusted language.** On modern PHP there is no sandbox
> (`safe_mode` was removed in PHP 5.4), so a PL/php function can do anything the
> PostgreSQL server's operating-system user can read and write files, open
> PostgreSQL server's operating-system user can: read and write files, open
> network connections, run shell commands, and so on.

The language is created **without** the `TRUSTED` attribute, so only superusers
Expand All @@ -133,8 +133,8 @@ roles you would trust with the server's OS account.
## Documentation

- [Language reference](doc/plphp.md)
- [Cookbook tested recipes](doc/cookbook.md)
- [Benchmarks](doc/benchmarks.md)within a few percent of PL/pgSQL on scalar work; 1.75× PL/Perl on row loops
- [Cookbook: tested recipes](doc/cookbook.md)
- [Benchmarks](doc/benchmarks.md): within a few percent of PL/pgSQL on scalar work; 1.75x PL/Perl on row loops
- [Installation](INSTALL)
- [Changelog](CHANGELOG.md)
- [PL/php vs PL/Perl](doc/plperl-comparison.md)
Expand All @@ -143,4 +143,4 @@ roles you would trust with the server's OS account.
## License

PL/php is copyright © Command Prompt, Inc. and is distributed under a permissive,
PostgreSQL-style license see the notice at the top of each source file.
PostgreSQL-style license; see the notice at the top of each source file.
6 changes: 3 additions & 3 deletions doc/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ warm session, Ubuntu 24.04 container on x86-64. Higher is better; treat
- **Row iteration is PL/pgSQL's home turf.** Its `FOR ... IN SELECT` loop
iterates natively without crossing a language boundary per row. PL/php
pays a C-to-PHP conversion per row (one output-function call and one zval
per column) and is still **1.75× faster than PL/Perl**, which
per column) and is still **1.75x faster than PL/Perl**, which
materializes the entire result into Perl structures up front.
- **Repeated SPI statements** carry a per-call subtransaction in both PL/php
and PL/Perl (that is what makes their errors catchable); PL/pgSQL's
Expand All @@ -43,12 +43,12 @@ per row) measured as a no-op: the row-loop cost lives in per-cell value
conversion, not key handling. The optimization was dropped rather than
carried as complexity without benefit. A future fast path worth exploring is
converting common scalar types (int/float/bool/text) from their binary Datum
form instead of through the type output functions that requires threading
form instead of through the type output functions; that requires threading
type metadata into `spi_fetch_row`'s result handling.

## Guidance

- For pure computation, use whichever language reads best the overhead
- For pure computation, use whichever language reads best; the overhead
differences are negligible.
- For tight loops over large results, prefer a set-based SQL statement (or
PL/pgSQL) when the logic allows; when you need PHP's expressiveness per
Expand Down
18 changes: 9 additions & 9 deletions doc/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Practical, self-contained recipes showing where PL/php earns its keep over
`plpgsql`: PHP's standard library. Everything in the first section is run by
the `cookbook` regression test on every supported PostgreSQL version if it's
the `cookbook` regression test on every supported PostgreSQL version: if it's
on this page, it works.

The embedded interpreter ships with `json`, `pcre`, `openssl`, `sodium`,
Expand Down Expand Up @@ -54,7 +54,7 @@ is available too if libargon2 was compiled in.

### HMAC-signed tokens

Issue and verify tamper-proof tokens with a server-side secret no extension
Issue and verify tamper-proof tokens with a server-side secret, no extension
needed:

```sql
Expand All @@ -80,7 +80,7 @@ otherwise, and uses constant-time `hash_equals`.

### Reshape JSON recursively

Deeply redact keys anywhere in a document — awkward with `jsonb` operators,
Deeply redact keys anywhere in a document. Awkward with `jsonb` operators,
three lines of PHP:

```sql
Expand Down Expand Up @@ -185,7 +185,7 @@ CALL process_queue(1000);

### Stream a big scan, stop early

A cursor (`spi_query`) reads rows one at a time in constant memory — close it
A cursor (`spi_query`) reads rows one at a time in constant memory. Close it
as soon as you have your answer instead of materializing millions of rows:

```sql
Expand All @@ -206,7 +206,7 @@ $$;

### Read a CSV file into rows

Server-side file ETL without `COPY`'s format restrictions `fgetcsv` handles
Server-side file ETL without `COPY`'s format restrictions: `fgetcsv` handles
quoting/escaping, and `RETURNS TABLE` + `return_next(array)` does the rest:

```sql
Expand Down Expand Up @@ -257,10 +257,10 @@ SELECT gunz(gz(long_text)) = long_text FROM documents; -- true
```

(PostgreSQL TOAST already compresses transparently; this is for when you want
the compressed bytes explicitlyto hand to a client, or with `SET STORAGE
the compressed bytes explicitly, to hand to a client or use with `SET STORAGE
EXTERNAL`.)

## Doc-only recipes (side effects use with care)
## Doc-only recipes (side effects: use with care)

These work but are deliberately not in the regression test: they touch the
outside world, and they run **inside your transaction on the backend**. A slow
Expand All @@ -270,7 +270,7 @@ write to a queue table instead and let a worker drain it.

### HTTP webhook from a trigger

No curl needed PHP's HTTP stream wrapper does POSTs, and a short timeout is
No curl needed: PHP's HTTP stream wrapper does POSTs, and a short timeout is
mandatory:

```sql
Expand Down Expand Up @@ -300,7 +300,7 @@ Requires a working `sendmail` on the database server.
### Slugify with transliteration

`iconv`'s `//TRANSLIT` is locale-dependent, so results can vary between
systemsgood for display, not for keys you need to be stable everywhere:
systems: good for display, not for keys that must be stable everywhere:

```sql
CREATE FUNCTION slugify(text) RETURNS text LANGUAGE plphp STRICT AS $$
Expand Down
12 changes: 6 additions & 6 deletions doc/plperl-comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ intentionally out of scope, with the rationale given.
| `spi_cursor_close` | `spi_cursor_close` **added** | Abandon a cursor early |
| errors trappable with `eval` | `try`/`catch (PgError $e)` **added** | PgError carries SQLSTATE, detail, and hint (richer than `$@`) |
| SRF returns array reference | return an array from the SRF **added** | One element per row, as an alternative to `return_next` |
| | `spi_each(query, callable)` **added** | Streaming per-row callback; PL/Perl has no equivalent |
| (no equivalent) | `spi_each(query, callable)` **added** | Streaming per-row callback; PL/Perl has no equivalent |
| `elog(level, msg)` | `elog` **added** | DEBUG/LOG/INFO/NOTICE/WARNING/ERROR; `pg_raise` remains as the older spelling |
| `spi_prepare` | `spi_prepare` **added** | Type names given as SQL type strings |
| `spi_exec_prepared` | `spi_exec_prepared` **added** | |
Expand All @@ -42,24 +42,24 @@ intentionally out of scope, with the rationale given.
| `quote_nullable` | `quote_nullable` **added** | |
| `quote_ident` | `quote_ident` **added** | |
| `spi_commit` / `spi_rollback` | `spi_commit` / `spi_rollback` **added** | Transaction control in a procedure invoked by `CALL` (non-atomic) |
| `looks_like_number` | | Use PHP's built-in `is_numeric()` |
| `encode_bytea` / `decode_bytea` | | Use PHP's `bin2hex` / `hex2bin`, `base64_encode`, etc. |
| `encode_array_literal` / `encode_typed_literal` | | Marginal; build literals with the quoting helpers |
| `looks_like_number` | (none) | Use PHP's built-in `is_numeric()` |
| `encode_bytea` / `decode_bytea` | (none) | Use PHP's `bin2hex` / `hex2bin`, `base64_encode`, etc. |
| `encode_array_literal` / `encode_typed_literal` | (none) | Marginal; build literals with the quoting helpers |

## Interpreter configuration

| PL/Perl | PL/php | Notes |
|-----------------------|--------|-------|
| `plperl.on_init` | `plphp.on_init` **added** | PHP source run at interpreter initialization |
| (start proc, PL/Tcl-style) | `plphp.start_proc` | Runs a named PL/php function once per session |
| `plperl.use_strict` | | No PHP equivalent; PHP is always "strict" about undefined functions |
| `plperl.use_strict` | (none) | No PHP equivalent; PHP is always "strict" about undefined functions |

## Transforms

| PL/Perl | PL/php | Notes |
|-----------------------|--------|-------|
| `jsonb_plperl` | `jsonb_plphp` **added** | `TRANSFORM FOR TYPE jsonb`: native values in both directions |
| `bool_plperl` | | PL/php already maps `t`/`f`; a transform would add little |
| `bool_plperl` | (none) | PL/php already maps `t`/`f`; a transform would add little |

## Intentionally not implemented

Expand Down
Loading
Loading