Skip to content

feat: expand PDO parity across maintained PHP drivers#559

Open
Guikingone wants to merge 1 commit into
mainfrom
worktree-moonlit-finding-pelican
Open

feat: expand PDO parity across maintained PHP drivers#559
Guikingone wants to merge 1 commit into
mainfrom
worktree-moonlit-finding-pelican

Conversation

@Guikingone

@Guikingone Guikingone commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR takes the PDO compatibility campaign from the original SQLite/PostgreSQL/MySQL bridge to the complete eleven-driver matrix tracked for supported PHP releases. It also fixes the compiler/runtime defects exposed by the expanded PDO test corpus, adds version-aware API surfaces for PHP 8.0 through 8.6, and introduces build plus live-database CI for the new profiles.

The normative compatibility scope is PHP 8.2 through 8.5. PHP 8.0 and 8.1 remain historical compatibility targets, while PHP 8.6 is treated as a preview surface until upstream stabilizes.

PDO core and versioned surface

  • Adds --php-version and ELEPHC_PHP_VERSION, with CLI precedence and explicit 8.0 through 8.6 selection.
  • Expands the generated PDO prelude with version-gated constants, aliases, deprecations, driver subclasses, methods, signatures, and attributes.
  • Adds PDO::connect() and the driver-specific subclass construction path.
  • Completes PDO/PDOStatement behavior around binds, fetch modes, grouped and unique fetches, metadata, LOB streams, iteration, rowsets, error modes, driver errors, SQLSTATE descriptions, native error codes, statement lifetime, and persistent connection validation.
  • Adds a central compiled-driver registry so PDO::getAvailableDrivers() and pdo_drivers() only report drivers present in the selected bridge archive.
  • Implements pdo.dsn.* aliases and PHP-compatible INI/environment precedence, including uri: DSNs and credential precedence.
  • Keeps source evaluation, callbacks, statement subclass construction, and PHP-visible error propagation on the compiled-PHP side through dedicated runtime adapters.

Driver matrix

Default bridge:

  • SQLite: native SQLite behavior, metadata, incremental BLOB streams, extension loading, and scalar/aggregate/collation callbacks.
  • PostgreSQL: pure-Rust protocol path, TLS, notices, notifications, COPY, large objects, unbuffered rows, and driver methods.
  • MySQL/MariaDB: native and emulated prepares, buffered/unbuffered results, local-infile controls, TLS/client options, compression, multi-statements, init command, and warning count.

Optional profiles:

  • pdo-libpq-gss: full libpq connection lifetime for PHP-style GSSAPI/Kerberos, service/pass files, PG* defaults, encrypted-key options, and libpq-version-dependent keywords.
  • pdo-dblib: FreeTDS DB-Library with Pdo\Dblib, versioned aliases, attributes, and live SQL Server acceptance.
  • pdo-firebird: Firebird wire client with Pdo\Firebird, versioned aliases, transactions, attributes, metadata, and live acceptance.
  • pdo-odbc: unixODBC/iODBC driver-manager path with Pdo\Odbc, attributes, diagnostics, metadata, and live PostgreSQL-over-ODBC acceptance.
  • pdo-informix: Informix CLI/ODBC profile with the PECL surface and explicit external Client SDK boundary.
  • pdo-ibm: IBM CLI/ODBC profile with Pdo\Ibm, PHP-versioned aliases, attributes, and explicit external driver boundary.
  • pdo-sqlsrv: Microsoft PDO_SQLSRV 5.13 profile through ODBC Driver 17/18, including its version availability and complete attribute range.
  • pdo-oci: PDO_OCI through Oracle Instant Client/ODPI-C, including the PHP 8.3 bundled-to-PECL transition.
  • pdo-cubrid: official CUBRID CCI path, including attributes, schema/metadata, scrolling, rowsets, LOBs, quoting, and the extension-specific surface.

