diff --git a/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-connection.php b/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-connection.php index 1509a1e64..fbd7cd3d1 100644 --- a/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-connection.php +++ b/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-connection.php @@ -19,6 +19,16 @@ class WP_SQLite_Connection { */ const DEFAULT_SQLITE_TIMEOUT = 10; + /** + * The default SQLite journal mode. + */ + const DEFAULT_SQLITE_JOURNAL_MODE = 'WAL'; + + /** + * The default SQLite synchronous setting for WAL mode. + */ + const DEFAULT_SQLITE_WAL_SYNCHRONOUS = 'NORMAL'; + /** * The supported SQLite journal modes. * @@ -33,6 +43,18 @@ class WP_SQLite_Connection { 'OFF', ); + /** + * The supported SQLite synchronous settings. + * + * See: https://www.sqlite.org/pragma.html#pragma_synchronous + */ + const SQLITE_SYNCHRONOUS_SETTINGS = array( + 'OFF', + 'NORMAL', + 'FULL', + 'EXTRA', + ); + /** * The PDO connection for SQLite. * @@ -63,6 +85,7 @@ class WP_SQLite_Connection { * @type int|null $timeout Optional. SQLite timeout in seconds. * The time to wait for a writable lock. * @type string|null $journal_mode Optional. SQLite journal mode. + * @type string|null $synchronous Optional. SQLite synchronous setting. * } * * @throws InvalidArgumentException When some connection options are invalid. @@ -92,9 +115,28 @@ public function __construct( array $options ) { $this->pdo->setAttribute( PDO::ATTR_TIMEOUT, $timeout ); // Configure SQLite journal mode. - $journal_mode = $options['journal_mode'] ?? null; + $effective_journal_mode = null; + $journal_mode = $options['journal_mode'] ?? self::DEFAULT_SQLITE_JOURNAL_MODE; + if ( is_string( $journal_mode ) ) { + $journal_mode = strtoupper( $journal_mode ); + } if ( $journal_mode && in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) { - $this->query( 'PRAGMA journal_mode = ' . $journal_mode ); + $effective_journal_mode = strtoupper( + (string) $this->query( 'PRAGMA journal_mode = ' . $journal_mode )->fetchColumn() + ); + } + + // Configure SQLite synchronous setting. + $synchronous = $options['synchronous'] ?? null; + if ( null === $synchronous && 'WAL' === $effective_journal_mode ) { + // Keep rollback journal modes on SQLite's default durability unless explicitly configured. + $synchronous = self::DEFAULT_SQLITE_WAL_SYNCHRONOUS; + } + if ( is_string( $synchronous ) ) { + $synchronous = strtoupper( $synchronous ); + } + if ( $synchronous && in_array( $synchronous, self::SQLITE_SYNCHRONOUS_SETTINGS, true ) ) { + $this->query( 'PRAGMA synchronous = ' . $synchronous ); } } diff --git a/packages/mysql-on-sqlite/tests/WP_SQLite_Connection_Tests.php b/packages/mysql-on-sqlite/tests/WP_SQLite_Connection_Tests.php new file mode 100644 index 000000000..5ffb93ce3 --- /dev/null +++ b/packages/mysql-on-sqlite/tests/WP_SQLite_Connection_Tests.php @@ -0,0 +1,75 @@ +db_path = tempnam( sys_get_temp_dir(), 'wp_sqlite_' ); + unlink( $this->db_path ); + } + + public function tearDown(): void { + foreach ( array( + $this->db_path, + $this->db_path . '-wal', + $this->db_path . '-shm', + $this->db_path . '-journal', + ) as $path ) { + if ( is_string( $path ) && file_exists( $path ) ) { + unlink( $path ); + } + } + $this->db_path = null; + } + + public function testDefaultJournalModeUsesWal(): void { + $connection = new WP_SQLite_Connection( array( 'path' => $this->db_path ) ); + + $this->assertSame( + 'wal', + strtolower( (string) $connection->query( 'PRAGMA journal_mode' )->fetchColumn() ) + ); + $this->assertSame( + '1', + (string) $connection->query( 'PRAGMA synchronous' )->fetchColumn() + ); + } + + public function testJournalModeCanBeOverridden(): void { + $connection = new WP_SQLite_Connection( + array( + 'path' => $this->db_path, + 'journal_mode' => 'DELETE', + ) + ); + + $this->assertSame( + 'delete', + strtolower( (string) $connection->query( 'PRAGMA journal_mode' )->fetchColumn() ) + ); + } + + public function testSynchronousCanBeOverridden(): void { + $connection = new WP_SQLite_Connection( + array( + 'path' => $this->db_path, + 'synchronous' => 'FULL', + ) + ); + + $this->assertSame( + '2', + (string) $connection->query( 'PRAGMA synchronous' )->fetchColumn() + ); + } +} diff --git a/packages/plugin-sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php b/packages/plugin-sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php index 072030e7f..fa4a02f04 100644 --- a/packages/plugin-sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php +++ b/packages/plugin-sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php @@ -316,6 +316,7 @@ public function db_connect( $allow_bail = true ) { 'pdo' => $pdo, 'path' => FQDB, 'journal_mode' => defined( 'SQLITE_JOURNAL_MODE' ) ? SQLITE_JOURNAL_MODE : null, + 'synchronous' => defined( 'SQLITE_SYNCHRONOUS' ) ? SQLITE_SYNCHRONOUS : null, ) ); $this->dbh = new WP_SQLite_Driver( $connection, $this->dbname ); diff --git a/packages/plugin-sqlite-database-integration/wp-includes/sqlite/install-functions.php b/packages/plugin-sqlite-database-integration/wp-includes/sqlite/install-functions.php index 5d73e39eb..c5018b9fb 100644 --- a/packages/plugin-sqlite-database-integration/wp-includes/sqlite/install-functions.php +++ b/packages/plugin-sqlite-database-integration/wp-includes/sqlite/install-functions.php @@ -35,7 +35,13 @@ function sqlite_make_db_sqlite() { } $translator = new WP_SQLite_Driver( - new WP_SQLite_Connection( array( 'pdo' => $pdo ) ), + new WP_SQLite_Connection( + array( + 'pdo' => $pdo, + 'journal_mode' => defined( 'SQLITE_JOURNAL_MODE' ) ? SQLITE_JOURNAL_MODE : null, + 'synchronous' => defined( 'SQLITE_SYNCHRONOUS' ) ? SQLITE_SYNCHRONOUS : null, + ) + ), $wpdb->dbname ); $query = null;