Skip to content

Sync with upstream trunk (8 commits) - #5

Merged
batonac merged 10 commits into
d1-supportfrom
upstream-sync
Aug 11, 2026
Merged

Sync with upstream trunk (8 commits)#5
batonac merged 10 commits into
d1-supportfrom
upstream-sync

Conversation

@batonac

@batonac batonac commented Aug 11, 2026

Copy link
Copy Markdown
Member

Automated sync of WordPress/sqlite-database-integration@trunk into d1-support.

This pull request tracks every upstream commit that has not landed on d1-support yet.
It is updated in place as upstream moves, and merges itself once the merge is clean and
every check is green. While it is open, it needs a person.

Upstream commits (8)


Branch shape: merged. Maintained by upstream-sync.yml. Closing this pull request is not permanent — the next scheduled run reopens it. Disable the workflow to stop the sync.

## Summary

This PR improves SQL mode handling and adds **`ANSI_QUOTES`** and
**`ANSII`** support. This includes:

- **SQL mode storage:** SQL modes use MySQL's native bitmap and
canonical order.
- **Validation:** Named and numeric assignments are validated with
MySQL-compatible errors.
- **ANSI_QUOTES:** `ANSI_QUOTES` is supported by the driver, standalone
parser, and native lexer.
- **ANSI** Composite `ANSI` SQL mode is expanded into its component
modes.
- **Quoted identifiers:** Fixed backslash behavior in quoted
identifiers.
- **Native builds:** Lexer constants are regenerated, and the
generator's monorepo path is fixed.

## Why
The `ANSI_QUOTES` SQL mode support keeps reappearing in our recent
performance optimization and parser experiments. This PR extracts and
completes that functionality.

Additionally, the driver stored arbitrary mode names, accepted invalid
assignments, always treated double quotes as string delimiters, did not
expand did not expand composite SQL modes, and applied string escaping
to quoted identifiers. The standalone parser and native lexer lacked
equivalent quoted-identifier handling.
## Summary

> [!NOTE]  
> The commits reflect the logical sequence of changes, so the PR is best
reviewed by commit.

This prepares the **3.0 API surface** by removing obsolete and broken
compatibility paths, narrowing implementation APIs, and aligning exposed
class names with `WP_MySQL_On_SQLite`. This includes:

- Remove the obsolete **`DATABASE_ENGINE` input alias** in favor of
`DB_ENGINE`.
- Remove the broken development-only **crosscheck instrumentation** from
the plugin and release package.
- Remove test-only access to private **legacy driver state**.
- Make self-contained constant maps and self-only translation helpers
**private**.
- Mark parser, native bridge, connection, schema, diagnostic, and
compatibility implementation APIs with WordPress Core-style **`@access
private` annotations**.
- **Deprecate `WP_SQLite_Driver`** in favor of `WP_MySQL_On_SQLite`.
- Remove the obsolete **Performance Lab SQLite migration**, upgrade UI,
and notice compatibility. The migration had been broken since 2023
because it used `$wp_filesystem` without initializing the
`WP_Filesystem` class.
- **Stop accepting the `@pdo` global as connection input.** Expose the
active driver through `WP_SQLite_DB::get_driver()` and its underlying
SQLite PDO through `WP_MySQL_On_SQLite::get_sqlite_pdo()`, while
retaining the global temporarily as a deprecated output alias.
- Rename `WP_SQLite_Driver_Exception` to `WP_MySQL_On_SQLite_Exception`.
- Rename `WP_PDO_Proxy_Statement` to `WP_MySQL_On_SQLite_Statement`.
## Summary

Stacked on WordPress#469.

> [!NOTE]  
> The commits reflect the logical sequence of changes, so the PR is best
reviewed by commit.

This expands **PDO compatibility** for `WP_MySQL_On_SQLite` before the
3.0 release:

- Implement **`lastInsertId()`, `errorCode()`, and `errorInfo()`**
without relying on uninitialized parent PDO state.
- **Isolate the public last insert ID** from internal information-schema
writes.
- **Reset operation state via `flush()`** for direct transaction
methods.
- Honor numeric **PDO constructor options**, improve PDO attribute
handling.
- Initialize **`PDOStatement::$queryString`** on PHP 8.1+ (on older PHP
versions, it's unfortunately not writable).
- **Delegate safe result statement methods**, including iteration,
`closeCursor()`, `bindColumn()`, and diagnostics.
- Snapshot result **column metadata** into each statement; implement
`getColumnMeta()` with its `array|false` contract.
- Populate **`PDOException::$errorInfo`** with native SQLite or emulated
MySQL diagnostics.
- Override **`PDO::connect()` on PHP 8.4+** so it creates the emulated
connection.
- Report **`prepare()` as unsupported** for now.

Created follow-up tickets related to this PR:
- WordPress#473
- WordPress#474
- WordPress#475
## Summary

This PR prepares the repository's documentation and non-functional
metadata for the 3.0 release:

- **Driver documentation:** Adds a focused package README covering the
overview, requirements, usage, DSN format, connection options,
compatibility limits, and development workflow. It also documents the
public exception and driver accessor in source.
- **Package status and naming:** Marks the MySQL proxy as experimental
and replaces the stale "WP SQLite Driver" name with "MySQL on SQLite."
- **Release and source documentation:** Corrects the release command and
branch naming, outdated option and drop-in metadata, inaccurate class
documentation, and spelling and grammar errors.
- **Obsolete annotations:** Removes `@since` and `@package` annotations
throughout the repository.

## Why

Version 3.0 establishes a well-defined PDO-compatible API. Documenting
its entry points, connection format, configuration, and limitations
makes that contract discoverable outside source docblocks, while the
remaining cleanup removes stale or misleading information before the
release is tagged.

These changes do not alter runtime behavior.
## Summary

This PR is stacked on WordPress#477.

This PR makes the configured **MySQL version** the single source of
truth for parsing and version reporting. It also makes the driver
consistently identify itself as **MySQL on SQLite** instead of exposing
SQLite details through MySQL-facing APIs.

The changes are:

- **Version handling:** Validate `mysql_version`, unify its usage, and
fix formatting.
- **Server identity:** Report the driver name and consistent server,
client, SQL, and WordPress version information.
- **System variables:** Report `version` and `version_comment` through
both `@@` variables and `SHOW VARIABLES`.

## Why

The parser previously used a hard-coded MySQL version, while different
public APIs could report conflicting MySQL and SQLite identities. The
version and server identity reporting was messy and unfinished.

## Reported values

With the default `mysql_version` (`80038`) and current driver version
(`3.0.0-rc.8`), the APIs report:

The MariaDB reference values were measured with MariaDB 10.11.15 from
the official container and PHP 8.5.9 with mysqlnd. Distribution and
client suffixes can vary.

| Interface | MySQL on SQLite | MariaDB reference |
| --- | --- | --- |
| `PDO::ATTR_DRIVER_NAME` | `mysql` | `mysql` |
| `PDO::ATTR_SERVER_VERSION` | `8.0.38-mysql-on-sqlite-3.0.0-rc.8` |
`10.11.15-MariaDB-ubu2204` |
| `PDO::ATTR_CLIENT_VERSION` | `mysqlnd
8.0.38-mysql-on-sqlite-3.0.0-rc.8` | `mysqlnd 8.5.9` |
| `VERSION()` | `8.0.38-mysql-on-sqlite-3.0.0-rc.8` |
`10.11.15-MariaDB-ubu2204` |
| `@@version`<br>`@@GLOBAL.version`<br>`SHOW VARIABLES: version` |
`8.0.38-mysql-on-sqlite-3.0.0-rc.8` | `10.11.15-MariaDB-ubu2204` |
| `@@version_comment`<br>`@@GLOBAL.version_comment`<br>`SHOW VARIABLES:
version_comment` | `MySQL on SQLite` | `mariadb.org binary distribution`
|
| `WP_SQLite_DB::db_version()` | `8.0.38` | `10.11.15` |
| `WP_SQLite_DB::db_server_info()` | `8.0.38-mysql-on-sqlite-3.0.0-rc.8`
| `10.11.15-MariaDB-ubu2204` |
| `WP_MySQL_On_SQLite::$client_info` | `mysqlnd
8.0.38-mysql-on-sqlite-3.0.0-rc.8` | `mysqlnd 8.5.9` |

## Accepted WordPress test failures

Reporting MySQL 8.0.38 causes WordPress to apply MySQL 8 behavior when
interpreting database metadata. This exposes three existing
compatibility gaps:

- `Tests_DB_Charset::test_get_column_charset` data sets `#5` and `#6`,
because the driver still reports `utf8` instead of the modern `utf8mb3`
alias.
- `Tests_DB_dbDelta::test_column_type_change`, because the driver still
reports integer display widths that MySQL 8.0.19 and later omit.

These cases are tracked as expected failures in this PR.
Version-dependent metadata reporting will be addressed separately in a
follow-up.
## Summary

Rename the SQLite-specific `WP_MySQL_On_SQLite` constructor options so
their ownership is explicit:

- `pdo` → `sqlite_pdo`
- `journal_mode` → `sqlite_journal_mode`
- `synchronous` → `sqlite_synchronous`

The documentation, internal callers, and tests now use the prefixed
names.

## Rationale

`WP_MySQL_On_SQLite` accepts standard numeric PDO constructor options
alongside its own string-keyed options. The generic names did not
clearly distinguish SQLite connection configuration from PDO attributes
or other driver configuration. The `sqlite_` prefix makes that boundary
explicit.
## Summary

This completes four API and compatibility refinements before 3.0:

- Prevents `PDOStatement::execute()` from bypassing MySQL emulation.
Marking it not implemented for now.
- Normalizes missing table failures to the MySQL-compatible SQLSTATE,
error code, and message.
- Marks the legacy database constants as deprecated.
- Removes `ext-filter` dependency when reconstructing hexadecimal
defaults.
@batonac batonac added upstream-sync Automated sync with the WordPress upstream upstream-conflict Upstream sync needs a manual merge labels Aug 11, 2026
JanJakes and others added 2 commits August 11, 2026 17:36
…ess#484)

## Summary

Restore `WP_SQLite_DB::db_version()` and remove the redundant
implementation from the unit-test `wpdb` stub.

## Why

WordPress 5.4 and older do not provide `wpdb::db_server_info()`. Their
inherited `db_version()` asks the MySQL extension for server information
directly, which does not work with the SQLite-backed connection used by
Playground. Defining the method on the drop-in makes these releases read
the emulated MySQL version through the driver API.

Fixes WordPress/wordpress-playground#4256
Resolve the drift between the fork's D1 backend and the upstream 3.0 API
refinements, and adopt the upstream changes in the D1-specific code.

Conflict resolutions:

  - Keep the connection interface as the driver's single access path.
    Upstream reintroduced direct `get_pdo()` calls in `setAttribute()`,
    `getAttribute()` and the user-defined function registration; these now
    go through `set_attribute()` / `get_attribute()` / `create_function()`
    so the D1 connection keeps working, while upstream's new behavior
    (driver-specific attribute rejection, caller error mode tracking,
    stringify-fetches reporting) is preserved in full.
  - Keep `WP_PDO_Array_Statement` for composed result sets. Upstream builds
    them from a connection-private TEMP table, which a remote backend has
    no way to provide. The in-memory statement is backend-agnostic and
    avoids the write lock the TEMP table exists to dodge, so the now-unused
    `EMPTY_RESULT_TABLE_NAME` constant is dropped.
  - Follow upstream's rename of the statement class and of
    `WP_SQLite_Driver_Exception` to `WP_MySQL_On_SQLite_Exception`.
  - Keep both sides where upstream and the fork added different methods
    to the same place.

Adopted upstream changes:

  - Rename the connection injection option to `sqlite_connection`, matching
    the upstream convention of prefixing SQLite-specific options (WordPress#481), and
    document it in the driver README.
  - Give the D1 connection an error mode attribute, so upstream's new
    caller-visible `PDO::ATTR_ERRMODE` handling works on that backend
    instead of failing every set.
  - Report `PDO::ATTR_STRINGIFY_FETCHES` from PDO itself on PHP 8.2+. The
    locally tracked value could not see the initial state of an injected
    PDO instance.

Also fix a latent bug in the D1 fake transport: it restored the handle's
stringification setting before fetching rows rather than after, and PDO
SQLite applies that attribute at fetch time. Every value therefore arrived
as a string and the fake never reproduced D1's native JSON types. This
surfaced through upstream's new numeric `sql_mode` bitmaps (WordPress#452), which
depend on `getColumnMeta()` reporting a native type.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

🤖 Lexer benchmark

Changes to lexer-related files were detected and triggered a benchmark:

Config Base (QPS) This PR (QPS) Speedup
no JIT 71,492 71,541 1.00×
tracing JIT 162,807 159,807 0.98×

Note: Hosted runners are noisy, and absolute numbers vary. Treat the results with caution and verify them locally.

To reproduce locally:

cd packages/mysql-on-sqlite && composer run bench-lexer

@batonac batonac changed the title Sync with upstream trunk (7 commits) Sync with upstream trunk (8 commits) Aug 11, 2026
@batonac batonac removed the upstream-conflict Upstream sync needs a manual merge label Aug 11, 2026
The MySQL functions that the D1 backend rewrites instead of emulating with
PHP callbacks used SQLite features newer than the project's floor of SQLite
3.37.0. They only ever ran against whatever SQLite the D1 job happened to
have, so this went unnoticed until the full version matrix ran:

  - IF() is a reserved word, so it parses as a runtime function call and
    never reached the rewrite at all. It survived only because SQLite 3.48
    added an "if" alias for IIF(). It now compiles to a CASE expression,
    which every version understands and which selects the "else" branch for
    a NULL condition, as MySQL does.
  - WEEK() mode 0 used the "%U" strftime specifier (SQLite 3.46+). It now
    computes the same value the way "%U" is defined. Verified against "%U"
    for every date in 2020-2027.
  - UNIX_TIMESTAMP() used UNIXEPOCH() (SQLite 3.38+), now STRFTIME('%s').
  - LOCATE() with an offset used IIF() (SQLite 3.32+), now a CASE expression.
  - LOG() uses LN(), which is a math function: SQLite 3.35+ and only when
    compiled with SQLITE_ENABLE_MATH_FUNCTIONS. Constant arguments are now
    evaluated in PHP, matching LN() including its NULL results for
    out-of-domain values; other arguments still compile to LN().

Scope the oracle tests to the versions their oracle can serve:

  - WP_PDO_Array_Statement_Tests compares against PDO SQLite, which before
    PHP 8.1 has different value semantics entirely: it always stringifies,
    and does so from SQLite's text rendering rather than from a PHP value,
    so a float reads as "1230.0" and every column reports PDO::PARAM_STR.
    The array statement implements the current semantics, so there is
    nothing to compare against on older versions.
  - The D1 fake transport reads its values from PDO SQLite and inherits the
    same limitation, which the driver's numeric sql_mode handling depends
    on. Those five tests are skipped below PHP 8.1 with that reason. Only
    the test double is affected: json_decode() yields native types on every
    supported PHP version, so the real transports carry them as D1 does.

These failures all predate the upstream merge; they are fixed here because
the merge is the first change to run the full PHP and SQLite matrix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@batonac
batonac merged commit 1b532ab into d1-support Aug 11, 2026
54 of 55 checks passed
@batonac
batonac deleted the upstream-sync branch August 11, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

upstream-sync Automated sync with the WordPress upstream

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants