Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
81.21% |
281 / 346 |
|
38.46% |
15 / 39 |
CRAP | |
0.00% |
0 / 1 |
doctrine | |
81.21% |
281 / 346 |
|
38.46% |
15 / 39 |
185.33 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
get_schema_manager | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
get_schema | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
sql_list_tables | |
50.00% |
2 / 4 |
|
0.00% |
0 / 1 |
2.50 | |||
sql_table_exists | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
3.19 | |||
sql_list_columns | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
3.19 | |||
sql_column_exists | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
3.19 | |||
sql_list_index | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
sql_index_exists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
sql_unique_index_exists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
perform_schema_changes | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
2.01 | |||
sql_create_table | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
sql_table_drop | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
sql_column_add | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
sql_column_change | |
95.83% |
23 / 24 |
|
0.00% |
0 / 1 |
6 | |||
sql_column_remove | |
95.24% |
20 / 21 |
|
0.00% |
0 / 1 |
4 | |||
sql_create_index | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
sql_index_drop | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
sql_create_unique_index | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
sql_create_primary_key | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
sql_truncate_table | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
3.19 | |||
get_filtered_index_list | |
83.33% |
10 / 12 |
|
0.00% |
0 / 1 |
3.04 | |||
get_asset_names | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
asset_exists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
alter_schema | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
4.01 | |||
alter_table | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
schema_perform_changes | |
100.00% |
55 / 55 |
|
100.00% |
1 / 1 |
9 | |||
schema_create_table | |
90.32% |
28 / 31 |
|
0.00% |
0 / 1 |
12.13 | |||
schema_drop_table | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
3.33 | |||
schema_column_add | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
3.00 | |||
schema_column_change | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
3.00 | |||
schema_column_change_add | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
schema_column_remove | |
85.00% |
17 / 20 |
|
0.00% |
0 / 1 |
8.22 | |||
schema_create_index | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
4.13 | |||
schema_create_unique_index | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
4.13 | |||
schema_index_drop | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
schema_create_primary_key | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
recreate_index | |
57.14% |
12 / 21 |
|
0.00% |
0 / 1 |
6.97 | |||
isSequenceAutoIncrementsFor | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | namespace phpbb\db\tools; |
15 | |
16 | use Doctrine\DBAL\Connection; |
17 | use Doctrine\DBAL\Exception; |
18 | use Doctrine\DBAL\Schema\AbstractAsset; |
19 | use Doctrine\DBAL\Schema\AbstractSchemaManager; |
20 | use Doctrine\DBAL\Schema\Index; |
21 | use Doctrine\DBAL\Schema\Schema; |
22 | use Doctrine\DBAL\Schema\SchemaException; |
23 | use Doctrine\DBAL\Schema\Sequence; |
24 | use Doctrine\DBAL\Schema\Table; |
25 | use Doctrine\DBAL\Types\Type; |
26 | use phpbb\db\doctrine\comparator; |
27 | use phpbb\db\doctrine\table_helper; |
28 | |
29 | /** |
30 | * BC layer for database tools. |
31 | * |
32 | * In general, it is recommended to use Doctrine directly instead of this class as this |
33 | * implementation is only a BC layer. |
34 | */ |
35 | class doctrine implements tools_interface |
36 | { |
37 | /** |
38 | * @var AbstractSchemaManager |
39 | */ |
40 | private $schema_manager; |
41 | |
42 | /** |
43 | * @var Connection |
44 | */ |
45 | private $connection; |
46 | |
47 | /** |
48 | * @var bool |
49 | */ |
50 | private $return_statements; |
51 | |
52 | /** |
53 | * Database tools constructors. |
54 | * |
55 | * @param Connection $connection |
56 | * @param bool $return_statements |
57 | */ |
58 | public function __construct(Connection $connection, bool $return_statements = false) |
59 | { |
60 | $this->return_statements = $return_statements; |
61 | $this->connection = $connection; |
62 | } |
63 | |
64 | /** |
65 | * @return AbstractSchemaManager |
66 | * |
67 | * @throws Exception |
68 | */ |
69 | protected function get_schema_manager(): AbstractSchemaManager |
70 | { |
71 | if ($this->schema_manager == null) |
72 | { |
73 | $this->schema_manager = $this->connection->createSchemaManager(); |
74 | } |
75 | |
76 | return $this->schema_manager; |
77 | } |
78 | |
79 | /** |
80 | * @return Schema |
81 | * |
82 | * @throws Exception |
83 | */ |
84 | protected function get_schema(): Schema |
85 | { |
86 | return $this->get_schema_manager()->createSchema(); |
87 | } |
88 | |
89 | /** |
90 | * {@inheritDoc} |
91 | */ |
92 | public function sql_list_tables(): array |
93 | { |
94 | try |
95 | { |
96 | $tables = array_map('strtolower', $this->get_schema_manager()->listTableNames()); |
97 | return array_combine($tables, $tables); |
98 | } |
99 | catch (Exception $e) |
100 | { |
101 | return []; |
102 | } |
103 | } |
104 | |
105 | /** |
106 | * {@inheritDoc} |
107 | */ |
108 | public function sql_table_exists(string $table_name): bool |
109 | { |
110 | try |
111 | { |
112 | return $this->get_schema_manager()->tablesExist([$table_name]); |
113 | } |
114 | catch (Exception $e) |
115 | { |
116 | return false; |
117 | } |
118 | } |
119 | |
120 | /** |
121 | * {@inheritDoc} |
122 | */ |
123 | public function sql_list_columns(string $table_name): array |
124 | { |
125 | try |
126 | { |
127 | return $this->get_asset_names($this->get_schema_manager()->listTableColumns($table_name)); |
128 | } |
129 | catch (Exception $e) |
130 | { |
131 | return []; |
132 | } |
133 | } |
134 | |
135 | /** |
136 | * {@inheritDoc} |
137 | */ |
138 | public function sql_column_exists(string $table_name, string $column_name): bool |
139 | { |
140 | try |
141 | { |
142 | return $this->asset_exists($column_name, $this->get_schema_manager()->listTableColumns($table_name)); |
143 | } |
144 | catch (Exception $e) |
145 | { |
146 | return false; |
147 | } |
148 | } |
149 | |
150 | /** |
151 | * {@inheritDoc} |
152 | */ |
153 | public function sql_list_index(string $table_name): array |
154 | { |
155 | return $this->get_asset_names($this->get_filtered_index_list($table_name, true)); |
156 | } |
157 | |
158 | /** |
159 | * {@inheritDoc} |
160 | */ |
161 | public function sql_index_exists(string $table_name, string $index_name): bool |
162 | { |
163 | return $this->asset_exists($index_name, $this->get_filtered_index_list($table_name, true)); |
164 | } |
165 | |
166 | /** |
167 | * {@inheritDoc} |
168 | */ |
169 | public function sql_unique_index_exists(string $table_name, string $index_name): bool |
170 | { |
171 | return $this->asset_exists($index_name, $this->get_filtered_index_list($table_name, false)); |
172 | } |
173 | |
174 | /** |
175 | * {@inheritDoc} |
176 | */ |
177 | public function perform_schema_changes(array $schema_changes) |
178 | { |
179 | if (empty($schema_changes)) |
180 | { |
181 | return true; |
182 | } |
183 | |
184 | return $this->alter_schema( |
185 | function (Schema $schema) use ($schema_changes): void |
186 | { |
187 | $this->schema_perform_changes($schema, $schema_changes); |
188 | } |
189 | ); |
190 | } |
191 | |
192 | /** |
193 | * {@inheritDoc} |
194 | */ |
195 | public function sql_create_table(string $table_name, array $table_data) |
196 | { |
197 | return $this->alter_schema( |
198 | function (Schema $schema) use ($table_name, $table_data): void |
199 | { |
200 | $this->schema_create_table($schema, $table_name, $table_data, true); |
201 | } |
202 | ); |
203 | } |
204 | |
205 | /** |
206 | * {@inheritDoc} |
207 | */ |
208 | public function sql_table_drop(string $table_name) |
209 | { |
210 | return $this->alter_schema( |
211 | function (Schema $schema) use ($table_name): void |
212 | { |
213 | $this->schema_drop_table($schema, $table_name, true); |
214 | } |
215 | ); |
216 | } |
217 | |
218 | /** |
219 | * {@inheritDoc} |
220 | */ |
221 | public function sql_column_add(string $table_name, string $column_name, array $column_data) |
222 | { |
223 | return $this->alter_schema( |
224 | function (Schema $schema) use ($table_name, $column_name, $column_data): void |
225 | { |
226 | $this->schema_column_add($schema, $table_name, $column_name, $column_data); |
227 | } |
228 | ); |
229 | } |
230 | |
231 | /** |
232 | * {@inheritDoc} |
233 | */ |
234 | public function sql_column_change(string $table_name, string $column_name, array $column_data) |
235 | { |
236 | $column_indexes = $this->get_filtered_index_list($table_name, true); |
237 | |
238 | $column_indexes = array_filter($column_indexes, function($index) use ($column_name) { |
239 | $index_columns = array_map('strtolower', $index->getUnquotedColumns()); |
240 | return in_array($column_name, $index_columns, true); |
241 | }); |
242 | |
243 | if (count($column_indexes)) |
244 | { |
245 | $ret = $this->alter_schema( |
246 | function (Schema $schema) use ($table_name, $column_name, $column_data, $column_indexes): void |
247 | { |
248 | foreach ($column_indexes as $index) |
249 | { |
250 | $this->schema_index_drop($schema, $table_name, $index->getName()); |
251 | } |
252 | } |
253 | ); |
254 | |
255 | if ($ret !== true) |
256 | { |
257 | return $ret; |
258 | } |
259 | } |
260 | |
261 | return $this->alter_schema( |
262 | function (Schema $schema) use ($table_name, $column_name, $column_data, $column_indexes): void |
263 | { |
264 | $this->schema_column_change($schema, $table_name, $column_name, $column_data); |
265 | |
266 | if (count($column_indexes)) |
267 | { |
268 | foreach ($column_indexes as $index) |
269 | { |
270 | $this->schema_create_index($schema, $table_name, $index->getName(), $index->getColumns()); |
271 | } |
272 | } |
273 | } |
274 | ); |
275 | } |
276 | |
277 | /** |
278 | * {@inheritDoc} |
279 | */ |
280 | public function sql_column_remove(string $table_name, string $column_name) |
281 | { |
282 | // Check if this column is part of a primary key. If yes, remove the primary key. |
283 | $primary_key_indexes = $this->get_filtered_index_list($table_name, false); |
284 | |
285 | $primary_key_indexes = array_filter($primary_key_indexes, function($index) use ($column_name) { |
286 | $index_columns = array_map('strtolower', $index->getUnquotedColumns()); |
287 | return in_array($column_name, $index_columns, true) && $index->isPrimary(); |
288 | }); |
289 | |
290 | if (count($primary_key_indexes)) |
291 | { |
292 | $ret = $this->alter_schema( |
293 | function (Schema $schema) use ($table_name, $column_name): void |
294 | { |
295 | $table = $schema->getTable($table_name); |
296 | $table->dropPrimaryKey(); |
297 | } |
298 | ); |
299 | |
300 | if ($ret !== true) |
301 | { |
302 | return $ret; |
303 | } |
304 | } |
305 | |
306 | return $this->alter_schema( |
307 | function (Schema $schema) use ($table_name, $column_name): void |
308 | { |
309 | $this->schema_column_remove($schema, $table_name, $column_name); |
310 | } |
311 | ); |
312 | } |
313 | |
314 | /** |
315 | * {@inheritDoc} |
316 | */ |
317 | public function sql_create_index(string $table_name, string $index_name, $column) |
318 | { |
319 | return $this->alter_schema( |
320 | function (Schema $schema) use ($table_name, $index_name, $column): void |
321 | { |
322 | $this->schema_create_index($schema, $table_name, $index_name, $column); |
323 | } |
324 | ); |
325 | } |
326 | |
327 | /** |
328 | * {@inheritDoc} |
329 | */ |
330 | public function sql_index_drop(string $table_name, string $index_name) |
331 | { |
332 | return $this->alter_schema( |
333 | function (Schema $schema) use ($table_name, $index_name): void |
334 | { |
335 | $this->schema_index_drop($schema, $table_name, $index_name); |
336 | } |
337 | ); |
338 | } |
339 | |
340 | /** |
341 | * {@inheritDoc} |
342 | */ |
343 | public function sql_create_unique_index(string $table_name, string $index_name, $column) |
344 | { |
345 | return $this->alter_schema( |
346 | function (Schema $schema) use ($table_name, $index_name, $column): void |
347 | { |
348 | $this->schema_create_unique_index($schema, $table_name, $index_name, $column); |
349 | } |
350 | ); |
351 | } |
352 | |
353 | /** |
354 | * {@inheritDoc} |
355 | */ |
356 | public function sql_create_primary_key(string $table_name, $column) |
357 | { |
358 | return $this->alter_schema( |
359 | function (Schema $schema) use ($table_name, $column): void |
360 | { |
361 | $this->schema_create_primary_key($schema, $column, $table_name); |
362 | } |
363 | ); |
364 | } |
365 | |
366 | /** |
367 | * {@inheritDoc} |
368 | */ |
369 | public function sql_truncate_table(string $table_name): void |
370 | { |
371 | try |
372 | { |
373 | $this->connection->executeQuery($this->get_schema_manager()->getDatabasePlatform()->getTruncateTableSQL($table_name)); |
374 | } |
375 | catch (Exception $e) |
376 | { |
377 | return; |
378 | } |
379 | } |
380 | |
381 | /** |
382 | * Returns an array of indices for either unique and primary keys, or simple indices. |
383 | * |
384 | * @param string $table_name The name of the table. |
385 | * @param bool $is_non_unique Whether to return simple indices or primary and unique ones. |
386 | * |
387 | * @return Index[] The filtered index array. |
388 | */ |
389 | protected function get_filtered_index_list(string $table_name, bool $is_non_unique): array |
390 | { |
391 | try |
392 | { |
393 | $indices = $this->get_schema_manager()->listTableIndexes($table_name); |
394 | } |
395 | catch (Exception $e) |
396 | { |
397 | return []; |
398 | } |
399 | |
400 | if ($is_non_unique) |
401 | { |
402 | return array_filter($indices, function (Index $index) |
403 | { |
404 | return $index->isSimpleIndex(); |
405 | }); |
406 | } |
407 | |
408 | return array_filter($indices, function (Index $index) |
409 | { |
410 | return !$index->isSimpleIndex(); |
411 | }); |
412 | } |
413 | |
414 | /** |
415 | * Returns an array of lowercase asset names. |
416 | * |
417 | * @param array $assets Array of assets. |
418 | * |
419 | * @return array An array of lowercase asset names. |
420 | */ |
421 | protected function get_asset_names(array $assets): array |
422 | { |
423 | return array_map( |
424 | function (AbstractAsset $asset) |
425 | { |
426 | return strtolower($asset->getName()); |
427 | }, |
428 | $assets |
429 | ); |
430 | } |
431 | |
432 | /** |
433 | * Returns whether an asset name exists in a list of assets (case insensitive). |
434 | * |
435 | * @param string $needle The asset name to search for. |
436 | * @param array $assets The array of assets. |
437 | * |
438 | * @return bool Whether the asset name exists in a list of assets. |
439 | */ |
440 | protected function asset_exists(string $needle, array $assets): bool |
441 | { |
442 | return in_array(strtolower($needle), $this->get_asset_names($assets), true); |
443 | } |
444 | |
445 | /** |
446 | * Alter the current database representation using a callback and execute the changes. |
447 | * Returns false in case of error. |
448 | * |
449 | * @param callable $callback Callback taking the schema as parameters and returning it altered (or null in case of error) |
450 | * |
451 | * @return bool|string[] |
452 | */ |
453 | protected function alter_schema(callable $callback) |
454 | { |
455 | try |
456 | { |
457 | $current_schema = $this->get_schema(); |
458 | $new_schema = clone $current_schema; |
459 | call_user_func($callback, $new_schema); |
460 | |
461 | $comparator = new comparator(); |
462 | $schemaDiff = $comparator->compareSchemas($current_schema, $new_schema); |
463 | $queries = $schemaDiff->toSql($this->get_schema_manager()->getDatabasePlatform()); |
464 | |
465 | if ($this->return_statements) |
466 | { |
467 | return $queries; |
468 | } |
469 | |
470 | foreach ($queries as $query) |
471 | { |
472 | // executeQuery() must be used here because $query might return a result set, for instance REPAIR does |
473 | $this->connection->executeQuery($query); |
474 | } |
475 | |
476 | return true; |
477 | } |
478 | catch (Exception $e) |
479 | { |
480 | // @todo: check if it makes sense to properly handle the exception |
481 | return false; |
482 | } |
483 | } |
484 | |
485 | /** |
486 | * Alter table. |
487 | * |
488 | * @param string $table_name Table name. |
489 | * @param callable $callback Callback function to modify the table. |
490 | * |
491 | * @throws SchemaException |
492 | */ |
493 | protected function alter_table(Schema $schema, string $table_name, callable $callback): void |
494 | { |
495 | $table = $schema->getTable($table_name); |
496 | call_user_func($callback, $table); |
497 | } |
498 | |
499 | /** |
500 | * Perform schema changes |
501 | * |
502 | * @param Schema $schema |
503 | * @param array $schema_changes |
504 | */ |
505 | protected function schema_perform_changes(Schema $schema, array $schema_changes): void |
506 | { |
507 | $mapping = [ |
508 | 'drop_tables' => [ |
509 | 'method' => 'schema_drop_table', |
510 | 'use_key' => false, |
511 | ], |
512 | 'add_tables' => [ |
513 | 'method' => 'schema_create_table', |
514 | 'use_key' => true, |
515 | ], |
516 | 'change_columns' => [ |
517 | 'method' => 'schema_column_change_add', |
518 | 'use_key' => true, |
519 | 'per_table' => true, |
520 | ], |
521 | 'add_columns' => [ |
522 | 'method' => 'schema_column_add', |
523 | 'use_key' => true, |
524 | 'per_table' => true, |
525 | ], |
526 | 'drop_columns' => [ |
527 | 'method' => 'schema_column_remove', |
528 | 'use_key' => false, |
529 | 'per_table' => true, |
530 | ], |
531 | 'drop_keys' => [ |
532 | 'method' => 'schema_index_drop', |
533 | 'use_key' => false, |
534 | 'per_table' => true, |
535 | ], |
536 | 'add_primary_keys' => [ |
537 | 'method' => 'schema_create_primary_key', |
538 | 'use_key' => true, |
539 | ], |
540 | 'add_unique_index' => [ |
541 | 'method' => 'schema_create_unique_index', |
542 | 'use_key' => true, |
543 | 'per_table' => true, |
544 | ], |
545 | 'add_index' => [ |
546 | 'method' => 'schema_create_index', |
547 | 'use_key' => true, |
548 | 'per_table' => true, |
549 | ], |
550 | ]; |
551 | |
552 | foreach ($mapping as $action => $params) |
553 | { |
554 | if (array_key_exists($action, $schema_changes)) |
555 | { |
556 | foreach ($schema_changes[$action] as $table_name => $table_data) |
557 | { |
558 | if (array_key_exists('per_table', $params) && $params['per_table']) |
559 | { |
560 | foreach ($table_data as $key => $data) |
561 | { |
562 | if ($params['use_key'] == false) |
563 | { |
564 | $this->{$params['method']}($schema, $table_name, $data, true); |
565 | } |
566 | else |
567 | { |
568 | $this->{$params['method']}($schema, $table_name, $key, $data, true); |
569 | } |
570 | } |
571 | } |
572 | else |
573 | { |
574 | if ($params['use_key'] == false) |
575 | { |
576 | $this->{$params['method']}($schema, $table_data, true); |
577 | } |
578 | else |
579 | { |
580 | $this->{$params['method']}($schema, $table_name, $table_data, true); |
581 | } |
582 | } |
583 | } |
584 | } |
585 | } |
586 | } |
587 | |
588 | /** |
589 | * Update the schema representation with a new table. |
590 | * Returns null in case of errors |
591 | * |
592 | * @param Schema $schema |
593 | * @param string $table_name |
594 | * @param array $table_data |
595 | * @param bool $safe_check |
596 | * |
597 | * @throws SchemaException |
598 | */ |
599 | protected function schema_create_table(Schema $schema, string $table_name, array $table_data, bool $safe_check = false): void |
600 | { |
601 | if ($safe_check && $this->sql_table_exists($table_name)) |
602 | { |
603 | return; |
604 | } |
605 | |
606 | $table = $schema->createTable($table_name); |
607 | $dbms_name = $this->get_schema_manager()->getDatabasePlatform()->getName(); |
608 | |
609 | foreach ($table_data['COLUMNS'] as $column_name => $column_data) |
610 | { |
611 | list($type, $options) = table_helper::convert_column_data( |
612 | $column_data, |
613 | $dbms_name |
614 | ); |
615 | $table->addColumn($column_name, $type, $options); |
616 | } |
617 | |
618 | if (array_key_exists('PRIMARY_KEY', $table_data)) |
619 | { |
620 | $table_data['PRIMARY_KEY'] = (!is_array($table_data['PRIMARY_KEY'])) |
621 | ? [$table_data['PRIMARY_KEY']] |
622 | : $table_data['PRIMARY_KEY']; |
623 | |
624 | $table->setPrimaryKey($table_data['PRIMARY_KEY']); |
625 | } |
626 | |
627 | if (array_key_exists('KEYS', $table_data)) |
628 | { |
629 | foreach ($table_data['KEYS'] as $key_name => $key_data) |
630 | { |
631 | $columns = (is_array($key_data[1])) ? $key_data[1] : [$key_data[1]]; |
632 | |
633 | // Supports key columns defined with there length |
634 | $columns = array_map(function (string $column) |
635 | { |
636 | if (strpos($column, ':') !== false) |
637 | { |
638 | $parts = explode(':', $column, 2); |
639 | return $parts[0]; |
640 | } |
641 | return $column; |
642 | }, $columns); |
643 | |
644 | if ($key_data[0] === 'UNIQUE') |
645 | { |
646 | $table->addUniqueIndex($columns, $key_name); |
647 | } |
648 | else |
649 | { |
650 | $table->addIndex($columns, $key_name); |
651 | } |
652 | } |
653 | } |
654 | |
655 | switch ($dbms_name) |
656 | { |
657 | case 'mysql': |
658 | $table->addOption('collate', 'utf8_bin'); |
659 | break; |
660 | } |
661 | } |
662 | |
663 | /** |
664 | * @param Schema $schema |
665 | * @param string $table_name |
666 | * @param bool $safe_check |
667 | * |
668 | * @throws SchemaException |
669 | */ |
670 | protected function schema_drop_table(Schema $schema, string $table_name, bool $safe_check = false): void |
671 | { |
672 | if ($safe_check && !$schema->hasTable($table_name)) |
673 | { |
674 | return; |
675 | } |
676 | |
677 | $schema->dropTable($table_name); |
678 | } |
679 | |
680 | /** |
681 | * @param Schema $schema |
682 | * @param string $table_name |
683 | * @param string $column_name |
684 | * @param array $column_data |
685 | * @param bool $safe_check |
686 | * |
687 | * @throws SchemaException |
688 | */ |
689 | protected function schema_column_add(Schema $schema, string $table_name, string $column_name, array $column_data, bool $safe_check = false): void |
690 | { |
691 | $this->alter_table( |
692 | $schema, |
693 | $table_name, |
694 | function (Table $table) use ($column_name, $column_data, $safe_check) |
695 | { |
696 | if ($safe_check && $table->hasColumn($column_name)) |
697 | { |
698 | return false; |
699 | } |
700 | |
701 | $dbms_name = $this->get_schema_manager()->getDatabasePlatform()->getName(); |
702 | |
703 | list($type, $options) = table_helper::convert_column_data($column_data, $dbms_name); |
704 | $table->addColumn($column_name, $type, $options); |
705 | return $table; |
706 | } |
707 | ); |
708 | } |
709 | |
710 | /** |
711 | * @param Schema $schema |
712 | * @param string $table_name |
713 | * @param string $column_name |
714 | * @param array $column_data |
715 | * @param bool $safe_check |
716 | * |
717 | * @throws SchemaException |
718 | */ |
719 | protected function schema_column_change(Schema $schema, string $table_name, string $column_name, array $column_data, bool $safe_check = false): void |
720 | { |
721 | $this->alter_table( |
722 | $schema, |
723 | $table_name, |
724 | function (Table $table) use ($column_name, $column_data, $safe_check): void |
725 | { |
726 | if ($safe_check && !$table->hasColumn($column_name)) |
727 | { |
728 | return; |
729 | } |
730 | |
731 | $dbms_name = $this->get_schema_manager()->getDatabasePlatform()->getName(); |
732 | |
733 | list($type, $options) = table_helper::convert_column_data($column_data, $dbms_name); |
734 | $options['type'] = Type::getType($type); |
735 | $table->changeColumn($column_name, $options); |
736 | } |
737 | ); |
738 | } |
739 | |
740 | /** |
741 | * @param Schema $schema |
742 | * @param string $table_name |
743 | * @param string $column_name |
744 | * @param array $column_data |
745 | * @param bool $safe_check |
746 | * |
747 | * @throws SchemaException |
748 | */ |
749 | protected function schema_column_change_add(Schema $schema, string $table_name, string $column_name, array $column_data, bool $safe_check = false): void |
750 | { |
751 | $table = $schema->getTable($table_name); |
752 | if ($table->hasColumn($column_name)) |
753 | { |
754 | $this->schema_column_change($schema, $table_name, $column_name, $column_data, $safe_check); |
755 | } |
756 | else |
757 | { |
758 | $this->schema_column_add($schema, $table_name, $column_name, $column_data, $safe_check); |
759 | } |
760 | } |
761 | |
762 | /** |
763 | * @param Schema $schema |
764 | * @param string $table_name |
765 | * @param string $column_name |
766 | * @param bool $safe_check |
767 | * |
768 | * @throws SchemaException |
769 | */ |
770 | protected function schema_column_remove(Schema $schema, string $table_name, string $column_name, bool $safe_check = false): void |
771 | { |
772 | $this->alter_table( |
773 | $schema, |
774 | $table_name, |
775 | function (Table $table) use ($schema, $table_name, $column_name, $safe_check): void |
776 | { |
777 | if ($safe_check && !$table->hasColumn($column_name)) |
778 | { |
779 | return; |
780 | } |
781 | |
782 | /* |
783 | * As our sequences does not have the same name as these generated |
784 | * by default by doctrine or the DBMS, we have to manage them ourselves. |
785 | */ |
786 | if ($table->getColumn($column_name)->getAutoincrement()) |
787 | { |
788 | foreach ($schema->getSequences() as $sequence) |
789 | { |
790 | if ($this->isSequenceAutoIncrementsFor($sequence, $table)) |
791 | { |
792 | $schema->dropSequence($sequence->getName()); |
793 | } |
794 | } |
795 | } |
796 | |
797 | // Re-create / delete the indices using this column |
798 | foreach ($table->getIndexes() as $index) |
799 | { |
800 | $index_columns = array_map('strtolower', $index->getUnquotedColumns()); |
801 | $key = array_search($column_name, $index_columns, true); |
802 | if ($key !== false) |
803 | { |
804 | unset($index_columns[$key]); |
805 | $this->recreate_index($table, $index, $index_columns); |
806 | } |
807 | } |
808 | |
809 | $table->dropColumn($column_name); |
810 | } |
811 | ); |
812 | } |
813 | |
814 | /** |
815 | * @param Schema $schema |
816 | * @param string $table_name |
817 | * @param string $index_name |
818 | * @param string|array $column |
819 | * @param bool $safe_check |
820 | * |
821 | * @throws SchemaException |
822 | */ |
823 | protected function schema_create_index(Schema $schema, string $table_name, string $index_name, $column, bool $safe_check = false): void |
824 | { |
825 | $columns = (is_array($column)) ? $column : [$column]; |
826 | $table = $schema->getTable($table_name); |
827 | |
828 | if ($safe_check && $table->hasIndex($index_name)) |
829 | { |
830 | return; |
831 | } |
832 | |
833 | $table->addIndex($columns, $index_name); |
834 | } |
835 | |
836 | /** |
837 | * @param Schema $schema |
838 | * @param string $table_name |
839 | * @param string $index_name |
840 | * @param string|array $column |
841 | * @param bool $safe_check |
842 | * |
843 | * @throws SchemaException |
844 | */ |
845 | protected function schema_create_unique_index(Schema $schema, string $table_name, string $index_name, $column, bool $safe_check = false): void |
846 | { |
847 | $columns = (is_array($column)) ? $column : [$column]; |
848 | $table = $schema->getTable($table_name); |
849 | |
850 | if ($safe_check && $table->hasIndex($index_name)) |
851 | { |
852 | return; |
853 | } |
854 | |
855 | $table->addUniqueIndex($columns, $index_name); |
856 | } |
857 | |
858 | /** |
859 | * @param Schema $schema |
860 | * @param string $table_name |
861 | * @param string $index_name |
862 | * @param bool $safe_check |
863 | * |
864 | * @throws SchemaException |
865 | */ |
866 | protected function schema_index_drop(Schema $schema, string $table_name, string $index_name, bool $safe_check = false): void |
867 | { |
868 | $table = $schema->getTable($table_name); |
869 | |
870 | if ($safe_check && !$table->hasIndex($index_name)) |
871 | { |
872 | return; |
873 | } |
874 | |
875 | $table->dropIndex($index_name); |
876 | } |
877 | |
878 | /** |
879 | * @param $column |
880 | * @param Schema $schema |
881 | * @param string $table_name |
882 | * @param bool $safe_check |
883 | * |
884 | * @throws SchemaException |
885 | */ |
886 | protected function schema_create_primary_key(Schema $schema, $column, string $table_name, bool $safe_check = false): void |
887 | { |
888 | $columns = (is_array($column)) ? $column : [$column]; |
889 | $table = $schema->getTable($table_name); |
890 | $table->dropPrimaryKey(); |
891 | $table->setPrimaryKey($columns); |
892 | } |
893 | |
894 | /** |
895 | * Recreate an index of a table |
896 | * |
897 | * @param Table $table |
898 | * @param Index $index |
899 | * @param array Columns to use in the new (recreated) index |
900 | * |
901 | * @throws SchemaException |
902 | */ |
903 | protected function recreate_index(Table $table, Index $index, array $new_columns): void |
904 | { |
905 | if ($index->isPrimary()) |
906 | { |
907 | $table->dropPrimaryKey(); |
908 | } |
909 | else |
910 | { |
911 | $table->dropIndex($index->getName()); |
912 | } |
913 | |
914 | if (count($new_columns) > 0) |
915 | { |
916 | if ($index->isPrimary()) |
917 | { |
918 | $table->setPrimaryKey( |
919 | $new_columns, |
920 | $index->getName(), |
921 | ); |
922 | } |
923 | else if ($index->isUnique()) |
924 | { |
925 | $table->addUniqueIndex( |
926 | $new_columns, |
927 | $index->getName(), |
928 | $index->getOptions(), |
929 | ); |
930 | } |
931 | else |
932 | { |
933 | $table->addIndex( |
934 | $new_columns, |
935 | $index->getName(), |
936 | $index->getFlags(), |
937 | $index->getOptions(), |
938 | ); |
939 | } |
940 | } |
941 | } |
942 | |
943 | /** |
944 | * @param Sequence $sequence |
945 | * @param Table $table |
946 | * |
947 | * @return bool |
948 | * @throws SchemaException |
949 | * |
950 | * @see Sequence |
951 | */ |
952 | private function isSequenceAutoIncrementsFor(Sequence $sequence, Table $table): bool |
953 | { |
954 | $primaryKey = $table->getPrimaryKey(); |
955 | |
956 | if ($primaryKey === null) |
957 | { |
958 | return false; |
959 | } |
960 | |
961 | $pkColumns = $primaryKey->getColumns(); |
962 | |
963 | if (count($pkColumns) !== 1) |
964 | { |
965 | return false; |
966 | } |
967 | |
968 | $column = $table->getColumn($pkColumns[0]); |
969 | |
970 | if (! $column->getAutoincrement()) |
971 | { |
972 | return false; |
973 | } |
974 | |
975 | $sequenceName = $sequence->getShortestName($table->getNamespaceName()); |
976 | $tableName = $table->getShortestName($table->getNamespaceName()); |
977 | $tableSequenceName = sprintf('%s_seq', $tableName); |
978 | |
979 | return $tableSequenceName === $sequenceName; |
980 | } |
981 | } |