Skip to content

Fix utf8mb4_0900_ai_ci collation in tables created by WordPress - #514

Open
JanJakes wants to merge 2 commits into
trunkfrom
wpdb-collate
Open

JanJakes wants to merge 2 commits into
trunkfrom
wpdb-collate

Conversation

@JanJakes

@JanJakes JanJakes commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Summary

With the SQLite driver, $wpdb->collate is empty, so $wpdb->get_charset_collate() returns DEFAULT CHARACTER SET utf8mb4 without a COLLATE clause. Tables created with it (for example, via dbDelta()) therefore use the emulated MySQL 8 default, utf8mb4_0900_ai_ci. MariaDB doesn't support this collation, so these tables fail to import there, including when pushing a WordPress Studio site to WordPress.com.

This PR makes $wpdb use the same charset and collation as WordPress on MySQL:

  • $wpdb->charset is always utf8mb4, matching the emulated connection. DB_CHARSET is ignored.
  • $wpdb->collate is utf8mb4_unicode_520_ci, unless DB_COLLATE specifies another UTF-8 collation. For example, utf8_bin becomes utf8mb4_bin, as in wpdb::determine_charset().
  • utf8mb4_unicode_520_ci is listed in INFORMATION_SCHEMA.COLLATIONS and SHOW COLLATION.

Why

Previously, WP_SQLite_DB initialized the charset only before connecting. At that point, wpdb::determine_charset() leaves the values unchanged, so the collation stayed empty. The values are now resolved without a connection, since SQLite always uses UTF-8 and needs no server capability checks. This also makes them available while connecting, when the driver may reconstruct WordPress tables with wp_get_db_schema().

Calling init_charset() again after connecting, as wpdb::db_connect() does, wouldn't be enough: WordPress Studio may define DB_CHARSET in a mu-plugin that loads later.

This PR leaves the following unchanged:

  • Tables created without a collation (for example, a plain CREATE TABLE) still use utf8mb4_0900_ai_ci, the emulated MySQL 8 default.
  • Existing tables keep their recorded collation.
  • Column collations in the information schema remain utf8mb4_0900_ai_ci. Exports use only the table collation.

Broader charset and collation support is tracked in #456.

Alternative to #511.
Related to Automattic/studio#4737.

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with WordPress charset and collation settings by consistently resolving to utf8mb4 and the appropriate collation, including utf8mb4_unicode_520_ci.
    • Added support for the utf8mb4_unicode_520_ci collation in collation metadata.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 08687dcf-ccd7-432e-a470-2483a25bf7e7

📥 Commits

Reviewing files that changed from the base of the PR and between a8468b5 and 7228d44.

📒 Files selected for processing (8)
  • .github/workflows/phpunit-tests.yml
  • packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-information-schema-builder.php
  • packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Metadata_Tests.php
  • packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php
  • packages/mysql-on-sqlite/tests/WP_SQLite_DB_Tests.php
  • packages/mysql-on-sqlite/tests/tools/verify-native-parser-extension.php
  • packages/plugin-sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php
  • tests/phpunit/WP_SQLite_Database_Integration_Charset_Test.php

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change updates SQLite charset and collation initialization, adds utf8mb4_unicode_520_ci to collation metadata, and adds related tests. The PHPUnit workflow also loads the native parser extension through PHP's INI scan directory.

Changes

Charset and collation handling

Layer / File(s) Summary
Initialize and resolve charset and collation
packages/plugin-sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php, packages/mysql-on-sqlite/tests/WP_SQLite_DB_Tests.php
WP_SQLite_DB initializes charset and collation before connecting and resolves values through a helper. Tests cover initialization with different charset and collation constants.
Expose the added collation
packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-information-schema-builder.php, packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Metadata_Tests.php, packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php
Computed collation metadata and related test expectations include utf8mb4_unicode_520_ci.
Verify charset behavior in database integration
tests/phpunit/WP_SQLite_Database_Integration_Charset_Test.php
Integration tests check reported charset and collation, initialization without a connection, table reconstruction, and table creation using get_charset_collate().

Native parser test setup

Layer / File(s) Summary
Configure native parser loading for tests
.github/workflows/phpunit-tests.yml, packages/mysql-on-sqlite/tests/tools/verify-native-parser-extension.php
The workflow writes the parser extension path to an INI file and runs verification and PHPUnit with that configuration. The verification script calls setAccessible() only on PHP versions below 8.1.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 7228d

The change consistently exposes the intended SQLite charset and collation and configures parser loading for child PHP processes. No concrete merge-blocking issue is established; normal CI checks should pass before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 7228d

The changes are bounded to database initialization, collation metadata, and the test environment. No introduced security weakness was identified. Downstream import behavior and interrupted initialization were not validated in a deployed environment, so some uncertainty remains.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The new native-code loading scope is additional PHP processes inside the existing hosted test job. PR-controlled source was already built and its shared library loaded before this change; the change broadens process inheritance rather than introducing that execution authority. The job retains read-only repository permissions.

Trust Boundaries and Controls

  • observed — Production collation initialization reads the application configuration constant DB_COLLATE, not a request parameter. The added public metadata row is a fixed SQL literal. Native-parser test runs retain explicit verification of native classes, parser delegation, and AST behavior, with the bootstrap gate enabled by the workflow.

Resilience and Maintainability Implications

  • observed — Reconstruction cleans related metadata for missing table records within transactional database configuration. Added integration assertions exercise disconnected charset initialization, reconnection, and connection-time reconstruction, but do not establish behavior for every injected failure or interruption.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 7 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: preventing utf8mb4_0900_ai_ci from being used in WordPress-created tables by resolving the charset and collation to a supported value.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 7 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 PHPMD (2.15.0)
packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php

PHPMD could not process this file (exit code 255): PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/main/php/PDepend/Util/Cache/Driver/FileCacheDriver.php on line 209


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JanJakes JanJakes changed the title Fix utf8mb4_0900_ai_ci collation in tables created by WordPress Fix utf8mb4_0900_ai_ci collation in tables created by WordPress Sep 29, 2026
With DB_CHARSET 'utf8' and an empty DB_COLLATE, $wpdb->collate stayed
empty, because the charset was only initialized before connecting, when
wpdb::determine_charset() doesn't resolve it. Tables created with
get_charset_collate() then got the MySQL 8-only utf8mb4_0900_ai_ci
collation, which fails to import into MariaDB.

SQLite stores all text as UTF-8 and the emulated connection always uses
utf8mb4, so the charset and collation don't depend on the connection.
Resolve them to utf8mb4 with utf8mb4_unicode_520_ci (or a configured
UTF-8 collation) in init_charset(). This also applies while the driver
connects, when it may reconstruct WordPress tables with wp_get_db_schema().

The DB_COLLATE tests run in separate processes. In CI, load the native
parser extension from an INI file, so that these processes load it too,
and avoid a PHP 8.5 deprecation in the native parser check that fails them.

Reported in Automattic/studio#4737
WordPress uses utf8mb4_unicode_520_ci by default, but it was missing from
INFORMATION_SCHEMA.COLLATIONS and SHOW COLLATION.

This branch has not been deployed

No deployments
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.

1 participant