PluginProbe
SQLite Database Integration / trunk
SQLite Database Integration vtrunk
3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 All 32 releases
← All changes | wp-includes/database/sqlite/class-wp-sqlite-connection.php +102 -20 2.2.22trunk View file →
@@ -11,8 +11,10 @@
11 11 * This class configures and encapsulates the connection to an SQLite database.
12 12 * It requires PDO with the SQLite driver, and currently, it is only a simple
13 13 * wrapper that leaks some of the PDO APIs (returns PDOStatement values, etc.).
14 14 * In the future, we may abstract it away from PDO and support SQLite3 as well.
15 + *
16 + * @access private
15 17 */
16 18 class WP_SQLite_Connection {
17 19 /**
18 20 * The default timeout in seconds for SQLite to wait for a writable lock.
@@ -33,8 +35,22 @@
33 35 'OFF',
34 36 );
35 37
36 38 /**
39 + * The supported SQLite synchronous settings.
40 + *
41 + * The list is indexed by the corresponding numeric setting values (0 to 3).
42 + *
43 + * See: https://www.sqlite.org/pragma.html#pragma_synchronous
44 + */
45 + const SQLITE_SYNCHRONOUS_SETTINGS = array(
46 + 'OFF',
47 + 'NORMAL',
48 + 'FULL',
49 + 'EXTRA',
50 + );
51 +
52 + /**
37 53 * The PDO connection for SQLite.
38 54 *
39 55 * @var PDO
40 56 */
@@ -42,11 +58,11 @@
42 58
43 59 /**
44 60 * A query logger callback.
45 61 *
46 - * @var callable(string, array): void
62 + * @var (callable(string, array): void)|null
47 63 */
48 - private $query_logger;
64 + private $query_logger = null;
49 65
50 66 /**
51 67 * Constructor.
52 68 *
@@ -54,16 +70,19 @@
54 70 *
55 71 * @param array $options {
56 72 * An array of options.
57 73 *
58 - * @type string|null $path Optional. SQLite database path.
59 - * For in-memory database, use ':memory:'.
60 - * Must be set when PDO instance is not provided.
61 - * @type PDO|null $pdo Optional. PDO instance with SQLite connection.
62 - * If not provided, a new PDO instance will be created.
63 - * @type int|null $timeout Optional. SQLite timeout in seconds.
64 - * The time to wait for a writable lock.
65 - * @type string|null $journal_mode Optional. SQLite journal mode.
74 + * @type string|null $path Optional. SQLite database path.
75 + * For in-memory database, use ':memory:'.
76 + * Must be set when PDO instance is not provided.
77 + * @type PDO|null $pdo Optional. PDO instance with SQLite connection.
78 + * If not provided, a new PDO instance will be created.
79 + * @type int|null $timeout Optional. SQLite timeout in seconds.
80 + * The time to wait for a writable lock.
81 + * @type string|null $journal_mode Optional. SQLite journal mode. Defaults to WAL.
82 + * @type string|int|null $synchronous Optional. SQLite synchronous setting. Defaults to
83 + * NORMAL when the effective journal mode is WAL.
84 + * @type array $pdo_options Optional. PDO constructor options.
66 85 * }
67 86 *
68 87 * @throws InvalidArgumentException When some connection options are invalid.
69 88 * @throws PDOException When the driver initialization fails.
@@ -73,12 +92,17 @@
73 92 if ( isset( $options['pdo'] ) && $options['pdo'] instanceof PDO ) {
74 93 $this->pdo = $options['pdo'];
75 94 } else {
76 95 if ( ! isset( $options['path'] ) || ! is_string( $options['path'] ) ) {
77 - throw new InvalidArgumentException( 'Option "path" is required when "connection" is not provided.' );
96 + throw new InvalidArgumentException( 'Option "path" is required when "pdo" is not provided.' );
78 97 }
79 - $pdo_class = PHP_VERSION_ID >= 80400 ? PDO\SQLite::class : PDO::class;
80 - $this->pdo = new $pdo_class( 'sqlite:' . $options['path'] );
98 + $pdo_class = PHP_VERSION_ID >= 80400 ? Pdo\Sqlite::class : PDO::class;
99 + $pdo_options = $options['pdo_options'] ?? array();
100 +
101 + // Internal driver operations require exceptions regardless of the
102 + // caller-visible WP_MySQL_On_SQLite::ATTR_ERRMODE setting.
103 + $pdo_options[ PDO::ATTR_ERRMODE ] = PDO::ERRMODE_EXCEPTION;
104 + $this->pdo = new $pdo_class( 'sqlite:' . $options['path'], null, null, $pdo_options );
81 105 }
82 106
83 107 // Throw exceptions on error.
84 108 $this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
@@ -90,13 +114,71 @@
90 114 $timeout = self::DEFAULT_SQLITE_TIMEOUT;
91 115 }
92 116 $this->pdo->setAttribute( PDO::ATTR_TIMEOUT, $timeout );
93 117
94 - // Configure SQLite journal mode.
95 - $journal_mode = $options['journal_mode'] ?? null;
96 - if ( $journal_mode && in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) {
97 - $this->query( 'PRAGMA journal_mode = ' . $journal_mode );
118 + // Configure SQLite journal mode. Default to WAL for best throughput.
119 + $effective_journal_mode = null;
120 + $journal_mode = $options['journal_mode'] ?? 'WAL';
121 + if ( is_string( $journal_mode ) ) {
122 + $journal_mode = strtoupper( $journal_mode );
98 123 }
124 + if ( ! in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) {
125 + throw new InvalidArgumentException(
126 + sprintf( 'Invalid SQLite journal mode: %s.', $options['journal_mode'] )
127 + );
128 + }
129 + try {
130 + $effective_journal_mode = strtoupper(
131 + (string) $this->query( 'PRAGMA journal_mode = ' . $journal_mode )->fetchColumn()
132 + );
133 + } catch ( PDOException $e ) {
134 + // WAL may be unavailable in some environments, such as on network
135 + // filesystems. When it is explicitly configured, surface the error.
136 + // Otherwise, fall back to the default SQLite behavior.
137 + if ( isset( $options['journal_mode'] ) ) {
138 + throw $e;
139 + }
140 + }
141 +
142 + /*
143 + * Configure SQLite synchronous setting. Default to NORMAL for WAL mode.
144 + *
145 + * WAL improves read/write concurrency and "synchronous = NORMAL" avoids
146 + * frequent sync to the main database, which could become a bottleneck.
147 + * In WAL mode, NORMAL is safe and recommended. From the SQLite docs:
148 + *
149 + * The synchronous=NORMAL setting provides the best balance between
150 + * performance and safety for most applications running in WAL mode.
151 + * You lose durability across power loss with synchronous NORMAL in WAL
152 + * mode, but that is not important for most applications. Transactions
153 + * are still atomic, consistent, and isolated, which are the most
154 + * important characteristics in most use cases.
155 + *
156 + * SQLite defaults to "synchronous = FULL" to avoid data corruption with
157 + * other journal modes. With WAL, this is not necessary.
158 + *
159 + * See: https://sqlite.org/pragma.html#pragma_synchronous
160 + */
161 + $synchronous = $options['synchronous'] ?? null;
162 + if ( isset( $synchronous ) ) {
163 + // Validate and normalize explicitly provided synchronous value.
164 + if ( is_int( $synchronous ) && isset( self::SQLITE_SYNCHRONOUS_SETTINGS[ $synchronous ] ) ) {
165 + $synchronous = self::SQLITE_SYNCHRONOUS_SETTINGS[ $synchronous ];
166 + } elseif ( is_string( $synchronous ) ) {
167 + $synchronous = strtoupper( $synchronous );
168 + }
169 + if ( ! in_array( $synchronous, self::SQLITE_SYNCHRONOUS_SETTINGS, true ) ) {
170 + throw new InvalidArgumentException(
171 + sprintf( 'Invalid SQLite synchronous setting: %s.', $options['synchronous'] )
172 + );
173 + }
174 + } elseif ( 'WAL' === $effective_journal_mode ) {
175 + // Default to NORMAL for WAL mode.
176 + $synchronous = 'NORMAL';
177 + }
178 + if ( in_array( $synchronous, self::SQLITE_SYNCHRONOUS_SETTINGS, true ) ) {
179 + $this->query( 'PRAGMA synchronous = ' . $synchronous );
180 + }
99 181 }
100 182
101 183 /**
102 184 * Execute a query in SQLite.
@@ -199,12 +281,12 @@
199 281 return $this->pdo;
200 282 }
201 283
202 284 /**
203 - * Set a logger for the queries.
285 + * Set or clear a logger for SQLite queries.
204 286 *
205 - * @param callable(string, array): void $logger A query logger callback.
287 + * @param (callable(string, array): void)|null $logger A query logger callback, or null to clear it.
206 288 */
207 - public function set_query_logger( callable $logger ): void {
289 + public function set_query_logger( ?callable $logger ): void {
208 290 $this->query_logger = $logger;
209 291 }
210 292 }