PluginProbe
SQLite Database Integration / 2.2.3
SQLite Database Integration v2.2.3
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
sqlite-database-integration / wp-includes / sqlite-ast / class-wp-sqlite-connection.php

class-wp-sqlite-connection.php in SQLite Database Integration 2.2.3, at wp-includes/sqlite-ast/class-wp-sqlite-connection.php

199 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types = 1);
2
3 /*
4 * The SQLite connection uses PDO. Enable PDO function calls:
5 * phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO
6 */
7
8 /**
9 * SQLite connection.
10 *
11 * This class configures and encapsulates the connection to an SQLite database.
12 * It requires PDO with the SQLite driver, and currently, it is only a simple
13 * wrapper that leaks some of the PDO APIs (returns PDOStatement values, etc.).
14 * In the future, we may abstract it away from PDO and support SQLite3 as well.
15 */
16 class WP_SQLite_Connection {
17 /**
18 * The default timeout in seconds for SQLite to wait for a writable lock.
19 */
20 const DEFAULT_SQLITE_TIMEOUT = 10;
21
22 /**
23 * The supported SQLite journal modes.
24 *
25 * See: https://www.sqlite.org/pragma.html#pragma_journal_mode
26 */
27 const SQLITE_JOURNAL_MODES = array(
28 'DELETE',
29 'TRUNCATE',
30 'PERSIST',
31 'MEMORY',
32 'WAL',
33 'OFF',
34 );
35
36 /**
37 * The PDO connection for SQLite.
38 *
39 * @var PDO
40 */
41 private $pdo;
42
43 /**
44 * A query logger callback.
45 *
46 * @var callable(string, array): void
47 */
48 private $query_logger;
49
50 /**
51 * Constructor.
52 *
53 * Set up an SQLite connection.
54 *
55 * @param array $options {
56 * An array of options.
57 *
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.
66 * }
67 *
68 * @throws InvalidArgumentException When some connection options are invalid.
69 * @throws PDOException When the driver initialization fails.
70 */
71 public function __construct( array $options ) {
72 // Setup PDO connection.
73 if ( isset( $options['pdo'] ) && $options['pdo'] instanceof PDO ) {
74 $this->pdo = $options['pdo'];
75 } else {
76 if ( ! isset( $options['path'] ) || ! is_string( $options['path'] ) ) {
77 throw new InvalidArgumentException( 'Option "path" is required when "connection" is not provided.' );
78 }
79 $this->pdo = new PDO( 'sqlite:' . $options['path'] );
80 }
81
82 // Throw exceptions on error.
83 $this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
84
85 // Configure SQLite timeout.
86 if ( isset( $options['timeout'] ) && is_int( $options['timeout'] ) ) {
87 $timeout = $options['timeout'];
88 } else {
89 $timeout = self::DEFAULT_SQLITE_TIMEOUT;
90 }
91 $this->pdo->setAttribute( PDO::ATTR_TIMEOUT, $timeout );
92
93 // Return all values (except null) as strings.
94 $this->pdo->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, true );
95
96 // Configure SQLite journal mode.
97 $journal_mode = $options['journal_mode'] ?? null;
98 if ( $journal_mode && in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) {
99 $this->query( 'PRAGMA journal_mode = ' . $journal_mode );
100 }
101 }
102
103 /**
104 * Execute a query in SQLite.
105 *
106 * @param string $sql The query to execute.
107 * @param array $params The query parameters.
108 * @throws PDOException When the query execution fails.
109 * @return PDOStatement The PDO statement object.
110 */
111 public function query( string $sql, array $params = array() ): PDOStatement {
112 if ( $this->query_logger ) {
113 ( $this->query_logger )( $sql, $params );
114 }
115 $stmt = $this->pdo->prepare( $sql );
116 $stmt->execute( $params );
117 return $stmt;
118 }
119
120 /**
121 * Returns the ID of the last inserted row.
122 *
123 * @return string The ID of the last inserted row.
124 */
125 public function get_last_insert_id(): string {
126 return $this->pdo->lastInsertId();
127 }
128
129 /**
130 * Quote a value for use in a query.
131 *
132 * @param mixed $value The value to quote.
133 * @param int $type The type of the value.
134 * @return string The quoted value.
135 */
136 public function quote( $value, int $type = PDO::PARAM_STR ): string {
137 return $this->pdo->quote( $value, $type );
138 }
139
140 /**
141 * Quote an SQLite identifier.
142 *
143 * Wraps the identifier in backticks and escapes backtick characters within.
144 *
145 * ---
146 *
147 * Quoted identifiers in SQLite are represented by string constants:
148 *
149 * A string constant is formed by enclosing the string in single quotes (').
150 * A single quote within the string can be encoded by putting two single
151 * quotes in a row - as in Pascal. C-style escapes using the backslash
152 * character are not supported because they are not standard SQL.
153 *
154 * See: https://www.sqlite.org/lang_expr.html#literal_values_constants_
155 *
156 * Although sparsely documented, this applies to backtick and double quoted
157 * string constants as well, so only the quote character needs to be escaped.
158 *
159 * For more details, see the grammar for SQLite table and column names:
160 *
161 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/tokenize.c#L395-L419
162 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/parse.y#L321-L338
163 *
164 * ---
165 *
166 * We use backtick quotes instead of the SQL standard double quotes, due to
167 * an SQLite quirk causing double-quoted strings to be accepted as literals:
168 *
169 * This misfeature means that a misspelled double-quoted identifier will
170 * be interpreted as a string literal, rather than generating an error.
171 *
172 * See: https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
173 *
174 * @param string $unquoted_identifier The unquoted identifier value.
175 * @return string The quoted identifier value.
176 */
177 public function quote_identifier( string $unquoted_identifier ): string {
178 return '`' . str_replace( '`', '``', $unquoted_identifier ) . '`';
179 }
180
181 /**
182 * Get the PDO object.
183 *
184 * @return PDO
185 */
186 public function get_pdo(): PDO {
187 return $this->pdo;
188 }
189
190 /**
191 * Set a logger for the queries.
192 *
193 * @param callable(string, array): void $logger A query logger callback.
194 */
195 public function set_query_logger( callable $logger ): void {
196 $this->query_logger = $logger;
197 }
198 }
199