diff --git a/app/Config/Database.php b/app/Config/Database.php index 1b4c66e52e65..29f6f4a14f42 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -65,6 +65,7 @@ class Database extends Config // 'failover' => [], // 'foreignKeys' => true, // 'busyTimeout' => 1000, + // 'synchronous' => null, // 'dateFormat' => [ // 'date' => 'Y-m-d', // 'datetime' => 'Y-m-d H:i:s', diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 03eb3ab130f7..df6229d632d8 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -16,6 +16,7 @@ use CodeIgniter\Database\BaseConnection; use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\TableName; +use CodeIgniter\Exceptions\InvalidArgumentException; use Exception; use SQLite3; use SQLite3Result; @@ -56,6 +57,15 @@ class Connection extends BaseConnection */ protected $busyTimeout; + /** + * The setting of the "synchronous" flag + * + * @var int<0, 3>|null flag + * + * @see https://www.sqlite.org/pragma.html#pragma_synchronous + */ + protected ?int $synchronous = null; + /** * @return void */ @@ -70,6 +80,13 @@ public function initialize() if (is_int($this->busyTimeout)) { $this->connID->busyTimeout($this->busyTimeout); } + + if (is_int($this->synchronous)) { + if (! in_array($this->synchronous, [0, 1, 2, 3], true)) { + throw new InvalidArgumentException('Invalid synchronous value.'); + } + $this->connID->exec('PRAGMA synchronous = ' . $this->synchronous); + } } /** diff --git a/tests/system/Database/Live/SQLite3/ConnectTest.php b/tests/system/Database/Live/SQLite3/ConnectTest.php new file mode 100644 index 000000000000..17871de81bf1 --- /dev/null +++ b/tests/system/Database/Live/SQLite3/ConnectTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\Live\SQLite3; + +use CodeIgniter\Exceptions\InvalidArgumentException; +use CodeIgniter\Test\CIUnitTestCase; +use Config\Database; +use PHPUnit\Framework\Attributes\Group; + +/** + * @internal + */ +#[Group('DatabaseLive')] +final class ConnectTest extends CIUnitTestCase +{ + protected function setUp(): void + { + parent::setUp(); + + $this->db = Database::connect($this->DBGroup); + + if ($this->db->DBDriver !== 'SQLite3') { + $this->markTestSkipped('This test is only for SQLite3.'); + } + } + + public function testShowErrorMessageWhenSettingInvalidSynchronous(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid synchronous value.'); + + $config = config('Database'); + $group = $config->tests; + // Sets invalid synchronous. + $group['synchronous'] = 123; + $db = Database::connect($group); + + // Actually connect to DB. + $db->initialize(); + } +} diff --git a/user_guide_src/source/changelogs/v4.6.0.rst b/user_guide_src/source/changelogs/v4.6.0.rst index d3dc4529d512..02f5750d78fa 100644 --- a/user_guide_src/source/changelogs/v4.6.0.rst +++ b/user_guide_src/source/changelogs/v4.6.0.rst @@ -208,6 +208,8 @@ Others - Added a new configuration ``foundRows`` for MySQLi to use ``MYSQLI_CLIENT_FOUND_ROWS``. - Added the ``BaseConnection::resetTransStatus()`` method to reset the transaction status. See :ref:`transactions-resetting-transaction-status` for details. +- SQLite3 has a new Config item ``synchronous`` to adjust how strict SQLite is at flushing + to disk during transactions. Modifying this can be useful if we use journal mode set to ``WAL``. Model ===== diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index b42317469038..3d523bcbf3d9 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -178,6 +178,8 @@ Description of Values See `SQLite documentation `_. To enforce Foreign Key constraint, set this config item to true. **busyTimeout** (``SQLite3`` only) milliseconds (int) - Sleeps for a specified amount of time when a table is locked. +**synchronous** (``SQLite3`` only) flag (int) - How strict SQLite will be at flushing to disk during transactions. + Use `null` to stay with the default setting. This can be used since v4.6.0. **numberNative** (``MySQLi`` only) true/false (boolean) - Whether to enable MYSQLI_OPT_INT_AND_FLOAT_NATIVE. **foundRows** (``MySQLi`` only) true/false (boolean) - Whether to enable MYSQLI_CLIENT_FOUND_ROWS. **dateFormat** The default date/time formats as PHP's `DateTime format`_.