PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.35.1
MailPoet – Newsletters, Email Marketing, and Automation v5.35.1
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / doctrine / dbal / src / Schema / SqliteSchemaManager.php
mailpoet / vendor-prefixed / doctrine / dbal / src / Schema Last commit date
Exception 2 years ago Visitor 9 months ago AbstractAsset.php 2 years ago AbstractSchemaManager.php 9 months ago Column.php 2 years ago ColumnDiff.php 2 years ago Comparator.php 2 years ago Constraint.php 2 years ago DB2SchemaManager.php 2 years ago DefaultSchemaManagerFactory.php 2 years ago ForeignKeyConstraint.php 9 months ago Identifier.php 2 years ago Index.php 2 years ago LegacySchemaManagerFactory.php 2 years ago MySQLSchemaManager.php 9 months ago OracleSchemaManager.php 2 years ago PostgreSQLSchemaManager.php 9 months ago SQLServerSchemaManager.php 9 months ago Schema.php 2 years ago SchemaConfig.php 2 years ago SchemaDiff.php 2 years ago SchemaException.php 2 years ago SchemaManagerFactory.php 2 years ago Sequence.php 2 years ago SqliteSchemaManager.php 8 months ago Table.php 2 years ago TableDiff.php 2 years ago UniqueConstraint.php 2 years ago View.php 2 years ago
SqliteSchemaManager.php
480 lines
1 <?php
2 namespace MailPoetVendor\Doctrine\DBAL\Schema;
3 if (!defined('ABSPATH')) exit;
4 use MailPoetVendor\Doctrine\DBAL\DriverManager;
5 use MailPoetVendor\Doctrine\DBAL\Exception;
6 use MailPoetVendor\Doctrine\DBAL\Platforms\SQLite;
7 use MailPoetVendor\Doctrine\DBAL\Platforms\SqlitePlatform;
8 use MailPoetVendor\Doctrine\DBAL\Result;
9 use MailPoetVendor\Doctrine\DBAL\Types\StringType;
10 use MailPoetVendor\Doctrine\DBAL\Types\TextType;
11 use MailPoetVendor\Doctrine\DBAL\Types\Type;
12 use MailPoetVendor\Doctrine\Deprecations\Deprecation;
13 use function array_change_key_case;
14 use function array_map;
15 use function array_merge;
16 use function count;
17 use function explode;
18 use function file_exists;
19 use function implode;
20 use function preg_match;
21 use function preg_match_all;
22 use function preg_quote;
23 use function preg_replace;
24 use function rtrim;
25 use function str_replace;
26 use function strcasecmp;
27 use function strpos;
28 use function strtolower;
29 use function trim;
30 use function unlink;
31 use function usort;
32 use const CASE_LOWER;
33 class SqliteSchemaManager extends AbstractSchemaManager
34 {
35 public function listTableNames()
36 {
37 return $this->doListTableNames();
38 }
39 public function listTables()
40 {
41 return $this->doListTables();
42 }
43 public function listTableDetails($name)
44 {
45 Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__);
46 return $this->doListTableDetails($name);
47 }
48 public function listTableColumns($table, $database = null)
49 {
50 return $this->doListTableColumns($table, $database);
51 }
52 public function listTableIndexes($table)
53 {
54 return $this->doListTableIndexes($table);
55 }
56 protected function fetchForeignKeyColumnsByTable(string $databaseName) : array
57 {
58 $columnsByTable = parent::fetchForeignKeyColumnsByTable($databaseName);
59 if (count($columnsByTable) > 0) {
60 foreach ($columnsByTable as $table => $columns) {
61 $columnsByTable[$table] = $this->addDetailsToTableForeignKeyColumns($table, $columns);
62 }
63 }
64 return $columnsByTable;
65 }
66 public function dropDatabase($database)
67 {
68 Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4963', 'SqliteSchemaManager::dropDatabase() is deprecated. Delete the database file using the filesystem.');
69 if (!file_exists($database)) {
70 return;
71 }
72 unlink($database);
73 }
74 public function createDatabase($database)
75 {
76 Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4963', 'SqliteSchemaManager::createDatabase() is deprecated.' . ' The engine will create the database file automatically.');
77 $params = $this->_conn->getParams();
78 $params['path'] = $database;
79 unset($params['memory']);
80 $conn = DriverManager::getConnection($params);
81 $conn->connect();
82 $conn->close();
83 }
84 public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
85 {
86 if (!$table instanceof Table) {
87 $table = $this->listTableDetails($table);
88 }
89 $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [$foreignKey]));
90 }
91 public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
92 {
93 Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'SqliteSchemaManager::dropAndCreateForeignKey() is deprecated.' . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.');
94 if (!$table instanceof Table) {
95 $table = $this->listTableDetails($table);
96 }
97 $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [$foreignKey]));
98 }
99 public function dropForeignKey($foreignKey, $table)
100 {
101 if (!$table instanceof Table) {
102 $table = $this->listTableDetails($table);
103 }
104 $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [], [$foreignKey]));
105 }
106 public function listTableForeignKeys($table, $database = null)
107 {
108 $table = $this->normalizeName($table);
109 $columns = $this->selectForeignKeyColumns($database ?? 'main', $table)->fetchAllAssociative();
110 if (count($columns) > 0) {
111 $columns = $this->addDetailsToTableForeignKeyColumns($table, $columns);
112 }
113 return $this->_getPortableTableForeignKeysList($columns);
114 }
115 protected function _getPortableTableDefinition($table)
116 {
117 return $table['table_name'];
118 }
119 protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
120 {
121 $indexBuffer = [];
122 // fetch primary
123 $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_TABLE_INFO (?)', [$tableName]);
124 usort(
125 $indexArray,
126 static function (array $a, array $b) : int {
127 if ($a['pk'] === $b['pk']) {
128 return $a['cid'] - $b['cid'];
129 }
130 return $a['pk'] - $b['pk'];
131 }
132 );
133 foreach ($indexArray as $indexColumnRow) {
134 if ($indexColumnRow['pk'] === 0 || $indexColumnRow['pk'] === '0') {
135 continue;
136 }
137 $indexBuffer[] = ['key_name' => 'primary', 'primary' => \true, 'non_unique' => \false, 'column_name' => $indexColumnRow['name']];
138 }
139 // fetch regular indexes
140 foreach ($tableIndexes as $tableIndex) {
141 // Ignore indexes with reserved names, e.g. autoindexes
142 if (strpos($tableIndex['name'], 'sqlite_') === 0) {
143 continue;
144 }
145 $keyName = $tableIndex['name'];
146 $idx = [];
147 $idx['key_name'] = $keyName;
148 $idx['primary'] = \false;
149 $idx['non_unique'] = !$tableIndex['unique'];
150 $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_INDEX_INFO (?)', [$keyName]);
151 foreach ($indexArray as $indexColumnRow) {
152 $idx['column_name'] = $indexColumnRow['name'];
153 $indexBuffer[] = $idx;
154 }
155 }
156 return parent::_getPortableTableIndexesList($indexBuffer, $tableName);
157 }
158 protected function _getPortableTableColumnList($table, $database, $tableColumns)
159 {
160 $list = parent::_getPortableTableColumnList($table, $database, $tableColumns);
161 // find column with ?autoincrement
162 $autoincrementColumn = null;
163 $autoincrementCount = 0;
164 foreach ($tableColumns as $tableColumn) {
165 if ($tableColumn['pk'] === 0 || $tableColumn['pk'] === '0') {
166 continue;
167 }
168 $autoincrementCount++;
169 if ($autoincrementColumn !== null || strtolower($tableColumn['type']) !== 'integer') {
170 continue;
171 }
172 $autoincrementColumn = $tableColumn['name'];
173 }
174 if ($autoincrementCount === 1 && $autoincrementColumn !== null) {
175 foreach ($list as $column) {
176 if ($autoincrementColumn !== $column->getName()) {
177 continue;
178 }
179 $column->setAutoincrement(\true);
180 }
181 }
182 // inspect column collation and comments
183 $createSql = $this->getCreateTableSQL($table);
184 foreach ($list as $columnName => $column) {
185 $type = $column->getType();
186 if ($type instanceof StringType || $type instanceof TextType) {
187 $column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?? 'BINARY');
188 }
189 $comment = $this->parseColumnCommentFromSQL($columnName, $createSql);
190 if ($comment === null) {
191 continue;
192 }
193 $type = $this->extractDoctrineTypeFromComment($comment, '');
194 if ($type !== '') {
195 $column->setType(Type::getType($type));
196 $comment = $this->removeDoctrineTypeFromComment($comment, $type);
197 }
198 $column->setComment($comment);
199 }
200 return $list;
201 }
202 protected function _getPortableTableColumnDefinition($tableColumn)
203 {
204 $parts = explode('(', $tableColumn['type']);
205 $tableColumn['type'] = trim($parts[0]);
206 if (isset($parts[1])) {
207 $length = trim($parts[1], ')');
208 $tableColumn['length'] = $length;
209 }
210 $dbType = strtolower($tableColumn['type']);
211 $length = $tableColumn['length'] ?? null;
212 $unsigned = \false;
213 if (strpos($dbType, ' unsigned') !== \false) {
214 $dbType = str_replace(' unsigned', '', $dbType);
215 $unsigned = \true;
216 }
217 $fixed = \false;
218 $type = $this->_platform->getDoctrineTypeMapping($dbType);
219 $default = $tableColumn['dflt_value'];
220 if ($default === 'NULL') {
221 $default = null;
222 }
223 if ($default !== null) {
224 // SQLite returns the default value as a literal expression, so we need to parse it
225 if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) {
226 $default = str_replace("''", "'", $matches[1]);
227 }
228 }
229 $notnull = (bool) $tableColumn['notnull'];
230 if (!isset($tableColumn['name'])) {
231 $tableColumn['name'] = '';
232 }
233 $precision = null;
234 $scale = null;
235 switch ($dbType) {
236 case 'char':
237 $fixed = \true;
238 break;
239 case 'float':
240 case 'double':
241 case 'real':
242 case 'decimal':
243 case 'numeric':
244 if (isset($tableColumn['length'])) {
245 if (strpos($tableColumn['length'], ',') === \false) {
246 $tableColumn['length'] .= ',0';
247 }
248 [$precision, $scale] = array_map('trim', explode(',', $tableColumn['length']));
249 }
250 $length = null;
251 break;
252 }
253 $options = ['length' => $length, 'unsigned' => $unsigned, 'fixed' => $fixed, 'notnull' => $notnull, 'default' => $default, 'precision' => $precision, 'scale' => $scale];
254 return new Column($tableColumn['name'], Type::getType($type), $options);
255 }
256 protected function _getPortableViewDefinition($view)
257 {
258 return new View($view['name'], $view['sql']);
259 }
260 protected function _getPortableTableForeignKeysList($tableForeignKeys)
261 {
262 $list = [];
263 foreach ($tableForeignKeys as $value) {
264 $value = array_change_key_case($value, CASE_LOWER);
265 $id = $value['id'];
266 if (!isset($list[$id])) {
267 if (!isset($value['on_delete']) || $value['on_delete'] === 'RESTRICT') {
268 $value['on_delete'] = null;
269 }
270 if (!isset($value['on_update']) || $value['on_update'] === 'RESTRICT') {
271 $value['on_update'] = null;
272 }
273 $list[$id] = ['name' => $value['constraint_name'], 'local' => [], 'foreign' => [], 'foreignTable' => $value['table'], 'onDelete' => $value['on_delete'], 'onUpdate' => $value['on_update'], 'deferrable' => $value['deferrable'], 'deferred' => $value['deferred']];
274 }
275 $list[$id]['local'][] = $value['from'];
276 if ($value['to'] === null) {
277 // Inferring a shorthand form for the foreign key constraint, where the "to" field is empty.
278 // @see https://www.sqlite.org/foreignkeys.html#fk_indexes.
279 $foreignTableIndexes = $this->_getPortableTableIndexesList([], $value['table']);
280 if (!isset($foreignTableIndexes['primary'])) {
281 continue;
282 }
283 $list[$id]['foreign'] = [...$list[$id]['foreign'], ...$foreignTableIndexes['primary']->getColumns()];
284 continue;
285 }
286 $list[$id]['foreign'][] = $value['to'];
287 }
288 return parent::_getPortableTableForeignKeysList($list);
289 }
290 protected function _getPortableTableForeignKeyDefinition($tableForeignKey) : ForeignKeyConstraint
291 {
292 return new ForeignKeyConstraint($tableForeignKey['local'], $tableForeignKey['foreignTable'], $tableForeignKey['foreign'], $tableForeignKey['name'], ['onDelete' => $tableForeignKey['onDelete'], 'onUpdate' => $tableForeignKey['onUpdate'], 'deferrable' => $tableForeignKey['deferrable'], 'deferred' => $tableForeignKey['deferred']]);
293 }
294 private function parseColumnCollationFromSQL(string $column, string $sql) : ?string
295 {
296 $pattern = '{' . $this->buildIdentifierPattern($column) . '[^,(]+(?:\\([^()]+\\)[^,]*)?(?:(?:DEFAULT|CHECK)\\s*(?:\\(.*?\\))?[^,]*)*COLLATE\\s+["\']?([^\\s,"\')]+)}is';
297 if (preg_match($pattern, $sql, $match) !== 1) {
298 return null;
299 }
300 return $match[1];
301 }
302 private function parseTableCommentFromSQL(string $table, string $sql) : ?string
303 {
304 $pattern = '/\\s* # Allow whitespace characters at start of line
305 CREATE\\sTABLE' . $this->buildIdentifierPattern($table) . '
306 ( # Start capture
307 (?:\\s*--[^\\n]*\\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s)
308 )/ix';
309 if (preg_match($pattern, $sql, $match) !== 1) {
310 return null;
311 }
312 $comment = preg_replace('{^\\s*--}m', '', rtrim($match[1], "\n"));
313 return $comment === '' ? null : $comment;
314 }
315 private function parseColumnCommentFromSQL(string $column, string $sql) : ?string
316 {
317 $pattern = '{[\\s(,]' . $this->buildIdentifierPattern($column) . '(?:\\([^)]*?\\)|[^,(])*?,?((?:(?!\\n))(?:\\s*--[^\\n]*\\n?)+)}i';
318 if (preg_match($pattern, $sql, $match) !== 1) {
319 return null;
320 }
321 $comment = preg_replace('{^\\s*--}m', '', rtrim($match[1], "\n"));
322 return $comment === '' ? null : $comment;
323 }
324 private function buildIdentifierPattern(string $identifier) : string
325 {
326 return '(?:' . implode('|', array_map(static function (string $sql) : string {
327 return '\\W' . preg_quote($sql, '/') . '\\W';
328 }, [$identifier, $this->_platform->quoteSingleIdentifier($identifier)])) . ')';
329 }
330 private function getCreateTableSQL(string $table) : string
331 {
332 $sql = $this->_conn->fetchOne(<<<'SQL'
333 SELECT sql
334 FROM (
335 SELECT *
336 FROM sqlite_master
337 UNION ALL
338 SELECT *
339 FROM sqlite_temp_master
340 )
341 WHERE type = 'table'
342 AND name = ?
343 SQL
344 , [$table]);
345 if ($sql !== \false) {
346 return $sql;
347 }
348 return '';
349 }
350 private function addDetailsToTableForeignKeyColumns(string $table, array $columns) : array
351 {
352 $foreignKeyDetails = $this->getForeignKeyDetails($table);
353 $foreignKeyCount = count($foreignKeyDetails);
354 foreach ($columns as $i => $column) {
355 // SQLite identifies foreign keys in reverse order of appearance in SQL
356 $columns[$i] = array_merge($column, $foreignKeyDetails[$foreignKeyCount - $column['id'] - 1]);
357 }
358 return $columns;
359 }
360 private function getForeignKeyDetails($table)
361 {
362 $createSql = $this->getCreateTableSQL($table);
363 if (preg_match_all('#
364 (?:CONSTRAINT\\s+(\\S+)\\s+)?
365 (?:FOREIGN\\s+KEY[^)]+\\)\\s*)?
366 REFERENCES\\s+\\S+\\s*(?:\\([^)]+\\))?
367 (?:
368 [^,]*?
369 (NOT\\s+DEFERRABLE|DEFERRABLE)
370 (?:\\s+INITIALLY\\s+(DEFERRED|IMMEDIATE))?
371 )?#isx', $createSql, $match) === 0) {
372 return [];
373 }
374 $names = $match[1];
375 $deferrable = $match[2];
376 $deferred = $match[3];
377 $details = [];
378 for ($i = 0, $count = count($match[0]); $i < $count; $i++) {
379 $details[] = ['constraint_name' => isset($names[$i]) && $names[$i] !== '' ? $names[$i] : null, 'deferrable' => isset($deferrable[$i]) && strcasecmp($deferrable[$i], 'deferrable') === 0, 'deferred' => isset($deferred[$i]) && strcasecmp($deferred[$i], 'deferred') === 0];
380 }
381 return $details;
382 }
383 public function createComparator() : Comparator
384 {
385 return new SQLite\Comparator($this->_platform);
386 }
387 public function getSchemaSearchPaths()
388 {
389 Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', 'SqliteSchemaManager::getSchemaSearchPaths() is deprecated.');
390 // SQLite does not support schemas or databases
391 return [];
392 }
393 protected function selectTableNames(string $databaseName) : Result
394 {
395 $sql = <<<'SQL'
396 SELECT name AS table_name
397 FROM sqlite_master
398 WHERE type = 'table'
399 AND name != 'sqlite_sequence'
400 AND name != 'geometry_columns'
401 AND name != 'spatial_ref_sys'
402 UNION ALL
403 SELECT name
404 FROM sqlite_temp_master
405 WHERE type = 'table'
406 ORDER BY name
407 SQL;
408 return $this->_conn->executeQuery($sql);
409 }
410 protected function selectTableColumns(string $databaseName, ?string $tableName = null) : Result
411 {
412 $sql = <<<'SQL'
413 SELECT t.name AS table_name,
414 c.*
415 FROM sqlite_master t
416 JOIN pragma_table_info(t.name) c
417 SQL;
418 $conditions = ["t.type = 'table'", "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')"];
419 $params = [];
420 if ($tableName !== null) {
421 $conditions[] = 't.name = ?';
422 $params[] = $this->_platform->canEmulateSchemas() ? str_replace('.', '__', $tableName) : $tableName;
423 }
424 $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, c.cid';
425 return $this->_conn->executeQuery($sql, $params);
426 }
427 protected function selectIndexColumns(string $databaseName, ?string $tableName = null) : Result
428 {
429 $sql = <<<'SQL'
430 SELECT t.name AS table_name,
431 i.*
432 FROM sqlite_master t
433 JOIN pragma_index_list(t.name) i
434 SQL;
435 $conditions = ["t.type = 'table'", "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')"];
436 $params = [];
437 if ($tableName !== null) {
438 $conditions[] = 't.name = ?';
439 $params[] = $this->_platform->canEmulateSchemas() ? str_replace('.', '__', $tableName) : $tableName;
440 }
441 $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, i.seq';
442 return $this->_conn->executeQuery($sql, $params);
443 }
444 protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null) : Result
445 {
446 $sql = <<<'SQL'
447 SELECT t.name AS table_name,
448 p.*
449 FROM sqlite_master t
450 JOIN pragma_foreign_key_list(t.name) p
451 ON p."seq" != '-1'
452 SQL;
453 $conditions = ["t.type = 'table'", "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')"];
454 $params = [];
455 if ($tableName !== null) {
456 $conditions[] = 't.name = ?';
457 $params[] = $this->_platform->canEmulateSchemas() ? str_replace('.', '__', $tableName) : $tableName;
458 }
459 $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, p.id DESC, p.seq';
460 return $this->_conn->executeQuery($sql, $params);
461 }
462 protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null) : array
463 {
464 if ($tableName === null) {
465 $tables = $this->listTableNames();
466 } else {
467 $tables = [$tableName];
468 }
469 $tableOptions = [];
470 foreach ($tables as $table) {
471 $comment = $this->parseTableCommentFromSQL($table, $this->getCreateTableSQL($table));
472 if ($comment === null) {
473 continue;
474 }
475 $tableOptions[$table]['comment'] = $comment;
476 }
477 return $tableOptions;
478 }
479 }
480