Compiler and runtime fixes uncovered by PDO

  • Fixes PHP by-value array behavior with private shadow slots and balanced ownership.
  • Adds representation canonicalization across loops, conditionals, switch, try/catch, match, ternary, and short-circuit control flow.
  • Handles both indexed-array widening and indexed-array-to-hash transitions without static/runtime representation divergence.
  • Adds kind-aware and mixed-key array reads/writes, nested append lowering, property vivification/boxing, strict deep array comparison, and related type narrowing.
  • Fixes x86_64 mixed-key hash reads and Linux process-wide termination so bridge worker threads cannot keep a completed program alive.
  • Adds PDO callback runtime adapters and the EIR/runtime feature wiring needed to emit them only when reachable.
  • Preserves supported-target ABI, ownership, frame allocation, and linker behavior across macOS AArch64, Linux AArch64, and Linux x86_64.

CI and examples

  • Adds optional-profile builds for DBLIB, Firebird, ODBC, Informix, IBM, SQLSRV, OCI, and CUBRID to every supported target build job.
  • Adds the PDO Live Databases workflow for PostgreSQL 16, MySQL 8.4, SQL Server 2022, Firebird 5, ODBC, Oracle 23ai, CUBRID 11.4, and the libpq GSS/Kerberos fixture.
  • Adds an ephemeral MIT Kerberos realm test that proves both successful GSS authentication/encryption and fail-closed behavior without credentials.
  • Runs compiled-surface and bridge tests for Informix and IBM while keeping their proprietary SDK/server dependency explicit.
  • Adds or updates examples for every driver profile.

Documentation

  • Rewrites the PDO guide around the complete driver/version matrix, connection semantics, driver options, TLS/GSS, validation boundaries, and known divergences.
  • Documents --php-version and ELEPHC_PHP_VERSION in the CLI reference.
  • Synchronizes the architecture, EIR, memory-model, and runtime pages with the active EIR backend and src/codegen_support layout.
  • Documents conditional PDO runtime adapters and corrects stale runtime inventories and source paths.
  • Repairs the source-map page frontmatter and synchronizes the PDO roadmap snapshot.

Post-merge integration fixes

  • Aligns the array_key_exists() checker with its dynamic Mixed/Union runtime dispatch after is_array() guards.
  • Preserves Mixed PDO option state across control-flow joins and normalizes session option keys at the helper boundary.
  • Routes raw PDO buffer reads through the strict-PHP-safe internal __elephc_ptr_read_string alias and preserves declared method casing in private-access diagnostics.
  • Keeps the EIR example corpus explicit about default versus optional compiler profiles; dedicated driver suites retain coverage for every optional PDO example.
  • Validates the complete PDO prelude through test_pdo_constants_present plus focused narrowing, prelude-parity, private-factory, and corpus regressions.

Validation performed

Focused validation during the campaign includes:

  • Full clean local campaign baseline: 5,387 codegen tests, 999 error tests, 255 EIR smoke tests, 41 web tests, 647 library tests, 678 binary tests, 205 lexer tests, 349 parser tests, and 3 cdylib tests; all passed with zero failures.
  • PDO codegen baseline: 232 passed with 61 live-service tests intentionally ignored locally.
  • CUBRID: optional-profile check, 37 prelude tests, 4 bridge unit tests, live codegen acceptance, and compiled API-surface coverage for every PHP 8.0 through 8.6 target.
  • Supported-platform ABI/process regression checks for the Linux termination change.
  • Workflow YAML safe-load and Cargo metadata checks.
  • Documentation: 979 Markdown pages checked for required frontmatter/body structure, 922 generated builtin pages validated, 437 public builtin pages audited with zero errors, zero broken checked links, runtime/codegen inventories checked against source, CLI/version references synchronized, and git diff --check clean.

The new GitHub workflows provide the authoritative full supported-target and live-service qualification after push.

Explicit qualification boundaries

  • Informix and IBM require proprietary IBM/HCL client installations and live servers that hosted CI cannot redistribute. Their profiles build on every supported target and receive unit plus compiled-surface coverage; native live acceptance remains externally supplied.
  • Live database services run on Linux x86_64. The three-target CI matrix proves every optional profile builds on macOS AArch64, Linux AArch64, and Linux x86_64, but does not claim that every proprietary client installation was executed on all three architectures.
  • PHP 8.6 remains a preview compatibility target. PDO_SQLSRV is exposed only for upstream-supported PHP versions.
  • The PDO guide records the remaining common and driver-specific divergences. This PR therefore avoids an unqualified literal 100% certification where upstream clients or redistributable live fixtures make that impossible.

Integration with current main

  • Merges origin/main into the feature branch and resolves the compiler, EIR, runtime, test-runner, documentation, and CI workflow overlaps.
  • Preserves current-main reflection/eval state, nullsafe/dynamic dispatch, schema changes, ownership cleanup, and target-aware ABI behavior while retaining every PDO extension.
  • Shares one PHP-version selector between the CLI, PDO, sessions, and web prelude; the supported selector range is 8.0 through 8.6 and the default is 8.5.
  • Adds a target-aware runtime callable-descriptor adapter used by SQLite PDO callbacks for strings, array callables, descriptors, closures, and invokable objects.
  • Removes the pathological DCE tail-cloning cost exposed by the merged large PDO prelude and adds a regression test for large control-flow tails.
  • Preserves packed-versus-associative layout when is_array() narrows a mixed value, joins nullable Mixed option accumulators correctly across conditional loop bodies, and normalizes session keys at the typed helper boundary. This restores PHP 8.4/8.5 session options after the shared version-profile merge.
  • Updates the CI image dependencies for FreeTDS and unixODBC and keeps the PostgreSQL 16/MySQL 8.4 Docker acceptance in the PDO live workflow.

Post-merge focused validation passed: cargo check, formatting, diff hygiene, PHP-version/CLI/PDO-prelude unit tests, the DCE regression, PDO constants, SQLite exec/fetch, all SQLite callable forms, pdo-cubrid and pdo-libpq-gss feature checks, the mixed-array/control-flow regressions plus the four previously failing PHP web-session scenarios, workflow YAML parsing, builtin extraction, and the complete documentation audits above.

Final current-main regression closure

  • Preserves the packed PHP array layout when PDO driver availability filters remove optional drivers, fixing the PHP 8.2/8.4 Informix surface checks without exposing uncompiled profiles.
  • Merges the latest origin/main, including the upstream nested Mixed-write work, and resolves its interaction with PDO branch value-read semantics.
  • Adds explicit EIR ArrayGetForWrite, HashGetForWrite, and MixedArrayGetForWrite operations plus MixedClone, keeping ordinary reads detached while nested writes retain a writable owner path.
  • Recursively COW-normalizes and stores back root indexed/associative containers, clones boxed Mixed locals before dynamic mutation, and detaches selected zvals after outer-container COW. This covers typed, nullable, dynamic Mixed, JSON-produced, multi-level, function-return, compound-assignment, and ArrayAccess parent shapes without leaking updates into aliases.
  • Implements the complete AArch64 and x86_64 runtime paths, including typed hash-entry promotion and selected-cell replacement, and documents the resulting EIR/ownership contract.

Final focused validation: cargo check; formatting/diff/assembly-comment hygiene; 17 nested-write regressions with EIR optimization enabled and disabled; heap-debug-clean static COW coverage; GNU assembly of the final generated Linux x86_64 program; all 10 PDO prelude version tests under pdo-informix; and the compiled PHP 8.2 plus PHP 8.4 Informix API-surface tests.

Concurrent PDO column-buffer isolation

  • Root cause of the live MySQL/PostgreSQL race: elephc_pdo_column_data_ptr() reused one process-global byte buffer, so parallel driver reads could invalidate each other before the returned pointer was copied.
  • The byte-counted column return buffer is now thread-local. Its pointer remains valid until the next column-data pointer call on that same thread, while another thread has independent storage.
  • Added a synchronized two-thread regression test and documented the corrected ABI lifetime.
  • Local validation: all 118 active elephc-pdo library tests pass (2 live tests remain intentionally ignored without servers); formatting and diff hygiene pass.

Resource-aware Mixed value reads

  • The live PostgreSQL bytea regression exposed a compiler/runtime lifetime bug rather than a driver decoder bug: detached boxed Mixed reads duplicated ownership of a raw resource without retaining it, so releasing the first temporary could close the stream still stored in the PDO row.
  • Adds target-aware __rt_mixed_clone lowering across EIR, indexed arrays, hashes, dynamic Mixed containers, and objects. Ordinary PHP values still receive detached zvals; resource tag 9 intentionally retains the existing cell so aliases share cursor and destructor lifetime as in PHP.
  • Adds a driver-independent repeated-resource-read regression and documents the EIR ownership exception.
  • Local validation: all 18 nested-Mixed regressions pass with EIR optimization enabled and disabled; the generic stream regression passes; the exact PostgreSQL 16 type-decoding live test now returns the expected bytea bytes; cargo check, formatting, diff, and assembly-comment hygiene pass.

