Resolve column keys when PDO SQLite lacks SQLITE_ENABLE_COLUMN_METADATA#446
Resolve column keys when PDO SQLite lacks SQLITE_ENABLE_COLUMN_METADATA#446wojtekn wants to merge 4 commits into
Conversation
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>
|
@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.
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 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 It would be great to have a fallback, but as the PR states, it's pretty complex to solve universally:
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 |
|
Thanks @JanJakes , closing in favor of the fix in static PHP. |
Problem
get_last_column_meta()sources each column's origin table from PDO'sPDOStatement::getColumnMeta()['table']. PDO SQLite only provides that when the SQLite library was built withSQLITE_ENABLE_COLUMN_METADATA, which is off by default in a standard SQLite build — so on many statically-linked PHP builds thetablekey is absent entirely, breaking two things:COLUMN_KEYmatches nothing, so columns lose their key flags — evenwp_posts.ID, a realPRIMARY KEY.table/mysqli:orgtablefields stay empty.Both matter to consumers. phpMyAdmin's
Sql::resultSetContainsUniqueKey()returnsfalseas soon as a column'stableis''— 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
SELECTreads from exactly one base table (no joins, subqueries, derived tables, or UNIONs), remember it and use it inget_last_column_meta()when PDO reports no column metadata at all (thetablekey is absent, as on builds without the flag) — for both the key-flag lookup and the returnedtable/mysqli:orgtablefields. 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
tableempty (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 fromget_last_column_meta()(before → after):
tableSELECT * FROM wp_postsID''→wp_postsnot_null→+ primary_key✅SELECT * FROM wp_optionsoption_id''→wp_optionsnot_null→+ primary_key✅SELECT * FROM wp_optionsoption_name''→wp_optionsnot_null→+ unique_key✅SELECT ID, COUNT(*) … FROM wp_postsCOUNT(*)''→''SELECT p.ID … JOIN …ID''→''A single-table
SELECTright after aJOINstill resolves, confirming no stale-state leak.testColumnInfoWithoutPdoColumnMetadatacovers the flagless path; the full driver suite (366 tests) passes on both flagless and metadata-enabled SQLite builds.End-to-end: browsing
wp_postsin phpMyAdmin now yieldshasUnique=trueand the notice is gone.