Skip to content

Resolve column keys when PDO SQLite lacks SQLITE_ENABLE_COLUMN_METADATA#446

Closed
wojtekn wants to merge 4 commits into
WordPress:trunkfrom
wojtekn:fix/column-keys-without-sqlite-column-metadata
Closed

Resolve column keys when PDO SQLite lacks SQLITE_ENABLE_COLUMN_METADATA#446
wojtekn wants to merge 4 commits into
WordPress:trunkfrom
wojtekn:fix/column-keys-without-sqlite-column-metadata

Conversation

@wojtekn

@wojtekn wojtekn commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Problem

get_last_column_meta() sources each column's origin table from PDO's PDOStatement::getColumnMeta()['table']. PDO SQLite only provides that when the SQLite library was built with SQLITE_ENABLE_COLUMN_METADATA, which is off by default in a standard SQLite build — so on many statically-linked PHP builds the table key is absent entirely, breaking two things:

  1. The information-schema lookup for COLUMN_KEY matches nothing, so columns lose their key flags — even wp_posts.ID, a real PRIMARY KEY.
  2. The returned table / mysqli:orgtable fields stay empty.

Both matter to consumers. phpMyAdmin's Sql::resultSetContainsUniqueKey() returns false as soon as a column's table is '' — before it looks at key flags — so browsing any table shows "Current selection does not contain a unique column. Grid edit, checkbox, Edit, Copy and Delete features are not available." and inline editing is disabled. It works on PHP built with the flag (e.g. WebAssembly builds), which is why it's environment-specific; surfaced in WordPress Studio's native-PHP runtime.

Fix

The driver already parses each statement into an AST, so it can recover the origin table itself. When a SELECT reads from exactly one base table (no joins, subqueries, derived tables, or UNIONs), remember it and use it in get_last_column_meta() when PDO reports no column metadata at all (the table key is absent, as on builds without the flag) — for both the key-flag lookup and the returned table / mysqli:orgtable fields. Each column is still confirmed against the information schema, so expression columns (e.g. COUNT(*)) don't inherit the table.

Conservative by design: multi-table queries leave table empty (behavior unchanged, matching how joins are already treated), and when PDO does provide column metadata the fallback never fires — so metadata-enabled builds (CI, WebAssembly) are entirely unaffected.

Testing

Against a real WordPress SQLite DB on PHP without SQLITE_ENABLE_COLUMN_METADATA, table / flags from get_last_column_meta()
(before → after):

Query Column table Flags
SELECT * FROM wp_posts ID ''wp_posts not_null+ primary_key
SELECT * FROM wp_options option_id ''wp_options not_null+ primary_key
SELECT * FROM wp_options option_name ''wp_options not_null+ unique_key
SELECT ID, COUNT(*) … FROM wp_posts COUNT(*) '''' unchanged (expression, correct) ✅
SELECT p.ID … JOIN … ID '''' unchanged (correct) ✅

A single-table SELECT right after a JOIN still resolves, confirming no stale-state leak. testColumnInfoWithoutPdoColumnMetadata covers the flagless path; the full driver suite (366 tests) passes on both flagless and metadata-enabled SQLite builds.

End-to-end: browsing wp_posts in phpMyAdmin now yields hasUnique=true and the notice is gone.

wojtekn and others added 4 commits July 3, 2026 16:13
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only apply the single-table fallback to columns confirmed in the information
schema, so expression columns (e.g. COUNT(*)) keep an empty origin table.
Adds a test simulating a build without SQLITE_ENABLE_COLUMN_METADATA.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only apply the single-table fallback when PDO omits the "table" key entirely
(SQLite built without SQLITE_ENABLE_COLUMN_METADATA). When the key is present
but empty - an expression column on a metadata-enabled build - leave it as-is,
so expression columns keep an empty origin table. Also fixes a phpcs alignment
warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Inject the stored column metadata directly instead of reflecting into whatever
PDO's getColumnMeta() happens to return, which varies by PHP/SQLite build and
broke the test on the matrix extremes. Also avoids ReflectionProperty::
setAccessible() on PHP 8.5, where it is deprecated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JanJakes

JanJakes commented Jul 7, 2026

Copy link
Copy Markdown
Member

@wojtekn Thanks for looking into this. We've addressed the same problem in Playground in the past, but as that PR states, the flag appears to be enabled in almost every SQLite build out there.

PDO SQLite only provides that when the SQLite library was built with SQLITE_ENABLE_COLUMN_METADATA, which is off by default in a standard SQLite build

What do you consider a "standard" SQLite build in this case? The flag seems to be used almost everywhere—MacOS, Debian, Fedora, Gentoo, Arch, Termux, FreeBSD, OpenBSD, Homebrew, Macports, Nix, Haikuports, msys2. Beyond that post, I also checked Ubuntu, Alpine, RHEL family, Amazon Linux, openSUSE, and the official PHP Docker, including Alpine versions. Actually, I didn't find one without this flag.

For PHP itself, as of PHP 7.4, the system libsqlite is used, so it inherits a build from the list above. PHP before 7.4 bundled its own libsqlite, but even that has the metadata flag and there is a PHP PDO test suite specifically requiring that flag. In that sense, we could say that PHP needs this flag to pass its own tests.

How do you bundle SQLite in your case? I just want to avoid solving something here that should probably be solved elsewhere, e.g., in StaticPHP, or wherever libsqlite is compiled. It appears StaticPHP uses that flag, but only for Windows builds. That might be a bug?

It would be great to have a fallback, but as the PR states, it's pretty complex to solve universally:

no joins, subqueries, derived tables, or UNIONs

I'm hesitant to include a fallback for only a single-table use case, as it could give a false sense that the flag is not needed. We could implement it fully (possible, but may get unnecessarily complex) or go the other way and explicitly require SQLITE_ENABLE_COLUMN_METADATA with a more informative error message.

@wojtekn

wojtekn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @JanJakes , closing in favor of the fix in static PHP.

@wojtekn wojtekn closed this Jul 8, 2026
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