Eval reference writeback integration

  • Detached boxed-Mixed PHP reads exposed a separate internal ABI requirement: Magician eval variable handles must retain the exact stored cell so generated/AOT by-reference calls can publish mutations back to the interpreter scope.
  • Adds an explicit target-aware __rt_mixed_array_get_shared mode. Ordinary PHP reads remain detached, nested-write fetches keep their COW/detach behavior, and only eval runtime handles retain stored identity with owned refcounts.
  • Covers scalar, nullable, Mixed, iterable, constructor, reflection, first-class callable, closure-bound callable, and call_user_func_array() writeback paths.
  • Local validation: the 10 exact failures from the Linux x86_64 shards pass; all 29 AOT eval-dispatch regressions pass sequentially; the generic PDO resource stream regression still passes; cargo check, formatting, assembly-comment alignment, and diff hygiene pass.
  • Documentation now records the three distinct Mixed read contracts and the eval shared-value ownership boundary.

macOS dead-strip closure

  • Marks the shared __rt_mixed_array_get implementation entry as a Mach-O alternate entry because the ordinary-read, nested-write, and eval-shared wrappers are distinct runtime atoms under -dead_strip.
  • Preserves the same AArch64 and x86_64 control flow while preventing the shared body from being stripped on macOS.
  • Local validation: the cross-atom dead-strip guard passes in both the library and binary test targets; formatting and diff hygiene pass.

Driver-specific PDO placeholder scanning

  • Fixes the SQLSRV live failure where a SQL Server #temp table caused the reused MySQL scanner to treat the rest of the statement as a comment and hide a following named bind.
  • Splits php-src-compatible scanner rules: MySQL retains its driver-specific # comments and whitespace-sensitive -- behavior; DBLIB, Firebird, ODBC, Informix, IBM, and SQLSRV use the generic PDO scanner without # comments.
  • Adds a focused #temp … :label regression, updates the PDO guide and changelog, and validates MySQL, DBLIB, Firebird, ODBC, Informix, IBM, and SQLSRV feature builds/tests locally.

SQLSRV zero-column execution

  • Fixes the second SQLSRV live issue uncovered after placeholder binding succeeded: Microsoft ODBC Driver 17/18 rejects SQLFetch on successful INSERT/UPDATE/DDL statements with no result columns as an invalid cursor state.
  • CLI-backed statements now read SQLRowCount and return immediately when SQLNumResultCols is zero; real rowsets retain the existing fetch/materialization path.
  • Updates the PDO guide and changelog; validates the default warning-free build, SQLSRV unit tests, and ODBC/Informix/IBM feature builds locally, with Driver 18 live acceptance delegated to the PR workflow.

SQLSRV native floating-point binds

  • Aligns SQLSRV input binding with the official Microsoft PHP driver: PHP doubles now use an aligned native buffer and SQL_C_DOUBLE for the complete SQLExecute lifetime.
  • Preserves numeric descriptors such as DECIMAL/NUMERIC; when Driver 17/18 reports an unknown or text descriptor, the bind falls back to SQL_FLOAT instead of the previous long-text type.
  • Closes the live 12.5 -> DECIMAL(10,2) failure, adds a focused mapping regression, and updates both the PDO guide and changelog.
  • Local validation: warning-free cargo build; SQLSRV mapping and unit tests; all five compiled SQLSRV surface tests; ODBC, Informix, and IBM feature checks; formatting and diff hygiene. The existing live SQLSRV round-trip is the authoritative Driver 18 regression gate in CI.

SQLSRV value-driven parameter defaults

  • The native float fix exposed the next generic-type mismatch: Microsoft ODBC described a temporary-table datetime parameter as legacy TEXT, which SQL Server cannot convert to DATETIME2.
  • Ordinary SQLSRV binds now match the official Microsoft PHP driver and derive SQL defaults from the PHP value plus statement encoding instead of calling SQLDescribeParam: UTF-8 text uses SQL_WVARCHAR, system text uses SQL_VARCHAR, binary strings use SQL_VARBINARY, integers select SQL_INTEGER/SQL_BIGINT, doubles use SQL_FLOAT, and NULL follows text/binary encoding.
  • Keeps SQLDescribeParam on the other CLI profiles, where it is part of PDO_ODBC/Informix/IBM behavior; SQLSRV Always Encrypted metadata remains a capability of the installed Microsoft stack.
  • Local validation: cargo build; all 11 active SQLSRV ODBC unit tests; the exact five compiled SQLSRV version-surface tests; ODBC, Informix, and IBM feature checks; formatting and diff hygiene.

Latest-main object ::class integration

  • Merges origin/main through PR feat: support ::class on object expressions #569, including runtime $object::class support.
  • Extends the branch-specific array-representation fixpoint walker with the new ObjectClassName AST node and recursively visits its receiver, restoring exhaustive merged-PR compilation without inventing mutation semantics.
  • Local validation: warning-free cargo build; all five matching object-class-name/eval regressions; SQLSRV parameter mapping regression; formatting and diff hygiene.

SQLSRV buffered-cursor crash fix

  • Aligns the prepare-time cursor transition with Microsoft msphpsql: PDO::CURSOR_SCROLL selects native SQL_CURSOR_STATIC, while PDO::SQLSRV_CURSOR_BUFFERED restores the ODBC handle to SQL_CURSOR_FORWARD_ONLY and keeps scrolling in the bounded client-side buffer.
  • Fixes the live SIGSEGV observed after a successful DECIMAL/DATETIME2/NVARCHAR insert and lastInsertId when the typed SELECT used the buffered cursor profile.
  • Expands the native live SQLSRV bridge regression to cover native doubles, datetime strings, Unicode labels, numeric/datetime fetch attributes, and the exact buffered-cursor transition.
  • Documents the native/client cursor split in docs/php/pdo.md and the regression in CHANGELOG.md.
  • Focused validation: SQLSRV bridge build and mapping test, all five compiled PDO_SQLSRV version-surface tests, plus ODBC/Informix/IBM feature checks. The authoritative full live rerun is recorded below.

SQLSRV prepare-order follow-up

The extended bridge regression exposed that Microsoft ODBC rejects SQL_ATTR_CURSOR_TYPE changes after SQLPrepare. Commit f8f711b now carries the final SQLSRV cursor selection through the internal prepare mode, validates the PDO scroll/driver scroll combination before allocating the statement, applies SQL_CURSOR_STATIC or SQL_CURSOR_FORWARD_ONLY before SQLPrepare, and only verifies the already-applied option afterward. This matches the ordering in msphpsql and keeps invalid scroll-type combinations catchable instead of reaching ODBC in an inconsistent state.

SQLSRV Mixed-hash isset() closure

  • The remaining SQLSRV crash was compiler-side: a heterogeneous associative hash may store a concrete runtime entry tag/payload or an already-boxed Mixed cell, but HashIsset always treated the payload as a box and attempted to unbox raw values such as integer 42.
  • AArch64 and x86_64 now distinguish missing/concrete-null entries, concrete non-null entries, and boxed Mixed entries before unboxing. The PDO prelude therefore retains its PHP-compatible foreach ([1000, ..., 1009]) option dispatch without a SQLSRV-specific static workaround.
  • Adds a driver-independent nested method-array regression covering a missing key, raw integer payload, concrete null, and a hash promoted to boxed Mixed entries by a by-reference loop.
  • Focused validation: the new regression and the existing Mixed-hash isset() ownership regression pass; SQLSRV bridge unit/live tests, its exact live compiled test, and all five version-surface tests pass in the authoritative workflow.

Supported Oracle Instant Client qualification

  • Replaces the CI-only attempt to copy a hand-selected subset of a database server's Oracle Home with Oracle's supported Instant Client Basic Light package.
  • Pins Instant Client 23.26.2, verifies Oracle's published CRC and byte size, installs Ubuntu 24.04's libaio compatibility name, and exposes the complete package through LD_LIBRARY_PATH.
  • Documents that PDO_OCI deployments must use the complete Basic or Basic Light dependency set instead of harvesting selected server libraries.
  • This removes the connection-initialization SIGSEGV and validates both the native Rust bridge round-trip and the compiled PHP PDO_OCI program against Oracle 23ai.

Authoritative PDO live result

GitHub Actions run 29687884748 completed successfully on commit e5ce7e4aa (43m46s). It passed every configured bridge/unit/surface/live gate for:

  • Microsoft PDO_SQLSRV 5.13.1 through ODBC Driver 18, including the exact typed/buffered live regression and every supported PHP-version surface;
  • PDO_OCI through Instant Client 23.26.2 against Oracle 23ai, both in-crate and as compiled PHP;
  • PDO_CUBRID through the official CCI client against CUBRID 11.4, including compiled surface and live round-trip;
  • PostgreSQL 16 through both the pure-Rust client and libpq, including compiled live suites and an ephemeral real GSSAPI/Kerberos handshake with fail-closed credential behavior;
  • MySQL 8.4 with TLS, procedures, multi-rowset behavior, and local-infile coverage;
  • FreeTDS PDO_DBLIB against SQL Server 2022, pure-Rust PDO_FIREBIRD against Firebird 5, and unixODBC PDO_ODBC over PostgreSQL;
  • the versioned PDO_INFORMIX 1.3.7 and PDO_IBM 1.7.0 bridge/unit/compiled surfaces while preserving their explicit proprietary live-SDK boundary.

The amd64 and arm64 CI images for the same commit also completed successfully. GitHub Actions run 29687884727 completed successfully for the same head with 112 jobs and zero failures: builds, docs/stub parity, non-codegen, web, codegen, and eval-codegen shards all passed across macOS AArch64, Linux AArch64, and Linux x86_64 (the benchmark job was intentionally skipped on the PR event).

Squashed delivery

  • The complete PDO implementation is delivered as one commit, c4864ef71, directly on top of the current main branch.
  • The squash preserves the exact Git tree validated above; it only rewrites history and does not alter the implementation.
  • Post-squash workflows: CI, PDO Live Databases, and CI Image.

@github-actions github-actions Bot added area:codegen Touches target-aware assembly or backend lowering. area:platform Touches targets, object formats, linking, or platform support. area:runtime Touches runtime helpers, GC, ownership, or bridge runtimes. scope:multi-area Touches more compiler areas than the automatic area-label cap. size:xl Very large pull request that needs deliberate review planning. target:linux-x86_64 Contains behavior specific to the Linux x86_64 target. type:feature Introduces new user-visible behavior or capabilities. labels Jul 17, 2026
@Guikingone Guikingone self-assigned this Jul 17, 2026
@github-actions github-actions Bot added area:web Touches --web mode, its prelude, or elephc-web. and removed area:codegen Touches target-aware assembly or backend lowering. labels Jul 17, 2026
Implement PHP-version-aware PDO parity across SQLite, MySQL, PostgreSQL, OCI, SQL Server, Firebird, CUBRID, ODBC, DBLIB, Informix, and IBM drivers.

Add native and Docker live coverage, libpq-backed GSSAPI, documentation, and regressions for runtime hash handling and supported Oracle Instant Client setup.
@Guikingone
Guikingone force-pushed the worktree-moonlit-finding-pelican branch from 98d68d2 to c4864ef Compare July 19, 2026 16:24
@Guikingone
Guikingone requested a review from nahime0 July 19, 2026 16:25
@nahime0

nahime0 commented Jul 19, 2026

Copy link
Copy Markdown
Member

Hi @Guikingone, thanks for this. Will look at this soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:platform Touches targets, object formats, linking, or platform support. area:runtime Touches runtime helpers, GC, ownership, or bridge runtimes. area:web Touches --web mode, its prelude, or elephc-web. scope:multi-area Touches more compiler areas than the automatic area-label cap. size:xl Very large pull request that needs deliberate review planning. target:linux-x86_64 Contains behavior specific to the Linux x86_64 target. type:feature Introduces new user-visible behavior or capabilities.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--web + PDO sqlite prepare()/execute(): non-deterministic heap corruption, workers double-free

2 participants