Complex classes like DB2Platform often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DB2Platform, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class DB2Platform extends AbstractPlatform |
||
24 | { |
||
25 | /** |
||
26 | * {@inheritDoc} |
||
27 | */ |
||
28 | 27 | public function getBlobTypeDeclarationSQL(array $field) : string |
|
33 | |||
34 | /** |
||
35 | * {@inheritDoc} |
||
36 | */ |
||
37 | 135 | public function initializeDoctrineTypeMappings() : void |
|
38 | { |
||
39 | 135 | $this->doctrineTypeMapping = [ |
|
|
|||
40 | 'bigint' => 'bigint', |
||
41 | 'binary' => 'binary', |
||
42 | 'blob' => 'blob', |
||
43 | 'character' => 'string', |
||
44 | 'clob' => 'text', |
||
45 | 'date' => 'date', |
||
46 | 'decimal' => 'decimal', |
||
47 | 'double' => 'float', |
||
48 | 'integer' => 'integer', |
||
49 | 'real' => 'float', |
||
50 | 'smallint' => 'smallint', |
||
51 | 'time' => 'time', |
||
52 | 'timestamp' => 'datetime', |
||
53 | 'varbinary' => 'binary', |
||
54 | 'varchar' => 'string', |
||
55 | ]; |
||
56 | 135 | } |
|
57 | |||
58 | /** |
||
59 | * {@inheritdoc} |
||
60 | */ |
||
61 | 1107 | public function isCommentedDoctrineType(Type $doctrineType) : bool |
|
62 | { |
||
63 | 1107 | if ($doctrineType->getName() === Types::BOOLEAN) { |
|
64 | // We require a commented boolean type in order to distinguish between boolean and smallint |
||
65 | // as both (have to) map to the same native type. |
||
66 | 27 | return true; |
|
67 | } |
||
68 | |||
69 | 1080 | return parent::isCommentedDoctrineType($doctrineType); |
|
70 | } |
||
71 | |||
72 | /** |
||
73 | * {@inheritDoc} |
||
74 | */ |
||
75 | 54 | protected function getBinaryTypeDeclarationSQLSnippet(?int $length) : string |
|
76 | { |
||
77 | 54 | return $this->getCharTypeDeclarationSQLSnippet($length) . ' FOR BIT DATA'; |
|
78 | } |
||
79 | |||
80 | /** |
||
81 | * {@inheritDoc} |
||
82 | */ |
||
83 | 54 | protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length) : string |
|
84 | { |
||
85 | 54 | return $this->getVarcharTypeDeclarationSQLSnippet($length) . ' FOR BIT DATA'; |
|
86 | } |
||
87 | |||
88 | /** |
||
89 | * {@inheritDoc} |
||
90 | */ |
||
91 | 81 | public function getClobTypeDeclarationSQL(array $field) : string |
|
92 | { |
||
93 | // todo clob(n) with $field['length']; |
||
94 | 81 | return 'CLOB(1M)'; |
|
95 | } |
||
96 | |||
97 | /** |
||
98 | * {@inheritDoc} |
||
99 | */ |
||
100 | 135 | public function getName() : string |
|
104 | |||
105 | /** |
||
106 | * {@inheritDoc} |
||
107 | */ |
||
108 | 54 | public function getBooleanTypeDeclarationSQL(array $columnDef) : string |
|
109 | { |
||
110 | 54 | return 'SMALLINT'; |
|
111 | } |
||
112 | |||
113 | /** |
||
114 | * {@inheritDoc} |
||
115 | */ |
||
116 | 324 | public function getIntegerTypeDeclarationSQL(array $columnDef) : string |
|
117 | { |
||
118 | 324 | return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); |
|
119 | } |
||
120 | |||
121 | /** |
||
122 | * {@inheritDoc} |
||
123 | */ |
||
124 | 27 | public function getBigIntTypeDeclarationSQL(array $columnDef) : string |
|
125 | { |
||
126 | 27 | return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); |
|
127 | } |
||
128 | |||
129 | /** |
||
130 | * {@inheritDoc} |
||
131 | */ |
||
132 | 27 | public function getSmallIntTypeDeclarationSQL(array $columnDef) : string |
|
133 | { |
||
134 | 27 | return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); |
|
135 | } |
||
136 | |||
137 | /** |
||
138 | * {@inheritDoc} |
||
139 | */ |
||
140 | 324 | protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string |
|
141 | { |
||
142 | 324 | $autoinc = ''; |
|
143 | 324 | if (! empty($columnDef['autoincrement'])) { |
|
144 | 54 | $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; |
|
145 | } |
||
146 | |||
147 | 324 | return $autoinc; |
|
148 | } |
||
149 | |||
150 | /** |
||
151 | * {@inheritdoc} |
||
152 | */ |
||
153 | 27 | public function getBitAndComparisonExpression(string $value1, string $value2) : string |
|
157 | |||
158 | /** |
||
159 | * {@inheritdoc} |
||
160 | */ |
||
161 | 27 | public function getBitOrComparisonExpression(string $value1, string $value2) : string |
|
165 | |||
166 | /** |
||
167 | * {@inheritdoc} |
||
168 | */ |
||
169 | 27 | protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string |
|
170 | { |
||
171 | switch ($unit) { |
||
172 | 27 | case DateIntervalUnit::WEEK: |
|
173 | 27 | $interval = $this->multiplyInterval($interval, 7); |
|
174 | 27 | $unit = DateIntervalUnit::DAY; |
|
175 | 27 | break; |
|
176 | |||
177 | 27 | case DateIntervalUnit::QUARTER: |
|
178 | 27 | $interval = $this->multiplyInterval($interval, 3); |
|
179 | 27 | $unit = DateIntervalUnit::MONTH; |
|
180 | 27 | break; |
|
181 | } |
||
182 | |||
183 | 27 | return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit; |
|
184 | } |
||
185 | |||
186 | /** |
||
187 | * {@inheritdoc} |
||
188 | */ |
||
189 | 27 | public function getDateDiffExpression(string $date1, string $date2) : string |
|
190 | { |
||
191 | 27 | return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')'; |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * {@inheritDoc} |
||
196 | */ |
||
197 | 27 | public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string |
|
198 | { |
||
199 | 27 | if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] === true) { |
|
200 | 27 | return 'TIMESTAMP(0) WITH DEFAULT'; |
|
201 | } |
||
202 | |||
203 | 27 | return 'TIMESTAMP(0)'; |
|
204 | } |
||
205 | |||
206 | /** |
||
207 | * {@inheritDoc} |
||
208 | */ |
||
209 | 27 | public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string |
|
210 | { |
||
211 | 27 | return 'DATE'; |
|
212 | } |
||
213 | |||
214 | /** |
||
215 | * {@inheritDoc} |
||
216 | */ |
||
217 | 27 | public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string |
|
221 | |||
222 | /** |
||
223 | * {@inheritdoc} |
||
224 | */ |
||
225 | 54 | public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string |
|
226 | { |
||
227 | 54 | $tableIdentifier = new Identifier($tableName); |
|
228 | |||
229 | 54 | return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE'; |
|
230 | } |
||
231 | |||
232 | /** |
||
233 | * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. |
||
234 | */ |
||
235 | 27 | public function getListTableColumnsSQL(string $table, ?string $database = null) : string |
|
236 | { |
||
237 | 27 | $table = $this->quoteStringLiteral($table); |
|
238 | |||
239 | // We do the funky subquery and join syscat.columns.default this crazy way because |
||
240 | // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB, |
||
241 | // it wants shorter stuff like a varchar. |
||
242 | return " |
||
243 | SELECT |
||
244 | cols.default, |
||
245 | subq.* |
||
246 | FROM ( |
||
247 | SELECT DISTINCT |
||
248 | c.tabschema, |
||
249 | c.tabname, |
||
250 | c.colname, |
||
251 | c.colno, |
||
252 | c.typename, |
||
253 | c.nulls, |
||
254 | c.length, |
||
255 | c.scale, |
||
256 | c.identity, |
||
257 | tc.type AS tabconsttype, |
||
258 | c.remarks AS comment, |
||
259 | k.colseq, |
||
260 | CASE |
||
261 | WHEN c.generated = 'D' THEN 1 |
||
262 | ELSE 0 |
||
263 | END AS autoincrement |
||
264 | FROM syscat.columns c |
||
265 | LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc |
||
266 | ON (k.tabschema = tc.tabschema |
||
267 | AND k.tabname = tc.tabname |
||
268 | AND tc.type = 'P')) |
||
269 | ON (c.tabschema = k.tabschema |
||
270 | AND c.tabname = k.tabname |
||
271 | AND c.colname = k.colname) |
||
272 | 27 | WHERE UPPER(c.tabname) = UPPER(" . $table . ') |
|
273 | ORDER BY c.colno |
||
274 | ) subq |
||
275 | JOIN syscat.columns cols |
||
276 | ON subq.tabschema = cols.tabschema |
||
277 | AND subq.tabname = cols.tabname |
||
278 | AND subq.colno = cols.colno |
||
279 | ORDER BY subq.colno |
||
280 | '; |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * {@inheritDoc} |
||
285 | */ |
||
286 | public function getListTablesSQL() : string |
||
287 | { |
||
288 | return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * {@inheritDoc} |
||
293 | */ |
||
294 | public function getListViewsSQL(string $database) : string |
||
295 | { |
||
296 | return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS'; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * {@inheritDoc} |
||
301 | */ |
||
302 | 27 | public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string |
|
303 | { |
||
304 | 27 | $table = $this->quoteStringLiteral($table); |
|
305 | |||
306 | return "SELECT idx.INDNAME AS key_name, |
||
307 | idxcol.COLNAME AS column_name, |
||
308 | CASE |
||
309 | WHEN idx.UNIQUERULE = 'P' THEN 1 |
||
310 | ELSE 0 |
||
311 | END AS primary, |
||
312 | CASE |
||
313 | WHEN idx.UNIQUERULE = 'D' THEN 1 |
||
314 | ELSE 0 |
||
315 | END AS non_unique |
||
316 | FROM SYSCAT.INDEXES AS idx |
||
317 | JOIN SYSCAT.INDEXCOLUSE AS idxcol |
||
318 | ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME |
||
319 | 27 | WHERE idx.TABNAME = UPPER(" . $table . ') |
|
320 | ORDER BY idxcol.COLSEQ ASC'; |
||
321 | } |
||
322 | |||
323 | /** |
||
324 | * {@inheritDoc} |
||
325 | */ |
||
326 | 27 | public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string |
|
327 | { |
||
328 | 27 | $table = $this->quoteStringLiteral($table); |
|
329 | |||
330 | return "SELECT fkcol.COLNAME AS local_column, |
||
331 | fk.REFTABNAME AS foreign_table, |
||
332 | pkcol.COLNAME AS foreign_column, |
||
333 | fk.CONSTNAME AS index_name, |
||
334 | CASE |
||
335 | WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT' |
||
336 | ELSE NULL |
||
337 | END AS on_update, |
||
338 | CASE |
||
339 | WHEN fk.DELETERULE = 'C' THEN 'CASCADE' |
||
340 | WHEN fk.DELETERULE = 'N' THEN 'SET NULL' |
||
341 | WHEN fk.DELETERULE = 'R' THEN 'RESTRICT' |
||
342 | ELSE NULL |
||
343 | END AS on_delete |
||
344 | FROM SYSCAT.REFERENCES AS fk |
||
345 | JOIN SYSCAT.KEYCOLUSE AS fkcol |
||
346 | ON fk.CONSTNAME = fkcol.CONSTNAME |
||
347 | AND fk.TABSCHEMA = fkcol.TABSCHEMA |
||
348 | AND fk.TABNAME = fkcol.TABNAME |
||
349 | JOIN SYSCAT.KEYCOLUSE AS pkcol |
||
350 | ON fk.REFKEYNAME = pkcol.CONSTNAME |
||
351 | AND fk.REFTABSCHEMA = pkcol.TABSCHEMA |
||
352 | AND fk.REFTABNAME = pkcol.TABNAME |
||
353 | 27 | WHERE fk.TABNAME = UPPER(" . $table . ') |
|
354 | ORDER BY fkcol.COLSEQ ASC'; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * {@inheritDoc} |
||
359 | */ |
||
360 | 27 | public function getCreateViewSQL(string $name, string $sql) : string |
|
361 | { |
||
362 | 27 | return 'CREATE VIEW ' . $name . ' AS ' . $sql; |
|
363 | } |
||
364 | |||
365 | /** |
||
366 | * {@inheritDoc} |
||
367 | */ |
||
368 | 27 | public function getDropViewSQL(string $name) : string |
|
372 | |||
373 | /** |
||
374 | * {@inheritDoc} |
||
375 | */ |
||
376 | 27 | public function getCreateDatabaseSQL(string $database) : string |
|
377 | { |
||
378 | 27 | return 'CREATE DATABASE ' . $database; |
|
379 | } |
||
380 | |||
381 | /** |
||
382 | * {@inheritDoc} |
||
383 | */ |
||
384 | 27 | public function getDropDatabaseSQL(string $database) : string |
|
385 | { |
||
386 | 27 | return 'DROP DATABASE ' . $database; |
|
387 | } |
||
388 | |||
389 | /** |
||
390 | * {@inheritDoc} |
||
391 | */ |
||
392 | 27 | public function supportsCreateDropDatabase() : bool |
|
393 | { |
||
394 | 27 | return false; |
|
395 | } |
||
396 | |||
397 | /** |
||
398 | * {@inheritDoc} |
||
399 | */ |
||
400 | 27 | public function supportsReleaseSavepoints() : bool |
|
401 | { |
||
402 | 27 | return false; |
|
403 | } |
||
404 | |||
405 | /** |
||
406 | * {@inheritdoc} |
||
407 | */ |
||
408 | 351 | public function supportsCommentOnStatement() : bool |
|
409 | { |
||
410 | 351 | return true; |
|
411 | } |
||
412 | |||
413 | /** |
||
414 | * {@inheritDoc} |
||
415 | */ |
||
416 | 54 | public function getCurrentDateSQL() : string |
|
417 | { |
||
418 | 54 | return 'CURRENT DATE'; |
|
419 | } |
||
420 | |||
421 | /** |
||
422 | * {@inheritDoc} |
||
423 | */ |
||
424 | 27 | public function getCurrentTimeSQL() : string |
|
425 | { |
||
426 | 27 | return 'CURRENT TIME'; |
|
427 | } |
||
428 | |||
429 | /** |
||
430 | * {@inheritDoc} |
||
431 | */ |
||
432 | 54 | public function getCurrentTimestampSQL() : string |
|
433 | { |
||
434 | 54 | return 'CURRENT TIMESTAMP'; |
|
435 | } |
||
436 | |||
437 | /** |
||
438 | * {@inheritDoc} |
||
439 | */ |
||
440 | 27 | public function getIndexDeclarationSQL(string $name, Index $index) : string |
|
441 | { |
||
442 | // Index declaration in statements like CREATE TABLE is not supported. |
||
443 | 27 | throw NotSupported::new(__METHOD__); |
|
444 | } |
||
445 | |||
446 | /** |
||
447 | * {@inheritDoc} |
||
448 | */ |
||
449 | 324 | protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array |
|
450 | { |
||
451 | 324 | $indexes = []; |
|
452 | 324 | if (isset($options['indexes'])) { |
|
453 | 324 | $indexes = $options['indexes']; |
|
454 | } |
||
455 | 324 | $options['indexes'] = []; |
|
456 | |||
457 | 324 | $sqls = parent::_getCreateTableSQL($tableName, $columns, $options); |
|
458 | |||
459 | 324 | foreach ($indexes as $definition) { |
|
460 | 108 | $sqls[] = $this->getCreateIndexSQL($definition, $tableName); |
|
461 | } |
||
462 | |||
463 | 324 | return $sqls; |
|
464 | } |
||
465 | |||
466 | /** |
||
467 | * {@inheritDoc} |
||
468 | */ |
||
469 | 648 | public function getAlterTableSQL(TableDiff $diff) : array |
|
470 | { |
||
471 | 648 | $sql = []; |
|
472 | 648 | $columnSql = []; |
|
473 | 648 | $commentsSQL = []; |
|
474 | |||
475 | 648 | $queryParts = []; |
|
476 | 648 | foreach ($diff->addedColumns as $column) { |
|
477 | 108 | if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { |
|
478 | continue; |
||
479 | } |
||
480 | |||
481 | 108 | $columnDef = $column->toArray(); |
|
482 | 108 | $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); |
|
483 | |||
484 | // Adding non-nullable columns to a table requires a default value to be specified. |
||
485 | 108 | if (! empty($columnDef['notnull']) && |
|
486 | 108 | ! isset($columnDef['default']) && |
|
487 | 108 | empty($columnDef['autoincrement']) |
|
488 | ) { |
||
489 | 81 | $queryPart .= ' WITH DEFAULT'; |
|
490 | } |
||
491 | |||
492 | 108 | $queryParts[] = $queryPart; |
|
493 | |||
494 | 108 | $comment = $this->getColumnComment($column); |
|
495 | |||
496 | 108 | if ($comment === null || $comment === '') { |
|
497 | 81 | continue; |
|
498 | } |
||
499 | |||
500 | 27 | $commentsSQL[] = $this->getCommentOnColumnSQL( |
|
501 | 27 | $diff->getName($this)->getQuotedName($this), |
|
502 | 27 | $column->getQuotedName($this), |
|
503 | $comment |
||
504 | ); |
||
505 | } |
||
506 | |||
507 | 648 | foreach ($diff->removedColumns as $column) { |
|
508 | 81 | if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { |
|
509 | continue; |
||
510 | } |
||
511 | |||
512 | 81 | $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); |
|
513 | } |
||
514 | |||
515 | 648 | foreach ($diff->changedColumns as $columnDiff) { |
|
516 | 459 | if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { |
|
517 | continue; |
||
518 | } |
||
519 | |||
520 | 459 | if ($columnDiff->hasChanged('comment')) { |
|
521 | 27 | $commentsSQL[] = $this->getCommentOnColumnSQL( |
|
522 | 27 | $diff->getName($this)->getQuotedName($this), |
|
523 | 27 | $columnDiff->column->getQuotedName($this), |
|
524 | 27 | $this->getColumnComment($columnDiff->column) |
|
525 | ); |
||
526 | |||
527 | 27 | if (count($columnDiff->changedProperties) === 1) { |
|
528 | 27 | continue; |
|
529 | } |
||
530 | } |
||
531 | |||
532 | 432 | $this->gatherAlterColumnSQL($diff->getName($this), $columnDiff, $sql, $queryParts); |
|
533 | } |
||
534 | |||
535 | 648 | foreach ($diff->renamedColumns as $oldColumnName => $column) { |
|
536 | 108 | if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { |
|
537 | continue; |
||
538 | } |
||
539 | |||
540 | 108 | $oldColumnName = new Identifier($oldColumnName); |
|
541 | |||
542 | 108 | $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . |
|
543 | 108 | ' TO ' . $column->getQuotedName($this); |
|
544 | } |
||
545 | |||
546 | 648 | $tableSql = []; |
|
547 | |||
548 | 648 | if (! $this->onSchemaAlterTable($diff, $tableSql)) { |
|
549 | 648 | if (count($queryParts) > 0) { |
|
550 | 486 | $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(' ', $queryParts); |
|
551 | } |
||
552 | |||
553 | // Some table alteration operations require a table reorganization. |
||
554 | 648 | if (! empty($diff->removedColumns) || ! empty($diff->changedColumns)) { |
|
555 | 459 | $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')"; |
|
556 | } |
||
557 | |||
558 | 648 | $sql = array_merge($sql, $commentsSQL); |
|
559 | |||
560 | 648 | $newName = $diff->getNewName(); |
|
561 | |||
562 | 648 | if ($newName !== null) { |
|
563 | 54 | $sql[] = sprintf( |
|
564 | 4 | 'RENAME TABLE %s TO %s', |
|
565 | 54 | $diff->getName($this)->getQuotedName($this), |
|
566 | 54 | $newName->getQuotedName($this) |
|
567 | ); |
||
568 | } |
||
569 | |||
570 | 648 | $sql = array_merge( |
|
571 | 648 | $this->getPreAlterTableIndexForeignKeySQL($diff), |
|
572 | 648 | $sql, |
|
573 | 648 | $this->getPostAlterTableIndexForeignKeySQL($diff) |
|
574 | ); |
||
575 | } |
||
576 | |||
577 | 648 | return array_merge($sql, $tableSql, $columnSql); |
|
578 | } |
||
579 | |||
580 | /** |
||
581 | * Gathers the table alteration SQL for a given column diff. |
||
582 | * |
||
583 | * @param Identifier $table The table to gather the SQL for. |
||
584 | * @param ColumnDiff $columnDiff The column diff to evaluate. |
||
585 | * @param string[] $sql The sequence of table alteration statements to fill. |
||
586 | * @param mixed[] $queryParts The sequence of column alteration clauses to fill. |
||
587 | */ |
||
588 | 432 | private function gatherAlterColumnSQL(Identifier $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts) : void |
|
589 | { |
||
590 | 432 | $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff); |
|
591 | |||
592 | 432 | if (empty($alterColumnClauses)) { |
|
593 | 54 | return; |
|
594 | } |
||
595 | |||
596 | // If we have a single column alteration, we can append the clause to the main query. |
||
597 | 378 | if (count($alterColumnClauses) === 1) { |
|
598 | 351 | $queryParts[] = current($alterColumnClauses); |
|
599 | |||
600 | 351 | return; |
|
601 | } |
||
602 | |||
603 | // We have multiple alterations for the same column, |
||
604 | // so we need to trigger a complete ALTER TABLE statement |
||
605 | // for each ALTER COLUMN clause. |
||
606 | 27 | foreach ($alterColumnClauses as $alterColumnClause) { |
|
607 | 27 | $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause; |
|
608 | } |
||
609 | 27 | } |
|
610 | |||
611 | /** |
||
612 | * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff. |
||
613 | * |
||
614 | * @param ColumnDiff $columnDiff The column diff to evaluate. |
||
615 | * |
||
616 | * @return string[] |
||
617 | */ |
||
618 | 432 | private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) : array |
|
619 | { |
||
620 | 432 | $column = $columnDiff->column->toArray(); |
|
621 | |||
622 | 432 | $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this); |
|
623 | |||
624 | 432 | if ($column['columnDefinition']) { |
|
625 | 27 | return [$alterClause . ' ' . $column['columnDefinition']]; |
|
626 | } |
||
627 | |||
628 | 405 | $clauses = []; |
|
629 | |||
630 | 405 | if ($columnDiff->hasChanged('type') || |
|
631 | 399 | $columnDiff->hasChanged('length') || |
|
632 | 397 | $columnDiff->hasChanged('precision') || |
|
633 | 395 | $columnDiff->hasChanged('scale') || |
|
634 | 405 | $columnDiff->hasChanged('fixed') |
|
635 | ) { |
||
636 | 216 | $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this); |
|
637 | } |
||
638 | |||
639 | 405 | if ($columnDiff->hasChanged('notnull')) { |
|
640 | 108 | $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; |
|
641 | } |
||
642 | |||
643 | 405 | if ($columnDiff->hasChanged('default')) { |
|
644 | 108 | if (isset($column['default'])) { |
|
645 | 81 | $defaultClause = $this->getDefaultValueDeclarationSQL($column); |
|
646 | |||
647 | 81 | if ($defaultClause) { |
|
648 | 81 | $clauses[] = $alterClause . ' SET' . $defaultClause; |
|
649 | } |
||
650 | } else { |
||
651 | 27 | $clauses[] = $alterClause . ' DROP DEFAULT'; |
|
652 | } |
||
653 | } |
||
654 | |||
655 | 405 | return $clauses; |
|
656 | } |
||
657 | |||
658 | /** |
||
659 | * {@inheritDoc} |
||
660 | */ |
||
661 | 648 | protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) : array |
|
662 | { |
||
663 | 648 | $sql = []; |
|
664 | 648 | $table = $diff->getName($this)->getQuotedName($this); |
|
665 | |||
666 | 648 | foreach ($diff->removedIndexes as $remKey => $remIndex) { |
|
667 | foreach ($diff->addedIndexes as $addKey => $addIndex) { |
||
668 | if ($remIndex->getColumns() === $addIndex->getColumns()) { |
||
669 | if ($remIndex->isPrimary()) { |
||
670 | $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; |
||
671 | } elseif ($remIndex->isUnique()) { |
||
672 | $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this); |
||
673 | } else { |
||
674 | $sql[] = $this->getDropIndexSQL($remIndex, $table); |
||
675 | } |
||
676 | |||
677 | $sql[] = $this->getCreateIndexSQL($addIndex, $table); |
||
678 | |||
679 | unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]); |
||
680 | |||
681 | break; |
||
682 | } |
||
683 | } |
||
684 | } |
||
685 | |||
686 | 648 | $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); |
|
687 | |||
688 | 648 | return $sql; |
|
689 | } |
||
690 | |||
691 | /** |
||
692 | * {@inheritdoc} |
||
693 | */ |
||
694 | 135 | protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array |
|
695 | { |
||
696 | 135 | if (strpos($tableName, '.') !== false) { |
|
697 | 54 | [$schema] = explode('.', $tableName); |
|
698 | 54 | $oldIndexName = $schema . '.' . $oldIndexName; |
|
699 | } |
||
700 | |||
701 | 135 | return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)]; |
|
702 | } |
||
703 | |||
704 | /** |
||
705 | * {@inheritDoc} |
||
706 | */ |
||
707 | 594 | public function getDefaultValueDeclarationSQL(array $field) : string |
|
708 | { |
||
709 | 594 | if (! empty($field['autoincrement'])) { |
|
710 | 54 | return ''; |
|
711 | } |
||
712 | |||
713 | 567 | if (isset($field['version']) && $field['version']) { |
|
714 | if ((string) $field['type'] !== 'DateTime') { |
||
715 | $field['default'] = '1'; |
||
716 | } |
||
717 | } |
||
718 | |||
719 | 567 | return parent::getDefaultValueDeclarationSQL($field); |
|
720 | } |
||
721 | |||
722 | /** |
||
723 | * {@inheritDoc} |
||
724 | */ |
||
725 | public function getEmptyIdentityInsertSQL(string $tableName, string $identifierColumnName) : string |
||
726 | { |
||
727 | return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; |
||
728 | } |
||
729 | |||
730 | /** |
||
731 | * {@inheritDoc} |
||
732 | */ |
||
733 | 27 | public function getCreateTemporaryTableSnippetSQL() : string |
|
734 | { |
||
735 | 27 | return 'DECLARE GLOBAL TEMPORARY TABLE'; |
|
736 | } |
||
737 | |||
738 | /** |
||
739 | * {@inheritDoc} |
||
740 | */ |
||
741 | public function getTemporaryTableName(string $tableName) : string |
||
742 | { |
||
743 | return 'SESSION.' . $tableName; |
||
744 | } |
||
745 | |||
746 | /** |
||
747 | * {@inheritDoc} |
||
748 | */ |
||
749 | 54 | protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string |
|
750 | { |
||
751 | 54 | $where = []; |
|
752 | |||
753 | 54 | if ($offset > 0) { |
|
754 | 27 | $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1); |
|
755 | } |
||
756 | |||
757 | 54 | if ($limit !== null) { |
|
758 | 27 | $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit); |
|
759 | } |
||
760 | |||
761 | 54 | if (empty($where)) { |
|
762 | 54 | return $query; |
|
763 | } |
||
764 | |||
765 | // Todo OVER() needs ORDER BY data! |
||
766 | 27 | return sprintf( |
|
767 | 2 | 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s', |
|
768 | 27 | $query, |
|
769 | 27 | implode(' AND ', $where) |
|
770 | ); |
||
771 | } |
||
772 | |||
773 | /** |
||
774 | * {@inheritDoc} |
||
775 | */ |
||
776 | 27 | public function getLocateExpression(string $string, string $substring, ?string $start = null) : string |
|
777 | { |
||
778 | 27 | if ($start === null) { |
|
779 | 27 | return sprintf('LOCATE(%s, %s)', $substring, $string); |
|
780 | } |
||
781 | |||
782 | 27 | return sprintf('LOCATE(%s, %s, %s)', $substring, $string, $start); |
|
783 | } |
||
784 | |||
785 | /** |
||
786 | * {@inheritDoc} |
||
787 | */ |
||
788 | 27 | public function getSubstringExpression(string $string, string $start, ?string $length = null) : string |
|
789 | { |
||
790 | 27 | if ($length === null) { |
|
791 | 27 | return sprintf('SUBSTR(%s, %s)', $string, $start); |
|
792 | } |
||
793 | |||
794 | 27 | return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length); |
|
795 | } |
||
796 | |||
797 | /** |
||
798 | * {@inheritDoc} |
||
799 | */ |
||
800 | public function getCurrentDatabaseExpression() : string |
||
801 | { |
||
802 | return 'CURRENT_USER'; |
||
803 | } |
||
804 | |||
805 | /** |
||
806 | * {@inheritDoc} |
||
807 | */ |
||
808 | 27 | public function supportsIdentityColumns() : bool |
|
809 | { |
||
810 | 27 | return true; |
|
811 | } |
||
812 | |||
813 | /** |
||
814 | * {@inheritDoc} |
||
815 | */ |
||
816 | 27 | public function prefersIdentityColumns() : bool |
|
817 | { |
||
818 | 27 | return true; |
|
819 | } |
||
820 | |||
821 | /** |
||
822 | * {@inheritDoc} |
||
823 | * |
||
824 | * DB2 returns all column names in SQL result sets in uppercase. |
||
825 | */ |
||
826 | 27 | public function getSQLResultCasing(string $column) : string |
|
827 | { |
||
828 | 27 | return strtoupper($column); |
|
829 | } |
||
830 | |||
831 | /** |
||
832 | * {@inheritDoc} |
||
833 | */ |
||
834 | 27 | public function getForUpdateSQL() : string |
|
838 | |||
839 | /** |
||
840 | * {@inheritDoc} |
||
841 | */ |
||
842 | public function getDummySelectSQL(string $expression = '1') : string |
||
843 | { |
||
844 | return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression); |
||
845 | } |
||
846 | |||
847 | /** |
||
848 | * {@inheritDoc} |
||
849 | * |
||
850 | * DB2 supports savepoints, but they work semantically different than on other vendor platforms. |
||
851 | * |
||
852 | * TODO: We have to investigate how to get DB2 up and running with savepoints. |
||
853 | */ |
||
854 | 27 | public function supportsSavepoints() : bool |
|
855 | { |
||
856 | 27 | return false; |
|
857 | } |
||
858 | |||
859 | /** |
||
860 | * {@inheritDoc} |
||
861 | */ |
||
862 | 1404 | protected function getReservedKeywordsClass() : string |
|
863 | { |
||
864 | 1404 | return Keywords\DB2Keywords::class; |
|
865 | } |
||
866 | |||
867 | public function getListTableCommentsSQL(string $table) : string |
||
868 | { |
||
869 | return sprintf( |
||
870 | <<<'SQL' |
||
871 | SELECT REMARKS |
||
872 | FROM SYSIBM.SYSTABLES |
||
873 | WHERE NAME = UPPER( %s ) |
||
874 | SQL |
||
875 | , |
||
876 | $this->quoteStringLiteral($table) |
||
877 | ); |
||
878 | } |
||
879 | } |
||
880 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..