Completed
Pull Request — develop (#3576)
by Jonathan
64:38 queued 61:53
created

SQLAnywherePlatform   F

Complexity

Total Complexity 208

Size/Duplication

Total Lines 1513
Duplicated Lines 0 %

Test Coverage

Coverage 94.22%

Importance

Changes 0
Metric Value
wmc 208
eloc 445
dl 0
loc 1513
ccs 440
cts 467
cp 0.9422
rs 2
c 0
b 0
f 0

94 Methods

Rating   Name   Duplication   Size   Complexity  
A appendLockHint() 0 14 4
A fixSchemaElementName() 0 9 2
B getAdvancedForeignKeyOptionsSQL() 0 23 8
A getAlterTableRemoveColumnClause() 0 3 1
A getAlterTableClause() 0 3 1
A getAlterTableAddColumnClause() 0 3 1
A getIndexDeclarationSQL() 0 4 1
A getBinaryDefaultLength() 0 3 1
A getListTableForeignKeysSQL() 0 87 2
A getDateDiffExpression() 0 3 1
A getCreateDatabaseSQL() 0 5 1
A getRenameIndexSQL() 0 3 1
A getCreateTemporaryTableSnippetSQL() 0 3 1
A getCreateIndexSQL() 0 3 1
A getClobTypeDeclarationSQL() 0 3 1
A getListTableIndexesSQL() 0 54 2
A getLocateExpression() 0 7 2
A getForUpdateSQL() 0 3 1
A getAlterSequenceSQL() 0 4 1
A getBooleanTypeDeclarationSQL() 0 5 3
A getDateTimeFormatString() 0 3 1
A getDateTypeDeclarationSQL() 0 3 1
A getVarcharMaxLength() 0 3 1
A getListDatabasesSQL() 0 3 1
A prefersIdentityColumns() 0 3 1
A getName() 0 3 1
A getStartDatabaseSQL() 0 5 1
B _getCreateTableSQL() 0 44 11
A getAlterTableChangeColumnClause() 0 17 5
A getDropIndexSQL() 0 27 6
A getCurrentDateSQL() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A getDateTimeTzTypeDeclarationSQL() 0 3 1
A getTruncateTableSQL() 0 5 1
A getDropViewSQL() 0 3 1
A getCreateIndexSQLFlags() 0 16 4
A getCurrentTimestampSQL() 0 3 1
A getSubstringExpression() 0 7 2
B getAdvancedIndexOptionsSQL() 0 23 11
B getForeignKeyBaseDeclarationSQL() 0 32 7
A initializeDoctrineTypeMappings() 0 42 1
A getCreateViewSQL() 0 3 1
A getCurrentTimeSQL() 0 3 1
A getDateTimeTzFormatString() 0 3 1
A getIntegerTypeDeclarationSQL() 0 5 1
A getDateTimeTypeDeclarationSQL() 0 3 1
A getDateArithmeticIntervalExpression() 0 9 2
A getForeignKeyMatchClauseSQL() 0 19 5
A getListViewsSQL() 0 3 1
A getBinaryTypeDeclarationSQLSnippet() 0 5 4
A getVarcharDefaultLength() 0 3 1
A getListTableColumnsSQL() 0 31 2
A getBigIntTypeDeclarationSQL() 0 5 1
A getPrimaryKeyDeclarationSQL() 0 9 2
A getSmallIntTypeDeclarationSQL() 0 5 1
A getCreatePrimaryKeySQL() 0 7 2
A getBlobTypeDeclarationSQL() 0 3 1
A getDropDatabaseSQL() 0 5 1
A getGuidTypeDeclarationSQL() 0 3 1
B getTrimExpression() 0 29 7
A getConcatExpression() 0 3 1
A getTemporaryTableSQL() 0 3 1
A getCommentOnColumnSQL() 0 11 2
A getForeignKeyReferentialActionSQL() 0 8 2
A getAlterTableRenameTableClause() 0 3 1
A getSequenceNextValSQL() 0 3 1
B getTableConstraintDeclarationSQL() 0 40 9
A getTopClauseSQL() 0 7 3
A getTimeFormatString() 0 3 1
A getDefaultTransactionIsolationLevel() 0 3 1
A getAlterTableRenameColumnClause() 0 5 1
A getListUsersSQL() 0 3 1
A getListTableConstraintsSQL() 0 23 2
A doModifyLimitQuery() 0 13 3
A getStopDatabaseSQL() 0 5 1
F getAlterTableSQL() 0 91 16
A getMaxIdentifierLength() 0 3 1
A supportsCommentOnStatement() 0 3 1
A _getCommonIntegerTypeDeclarationSQL() 0 6 3
A getMd5Expression() 0 3 1
A supportsSequences() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 5 4
A hasNativeGuidType() 0 3 1
A getTimeTypeDeclarationSQL() 0 3 1
A getListTablesSQL() 0 3 1
A getDropSequenceSQL() 0 7 2
A getCreateSequenceSQL() 0 6 1
A supportsIdentityColumns() 0 3 1
A getRegexpExpression() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getBinaryMaxLength() 0 3 1
A getCreateConstraintSQL() 0 12 3
A _getTransactionIsolationLevelSQL() 0 13 5
A getListSequencesSQL() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like SQLAnywherePlatform 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.

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 SQLAnywherePlatform, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Platforms;
6
7
use Doctrine\DBAL\LockMode;
8
use Doctrine\DBAL\Platforms\Exception\NotSupported;
9
use Doctrine\DBAL\Schema\Column;
10
use Doctrine\DBAL\Schema\ColumnDiff;
11
use Doctrine\DBAL\Schema\Constraint;
12
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
13
use Doctrine\DBAL\Schema\Identifier;
14
use Doctrine\DBAL\Schema\Index;
15
use Doctrine\DBAL\Schema\Sequence;
16
use Doctrine\DBAL\Schema\Table;
17
use Doctrine\DBAL\Schema\TableDiff;
18
use Doctrine\DBAL\TransactionIsolationLevel;
19
use InvalidArgumentException;
20
use UnexpectedValueException;
21
use function array_merge;
22
use function array_unique;
23
use function array_values;
24
use function count;
25
use function explode;
26
use function get_class;
27
use function implode;
28
use function in_array;
29
use function is_string;
30
use function preg_match;
31
use function sprintf;
32
use function strlen;
33
use function strpos;
34
use function strtoupper;
35
use function substr;
36
37
/**
38
 * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the
39
 * SAP Sybase SQL Anywhere 12 database platform.
40
 */
41
class SQLAnywherePlatform extends AbstractPlatform
42
{
43
    public const FOREIGN_KEY_MATCH_SIMPLE        = 1;
44
    public const FOREIGN_KEY_MATCH_FULL          = 2;
45
    public const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
46
    public const FOREIGN_KEY_MATCH_FULL_UNIQUE   = 130;
47
48
    /**
49
     * {@inheritdoc}
50
     */
51 2709
    public function appendLockHint(string $fromClause, ?int $lockMode) : string
52
    {
53 2709
        switch (true) {
54
            case $lockMode === LockMode::NONE:
55 2679
                return $fromClause . ' WITH (NOLOCK)';
56
57 2708
            case $lockMode === LockMode::PESSIMISTIC_READ:
58 2627
                return $fromClause . ' WITH (UPDLOCK)';
59
60 2707
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
61 2601
                return $fromClause . ' WITH (XLOCK)';
62
63
            default:
64 2706
                return $fromClause;
65
        }
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     *
71
     * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
72
     */
73 2549
    public function fixSchemaElementName(string $schemaElementName) : string
74
    {
75 2549
        $maxIdentifierLength = $this->getMaxIdentifierLength();
76
77 2549
        if (strlen($schemaElementName) > $maxIdentifierLength) {
78 2549
            return substr($schemaElementName, 0, $maxIdentifierLength);
79
        }
80
81 2549
        return $schemaElementName;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87 2789
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string
88
    {
89 2789
        $query = '';
90
91 2789
        if ($foreignKey->hasOption('match')) {
92 2341
            $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
93
        }
94
95 2789
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
96
97 2789
        if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
98 2341
            $query .= ' CHECK ON COMMIT';
99
        }
100
101 2789
        if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
102 2341
            $query .= ' CLUSTERED';
103
        }
104
105 2789
        if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
106 2341
            $query .= ' FOR OLAP WORKLOAD';
107
        }
108
109 2789
        return $query;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 2745
    public function getAlterTableSQL(TableDiff $diff) : array
116
    {
117 2745
        $sql          = [];
118 2745
        $columnSql    = [];
119 2745
        $commentsSQL  = [];
120 2745
        $tableSql     = [];
121 2745
        $alterClauses = [];
122
123 2745
        foreach ($diff->addedColumns as $column) {
124 862
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
125
                continue;
126
            }
127
128 862
            $alterClauses[] = $this->getAlterTableAddColumnClause($column);
129
130 862
            $comment = $this->getColumnComment($column);
131
132 862
            if ($comment === null || $comment === '') {
133 861
                continue;
134
            }
135
136 755
            $commentsSQL[] = $this->getCommentOnColumnSQL(
137 755
                $diff->getName($this)->getQuotedName($this),
138 755
                $column->getQuotedName($this),
139 1
                $comment
140
            );
141
        }
142
143 2745
        foreach ($diff->removedColumns as $column) {
144 861
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
145
                continue;
146
            }
147
148 861
            $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
149
        }
150
151 2745
        foreach ($diff->changedColumns as $columnDiff) {
152 2738
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
153
                continue;
154
            }
155
156 2738
            $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
157
158 2738
            if ($alterClause !== null) {
159 863
                $alterClauses[] = $alterClause;
160
            }
161
162 2738
            if (! $columnDiff->hasChanged('comment')) {
163 863
                continue;
164
            }
165
166 2733
            $column = $columnDiff->column;
167
168 2733
            $commentsSQL[] = $this->getCommentOnColumnSQL(
169 2733
                $diff->getName($this)->getQuotedName($this),
170 2733
                $column->getQuotedName($this),
171 2733
                $this->getColumnComment($column)
172
            );
173
        }
174
175 2745
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
176 810
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
177
                continue;
178
            }
179
180 810
            $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
181 810
                $this->getAlterTableRenameColumnClause($oldColumnName, $column);
182
        }
183
184 2745
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
185 2745
            if (! empty($alterClauses)) {
186 864
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
187
            }
188
189 2745
            $sql = array_merge($sql, $commentsSQL);
190
191 2745
            $newName = $diff->getNewName();
192
193 2745
            if ($newName !== null) {
194 860
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
195 860
                    $this->getAlterTableRenameTableClause($newName);
196
            }
197
198 2745
            $sql = array_merge(
199 2745
                $this->getPreAlterTableIndexForeignKeySQL($diff),
200 2745
                $sql,
201 2745
                $this->getPostAlterTableIndexForeignKeySQL($diff)
202
            );
203
        }
204
205 2745
        return array_merge($sql, $tableSql, $columnSql);
206
    }
207
208
    /**
209
     * Returns the SQL clause for creating a column in a table alteration.
210
     *
211
     * @param Column $column The column to add.
212
     */
213 862
    protected function getAlterTableAddColumnClause(Column $column) : string
214
    {
215 862
        return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
216
    }
217
218
    /**
219
     * Returns the SQL clause for altering a table.
220
     *
221
     * @param Identifier $tableName The quoted name of the table to alter.
222
     */
223 866
    protected function getAlterTableClause(Identifier $tableName) : string
224
    {
225 866
        return 'ALTER TABLE ' . $tableName->getQuotedName($this);
226
    }
227
228
    /**
229
     * Returns the SQL clause for dropping a column in a table alteration.
230
     *
231
     * @param Column $column The column to drop.
232
     */
233 861
    protected function getAlterTableRemoveColumnClause(Column $column) : string
234
    {
235 861
        return 'DROP ' . $column->getQuotedName($this);
236
    }
237
238
    /**
239
     * Returns the SQL clause for renaming a column in a table alteration.
240
     *
241
     * @param string $oldColumnName The quoted name of the column to rename.
242
     * @param Column $column        The column to rename to.
243
     */
244 810
    protected function getAlterTableRenameColumnClause(string $oldColumnName, Column $column) : string
245
    {
246 810
        $oldColumnName = new Identifier($oldColumnName);
247
248 810
        return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
249
    }
250
251
    /**
252
     * Returns the SQL clause for renaming a table in a table alteration.
253
     *
254
     * @param Identifier $newTableName The quoted name of the table to rename to.
255
     */
256 860
    protected function getAlterTableRenameTableClause(Identifier $newTableName) : string
257
    {
258 860
        return 'RENAME ' . $newTableName->getQuotedName($this);
259
    }
260
261
    /**
262
     * Returns the SQL clause for altering a column in a table alteration.
263
     *
264
     * This method returns null in case that only the column comment has changed.
265
     * Changes in column comments have to be handled differently.
266
     *
267
     * @param ColumnDiff $columnDiff The diff of the column to alter.
268
     */
269 2738
    protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff) : ?string
270
    {
271 2738
        $column = $columnDiff->column;
272
273
        // Do not return alter clause if only comment has changed.
274 2738
        if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) {
275
            $columnAlterationClause = 'ALTER ' .
276 863
                $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
277
278 863
            if ($columnDiff->hasChanged('default') && $column->getDefault() === null) {
279
                $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT';
280
            }
281
282 863
            return $columnAlterationClause;
283
        }
284
285 2733
        return null;
286
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291 2523
    public function getBigIntTypeDeclarationSQL(array $columnDef) : string
292
    {
293 2523
        $columnDef['integer_type'] = 'BIGINT';
294
295 2523
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301 1614
    public function getBinaryDefaultLength() : int
302
    {
303 1614
        return 1;
304
    }
305
306
    /**
307
     * {@inheritdoc}
308
     */
309 1
    public function getBinaryMaxLength() : int
310
    {
311 1
        return 32767;
312
    }
313
314
    /**
315
     * {@inheritdoc}
316
     */
317 2523
    public function getBlobTypeDeclarationSQL(array $field) : string
318
    {
319 2523
        return 'LONG BINARY';
320
    }
321
322
    /**
323
     * {@inheritdoc}
324
     *
325
     * BIT type columns require an explicit NULL declaration
326
     * in SQL Anywhere if they shall be nullable.
327
     * Otherwise by just omitting the NOT NULL clause,
328
     * SQL Anywhere will declare them NOT NULL nonetheless.
329
     */
330 2524
    public function getBooleanTypeDeclarationSQL(array $columnDef) : string
331
    {
332 2524
        $nullClause = isset($columnDef['notnull']) && (bool) $columnDef['notnull'] === false ? ' NULL' : '';
333
334 2524
        return 'BIT' . $nullClause;
335
    }
336
337
    /**
338
     * {@inheritdoc}
339
     */
340 2525
    public function getClobTypeDeclarationSQL(array $field) : string
341
    {
342 2525
        return 'TEXT';
343
    }
344
345
    /**
346
     * {@inheritdoc}
347
     */
348 2738
    public function getCommentOnColumnSQL(string $tableName, string $columnName, ?string $comment) : string
349
    {
350 2738
        $tableName  = new Identifier($tableName);
351 2738
        $columnName = new Identifier($columnName);
352 2738
        $comment    = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
353
354 2738
        return sprintf(
355 8
            'COMMENT ON COLUMN %s.%s IS %s',
356 2738
            $tableName->getQuotedName($this),
357 2738
            $columnName->getQuotedName($this),
358 2738
            $comment
359
        );
360
    }
361
362
    /**
363
     * {@inheritdoc}
364
     */
365 1977
    public function getConcatExpression(string ...$string) : string
366
    {
367 1977
        return 'STRING(' . implode(', ', $string) . ')';
368
    }
369
370
    /**
371
     * {@inheritdoc}
372
     */
373 2187
    public function getCreateConstraintSQL(Constraint $constraint, $table) : string
374
    {
375 2187
        if ($constraint instanceof ForeignKeyConstraint) {
376 911
            return $this->getCreateForeignKeySQL($constraint, $table);
377
        }
378
379 2187
        if ($table instanceof Table) {
380 2185
            $table = $table->getQuotedName($this);
381
        }
382
383 2187
        return 'ALTER TABLE ' . $table .
384 2187
               ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this));
385
    }
386
387
    /**
388
     * {@inheritdoc}
389
     */
390 2471
    public function getCreateDatabaseSQL(string $database) : string
391
    {
392 2471
        $database = new Identifier($database);
393
394 2471
        return "CREATE DATABASE '" . $database->getName() . "'";
395
    }
396
397
    /**
398
     * {@inheritdoc}
399
     *
400
     * Appends SQL Anywhere specific flags if given.
401
     */
402 2817
    public function getCreateIndexSQL(Index $index, $table) : string
403
    {
404 2817
        return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index);
405
    }
406
407
    /**
408
     * {@inheritdoc}
409
     */
410 2394
    public function getCreatePrimaryKeySQL(Index $index, $table) : string
411
    {
412 2394
        if ($table instanceof Table) {
413 2393
            $table = $table->getQuotedName($this);
414
        }
415
416 2394
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
417
    }
418
419
    /**
420
     * {@inheritdoc}
421
     */
422 2471
    public function getCreateTemporaryTableSnippetSQL() : string
423
    {
424 2471
        return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE';
425
    }
426
427
    /**
428
     * {@inheritdoc}
429
     */
430 2471
    public function getCreateViewSQL(string $name, string $sql) : string
431
    {
432 2471
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
433
    }
434
435
    /**
436
     * {@inheritdoc}
437
     */
438 1978
    public function getCurrentDateSQL() : string
439
    {
440 1978
        return 'CURRENT DATE';
441
    }
442
443
    /**
444
     * {@inheritdoc}
445
     */
446 1977
    public function getCurrentTimeSQL() : string
447
    {
448 1977
        return 'CURRENT TIME';
449
    }
450
451
    /**
452
     * {@inheritdoc}
453
     */
454 1978
    public function getCurrentTimestampSQL() : string
455
    {
456 1978
        return 'CURRENT TIMESTAMP';
457
    }
458
459
    /**
460
     * {@inheritdoc}
461
     */
462 1977
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
463
    {
464 1977
        $factorClause = '';
465
466 1977
        if ($operator === '-') {
467 1977
            $factorClause = '-1 * ';
468
        }
469
470 1977
        return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
471
    }
472
473
    /**
474
     * {@inheritdoc}
475
     */
476 1977
    public function getDateDiffExpression(string $date1, string $date2) : string
477
    {
478 1977
        return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')';
479
    }
480
481
    /**
482
     * {@inheritdoc}
483
     */
484 1977
    public function getDateTimeFormatString() : string
485
    {
486 1977
        return 'Y-m-d H:i:s.u';
487
    }
488
489
    /**
490
     * {@inheritdoc}
491
     */
492 2523
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
493
    {
494 2523
        return 'DATETIME';
495
    }
496
497
    /**
498
     * {@inheritdoc}
499
     */
500 1951
    public function getDateTimeTzFormatString() : string
501
    {
502 1951
        return 'Y-m-d H:i:s.uP';
503
    }
504
505
    /**
506
     * {@inheritdoc}
507
     */
508 2523
    public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
509
    {
510 2523
        return 'DATE';
511
    }
512
513
    /**
514
     * {@inheritdoc}
515
     */
516 1873
    public function getDefaultTransactionIsolationLevel() : int
517
    {
518 1873
        return TransactionIsolationLevel::READ_UNCOMMITTED;
519
    }
520
521
    /**
522
     * {@inheritdoc}
523
     */
524 2471
    public function getDropDatabaseSQL(string $database) : string
525
    {
526 2471
        $database = new Identifier($database);
527
528 2471
        return "DROP DATABASE '" . $database->getName() . "'";
529
    }
530
531
    /**
532
     * {@inheritdoc}
533
     */
534 2057
    public function getDropIndexSQL($index, $table = null) : string
535
    {
536 2057
        if ($index instanceof Index) {
537 2055
            $index = $index->getQuotedName($this);
538
        }
539
540 2057
        if (! is_string($index)) {
541 2029
            throw new InvalidArgumentException(
542 2029
                sprintf('SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be a string or an instance of %s.', Index::class)
543
            );
544
        }
545
546 2056
        if (! isset($table)) {
547 2055
            return 'DROP INDEX ' . $index;
548
        }
549
550 2056
        if ($table instanceof Table) {
551 2055
            $table = $table->getQuotedName($this);
552
        }
553
554 2056
        if (! is_string($table)) {
555 2003
            throw new InvalidArgumentException(
556 2003
                sprintf('SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be a string or an instance of %s.', Index::class)
557
            );
558
        }
559
560 2055
        return 'DROP INDEX ' . $table . '.' . $index;
561
    }
562
563
    /**
564
     * {@inheritdoc}
565
     */
566 2471
    public function getDropViewSQL(string $name) : string
567
    {
568 2471
        return 'DROP VIEW ' . $name;
569
    }
570
571
    /**
572
     * {@inheritdoc}
573
     */
574 2792
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) : string
575
    {
576 2792
        $sql              = '';
577 2792
        $foreignKeyName   = $foreignKey->getName();
578 2792
        $localColumns     = $foreignKey->getQuotedLocalColumns($this);
579 2792
        $foreignColumns   = $foreignKey->getQuotedForeignColumns($this);
580 2792
        $foreignTableName = $foreignKey->getQuotedForeignTableName($this);
581
582 2792
        if (! empty($foreignKeyName)) {
583 2788
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
584
        }
585
586 2792
        if (empty($localColumns)) {
587 2263
            throw new InvalidArgumentException('Incomplete definition. "local" required.');
588
        }
589
590 2791
        if (empty($foreignColumns)) {
591 2237
            throw new InvalidArgumentException('Incomplete definition. "foreign" required.');
592
        }
593
594 2790
        if (empty($foreignTableName)) {
595 2211
            throw new InvalidArgumentException('Incomplete definition. "foreignTable" required.');
596
        }
597
598 2789
        if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) {
599 2341
            $sql .= 'NOT NULL ';
600
        }
601
602
        return $sql .
603 2789
            'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' .
604 2789
            'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) .
605 2789
            ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')';
606
    }
607
608
    /**
609
     * Returns foreign key MATCH clause for given type.
610
     *
611
     * @param int $type The foreign key match type
612
     *
613
     * @throws InvalidArgumentException If unknown match type given.
614
     */
615 2343
    public function getForeignKeyMatchClauseSQL(int $type) : string
616
    {
617
        switch ($type) {
618 2343
            case self::FOREIGN_KEY_MATCH_SIMPLE:
619 2315
                return 'SIMPLE';
620
621
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
622 2343
            case self::FOREIGN_KEY_MATCH_FULL:
623 2315
                return 'FULL';
624
625
                break;
626 2343
            case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE:
627 2342
                return 'UNIQUE SIMPLE';
628
629
                break;
630 2316
            case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
631 2315
                return 'UNIQUE FULL';
632
            default:
633 2289
                throw new InvalidArgumentException(sprintf('Invalid foreign key match type "%s".', $type));
634
        }
635
    }
636
637
    /**
638
     * {@inheritdoc}
639
     */
640 2348
    public function getForeignKeyReferentialActionSQL(string $action) : string
641
    {
642
        // NO ACTION is not supported, therefore falling back to RESTRICT.
643 2348
        if (strtoupper($action) === 'NO ACTION') {
644 1275
            return 'RESTRICT';
645
        }
646
647 2347
        return parent::getForeignKeyReferentialActionSQL($action);
648
    }
649
650
    /**
651
     * {@inheritdoc}
652
     */
653 1977
    public function getForUpdateSQL() : string
654
    {
655 1977
        return '';
656
    }
657
658
    /**
659
     * {@inheritdoc}
660
     */
661 2524
    public function getGuidTypeDeclarationSQL(array $field) : string
662
    {
663 2524
        return 'UNIQUEIDENTIFIER';
664
    }
665
666
    /**
667
     * {@inheritdoc}
668
     */
669 2082
    public function getIndexDeclarationSQL(string $name, Index $index) : string
670
    {
671
        // Index declaration in statements like CREATE TABLE is not supported.
672 2082
        throw NotSupported::new(__METHOD__);
673
    }
674
675
    /**
676
     * {@inheritdoc}
677
     */
678 2819
    public function getIntegerTypeDeclarationSQL(array $columnDef) : string
679
    {
680 2819
        $columnDef['integer_type'] = 'INT';
681
682 2819
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
683
    }
684
685
    /**
686
     * {@inheritdoc}
687
     */
688
    public function getListDatabasesSQL() : string
689
    {
690
        return 'SELECT db_name(number) AS name FROM sa_db_list()';
691
    }
692
693
    /**
694
     * {@inheritdoc}
695
     */
696 1535
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
697
    {
698 1535
        $user = 'USER_NAME()';
699
700 1535
        if (strpos($table, '.') !== false) {
701 1535
            [$user, $table] = explode('.', $table);
702 1535
            $user           = $this->quoteStringLiteral($user);
703
        }
704
705 1535
        return sprintf(
706
            <<<'SQL'
707 1
SELECT    col.column_name,
708
          COALESCE(def.user_type_name, def.domain_name) AS 'type',
709
          def.declared_width AS 'length',
710
          def.scale,
711
          CHARINDEX('unsigned', def.domain_name) AS 'unsigned',
712
          IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull',
713
          col."default",
714
          def.is_autoincrement AS 'autoincrement',
715
          rem.remarks AS 'comment'
716
FROM      sa_describe_query('SELECT * FROM "%s"') AS def
717
JOIN      SYS.SYSTABCOL AS col
718
ON        col.table_id = def.base_table_id AND col.column_id = def.base_column_id
719
LEFT JOIN SYS.SYSREMARK AS rem
720
ON        col.object_id = rem.object_id
721
WHERE     def.base_owner_name = %s
722
ORDER BY  def.base_column_id ASC
723
SQL
724
            ,
725 1535
            $table,
726 1535
            $user
727
        );
728
    }
729
730
    /**
731
     * {@inheritdoc}
732
     *
733
     * @todo Where is this used? Which information should be retrieved?
734
     */
735 1510
    public function getListTableConstraintsSQL(string $table) : string
736
    {
737 1510
        $user = '';
738
739 1510
        if (strpos($table, '.') !== false) {
740 1483
            [$user, $table] = explode('.', $table);
741 1483
            $user           = $this->quoteStringLiteral($user);
742 1483
            $table          = $this->quoteStringLiteral($table);
743
        } else {
744 1509
            $table = $this->quoteStringLiteral($table);
745
        }
746
747 1510
        return sprintf(
748
            <<<'SQL'
749 2
SELECT con.*
750
FROM   SYS.SYSCONSTRAINT AS con
751
JOIN   SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id
752
WHERE  tab.table_name = %s
753
AND    tab.creator = USER_ID(%s)
754
SQL
755
            ,
756 1510
            $table,
757 1510
            $user
758
        );
759
    }
760
761
    /**
762
     * {@inheritdoc}
763
     */
764 1458
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
765
    {
766 1458
        $user = '';
767
768 1458
        if (strpos($table, '.') !== false) {
769 1431
            [$user, $table] = explode('.', $table);
770 1431
            $user           = $this->quoteStringLiteral($user);
771 1431
            $table          = $this->quoteStringLiteral($table);
772
        } else {
773 1457
            $table = $this->quoteStringLiteral($table);
774
        }
775
776 1458
        return sprintf(
777
            <<<'SQL'
778 2
SELECT    fcol.column_name AS local_column,
779
          ptbl.table_name AS foreign_table,
780
          pcol.column_name AS foreign_column,
781
          idx.index_name,
782
          IF fk.nulls = 'N'
783
              THEN 1
784
              ELSE NULL
785
          ENDIF AS notnull,
786
          CASE ut.referential_action
787
              WHEN 'C' THEN 'CASCADE'
788
              WHEN 'D' THEN 'SET DEFAULT'
789
              WHEN 'N' THEN 'SET NULL'
790
              WHEN 'R' THEN 'RESTRICT'
791
              ELSE NULL
792
          END AS  on_update,
793
          CASE dt.referential_action
794
              WHEN 'C' THEN 'CASCADE'
795
              WHEN 'D' THEN 'SET DEFAULT'
796
              WHEN 'N' THEN 'SET NULL'
797
              WHEN 'R' THEN 'RESTRICT'
798
              ELSE NULL
799
          END AS on_delete,
800
          IF fk.check_on_commit = 'Y'
801
              THEN 1
802
              ELSE NULL
803
          ENDIF AS check_on_commit, -- check_on_commit flag
804
          IF ftbl.clustered_index_id = idx.index_id
805
              THEN 1
806
              ELSE NULL
807
          ENDIF AS 'clustered', -- clustered flag
808
          IF fk.match_type = 0
809
              THEN NULL
810
              ELSE fk.match_type
811
          ENDIF AS 'match', -- match option
812
          IF pidx.max_key_distance = 1
813
              THEN 1
814
              ELSE NULL
815
          ENDIF AS for_olap_workload -- for_olap_workload flag
816
FROM      SYS.SYSFKEY AS fk
817
JOIN      SYS.SYSIDX AS idx
818
ON        fk.foreign_table_id = idx.table_id
819
AND       fk.foreign_index_id = idx.index_id
820
JOIN      SYS.SYSPHYSIDX pidx
821
ON        idx.table_id = pidx.table_id
822
AND       idx.phys_index_id = pidx.phys_index_id
823
JOIN      SYS.SYSTAB AS ptbl
824
ON        fk.primary_table_id = ptbl.table_id
825
JOIN      SYS.SYSTAB AS ftbl
826
ON        fk.foreign_table_id = ftbl.table_id
827
JOIN      SYS.SYSIDXCOL AS idxcol
828
ON        idx.table_id = idxcol.table_id
829
AND       idx.index_id = idxcol.index_id
830
JOIN      SYS.SYSTABCOL AS pcol
831
ON        ptbl.table_id = pcol.table_id
832
AND       idxcol.primary_column_id = pcol.column_id
833
JOIN      SYS.SYSTABCOL AS fcol
834
ON        ftbl.table_id = fcol.table_id
835
AND       idxcol.column_id = fcol.column_id
836
LEFT JOIN SYS.SYSTRIGGER ut
837
ON        fk.foreign_table_id = ut.foreign_table_id
838
AND       fk.foreign_index_id = ut.foreign_key_id
839
AND       ut.event = 'C'
840
LEFT JOIN SYS.SYSTRIGGER dt
841
ON        fk.foreign_table_id = dt.foreign_table_id
842
AND       fk.foreign_index_id = dt.foreign_key_id
843
AND       dt.event = 'D'
844
WHERE     ftbl.table_name = %s
845
AND       ftbl.creator = USER_ID(%s)
846
ORDER BY  fk.foreign_index_id ASC, idxcol.sequence ASC
847
SQL
848
            ,
849 1458
            $table,
850 1458
            $user
851
        );
852
    }
853
854
    /**
855
     * {@inheritdoc}
856
     */
857 1406
    public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string
858
    {
859 1406
        $user = '';
860
861 1406
        if (strpos($table, '.') !== false) {
862 1379
            [$user, $table] = explode('.', $table);
863 1379
            $user           = $this->quoteStringLiteral($user);
864 1379
            $table          = $this->quoteStringLiteral($table);
865
        } else {
866 1405
            $table = $this->quoteStringLiteral($table);
867
        }
868
869 1406
        return sprintf(
870
            <<<'SQL'
871 2
SELECT   idx.index_name AS key_name,
872
         IF idx.index_category = 1
873
             THEN 1
874
             ELSE 0
875
         ENDIF AS 'primary',
876
         col.column_name,
877
         IF idx."unique" IN(1, 2, 5)
878
             THEN 0
879
             ELSE 1
880
         ENDIF AS non_unique,
881
         IF tbl.clustered_index_id = idx.index_id
882
             THEN 1
883
             ELSE NULL
884
         ENDIF AS 'clustered', -- clustered flag
885
         IF idx."unique" = 5
886
             THEN 1
887
             ELSE NULL
888
         ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag
889
         IF pidx.max_key_distance = 1
890
              THEN 1
891
              ELSE NULL
892
          ENDIF AS for_olap_workload -- for_olap_workload flag
893
FROM     SYS.SYSIDX AS idx
894
JOIN     SYS.SYSPHYSIDX pidx
895
ON       idx.table_id = pidx.table_id
896
AND      idx.phys_index_id = pidx.phys_index_id
897
JOIN     SYS.SYSIDXCOL AS idxcol
898
ON       idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id
899
JOIN     SYS.SYSTABCOL AS col
900
ON       idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id
901
JOIN     SYS.SYSTAB AS tbl
902
ON       idx.table_id = tbl.table_id
903
WHERE    tbl.table_name = %s
904
AND      tbl.creator = USER_ID(%s)
905
AND      idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints
906
ORDER BY idx.index_id ASC, idxcol.sequence ASC
907
SQL
908
            ,
909 1406
            $table,
910 1406
            $user
911
        );
912
    }
913
914
    /**
915
     * {@inheritdoc}
916
     */
917
    public function getListTablesSQL() : string
918
    {
919
        return "SELECT   tbl.table_name
920
                FROM     SYS.SYSTAB AS tbl
921
                JOIN     SYS.SYSUSER AS usr ON tbl.creator = usr.user_id
922
                JOIN     dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id
923
                WHERE    tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP'
924
                AND      usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
925
                AND      obj.type = 'U' -- user created tables only
926
                ORDER BY tbl.table_name ASC";
927
    }
928
929
    /**
930
     * {@inheritdoc}
931
     *
932
     * @todo Where is this used? Which information should be retrieved?
933
     */
934
    public function getListUsersSQL() : string
935
    {
936
        return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC';
937
    }
938
939
    /**
940
     * {@inheritdoc}
941
     */
942
    public function getListViewsSQL(string $database) : string
943
    {
944
        return "SELECT   tbl.table_name, v.view_def
945
                FROM     SYS.SYSVIEW v
946
                JOIN     SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id
947
                JOIN     SYS.SYSUSER usr ON tbl.creator = usr.user_id
948
                JOIN     dbo.SYSOBJECTS obj ON tbl.object_id = obj.id
949
                WHERE    usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
950
                ORDER BY tbl.table_name ASC";
951
    }
952
953
    /**
954
     * {@inheritdoc}
955
     */
956 1977
    public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
957
    {
958 1977
        if ($start === null) {
959 1977
            return sprintf('LOCATE(%s, %s)', $string, $substring);
960
        }
961
962 1977
        return sprintf('LOCATE(%s, %s, %s)', $string, $substring, $start);
963
    }
964
965
    /**
966
     * {@inheritdoc}
967
     */
968 2576
    public function getMaxIdentifierLength() : int
969
    {
970 2576
        return 128;
971
    }
972
973
    /**
974
     * {@inheritdoc}
975
     */
976 1977
    public function getMd5Expression(string $string) : string
977
    {
978 1977
        return 'HASH(' . $string . ", 'MD5')";
979
    }
980
981
    /**
982
     * {@inheritdoc}
983
     */
984
    public function getRegexpExpression() : string
985
    {
986
        return 'REGEXP';
987
    }
988
989
    /**
990
     * {@inheritdoc}
991
     */
992 2837
    public function getName() : string
993
    {
994 2837
        return 'sqlanywhere';
995
    }
996
997
    /**
998
     * Obtain DBMS specific SQL code portion needed to set a primary key
999
     * declaration to be used in statements like ALTER TABLE.
1000
     *
1001
     * @param Index  $index Index definition
1002
     * @param string $name  Name of the primary key
1003
     *
1004
     * @return string DBMS specific SQL code portion needed to set a primary key
1005
     *
1006
     * @throws InvalidArgumentException If the given index is not a primary key.
1007
     */
1008 2448
    public function getPrimaryKeyDeclarationSQL(Index $index, ?string $name = null) : string
1009
    {
1010 2448
        if (! $index->isPrimary()) {
1011
            throw new InvalidArgumentException(
1012
                'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()'
1013
            );
1014
        }
1015
1016 2448
        return $this->getTableConstraintDeclarationSQL($index, $name);
1017
    }
1018
1019
    /**
1020
     * {@inheritdoc}
1021
     */
1022 1847
    public function getSetTransactionIsolationSQL(int $level) : string
1023
    {
1024 1847
        return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level);
1025
    }
1026
1027
    /**
1028
     * {@inheritdoc}
1029
     */
1030 2523
    public function getSmallIntTypeDeclarationSQL(array $columnDef) : string
1031
    {
1032 2523
        $columnDef['integer_type'] = 'SMALLINT';
1033
1034 2523
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
1035
    }
1036
1037
    /**
1038
     * Returns the SQL statement for starting an existing database.
1039
     *
1040
     * In SQL Anywhere you can start and stop databases on a
1041
     * database server instance.
1042
     * This is a required statement after having created a new database
1043
     * as it has to be explicitly started to be usable.
1044
     * SQL Anywhere does not automatically start a database after creation!
1045
     *
1046
     * @param string $database Name of the database to start.
1047
     */
1048 2471
    public function getStartDatabaseSQL(string $database) : string
1049
    {
1050 2471
        $database = new Identifier($database);
1051
1052 2471
        return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF";
1053
    }
1054
1055
    /**
1056
     * Returns the SQL statement for stopping a running database.
1057
     *
1058
     * In SQL Anywhere you can start and stop databases on a
1059
     * database server instance.
1060
     * This is a required statement before dropping an existing database
1061
     * as it has to be explicitly stopped before it can be dropped.
1062
     *
1063
     * @param string $database Name of the database to stop.
1064
     */
1065 2471
    public function getStopDatabaseSQL(string $database) : string
1066
    {
1067 2471
        $database = new Identifier($database);
1068
1069 2471
        return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY';
1070
    }
1071
1072
    /**
1073
     * {@inheritdoc}
1074
     */
1075 1977
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
1076
    {
1077 1977
        if ($length === null) {
1078 1977
            return sprintf('SUBSTRING(%s, %s)', $string, $start);
1079
        }
1080
1081 1977
        return sprintf('SUBSTRING(%s, %s, %s)', $string, $start, $length);
1082
    }
1083
1084
    /**
1085
     * {@inheritdoc}
1086
     */
1087 2472
    public function getTemporaryTableSQL() : string
1088
    {
1089 2472
        return 'GLOBAL TEMPORARY';
1090
    }
1091
1092
    /**
1093
     * {@inheritdoc}
1094
     */
1095 1977
    public function getTimeFormatString() : string
1096
    {
1097 1977
        return 'H:i:s.u';
1098
    }
1099
1100
    /**
1101
     * {@inheritdoc}
1102
     */
1103 2523
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
1104
    {
1105 2523
        return 'TIME';
1106
    }
1107
1108
    /**
1109
     * {@inheritdoc}
1110
     */
1111 1977
    public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED, ?string $char = null) : string
1112
    {
1113 1977
        if (! in_array($mode, [TrimMode::UNSPECIFIED, TrimMode::LEADING, TrimMode::TRAILING, TrimMode::BOTH], true)) {
1114
            throw new InvalidArgumentException(
1115
                sprintf('The value of $mode is expected to be one of the TrimMode constants, %d given', $mode)
1116
            );
1117
        }
1118
1119 1977
        if ($char === null) {
1120 1977
            switch ($mode) {
1121
                case TrimMode::LEADING:
1122 1977
                    return $this->getLtrimExpression($str);
1123
                case TrimMode::TRAILING:
1124 1977
                    return $this->getRtrimExpression($str);
1125
                default:
1126 1977
                    return 'TRIM(' . $str . ')';
1127
            }
1128
        }
1129
1130 1977
        $pattern = "'%[^' + " . $char . " + ']%'";
1131
1132 1977
        switch ($mode) {
1133
            case TrimMode::LEADING:
1134 1977
                return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
1135
            case TrimMode::TRAILING:
1136 1977
                return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
1137
            default:
1138 1977
                return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
1139 1977
                    'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
1140
        }
1141
    }
1142
1143
    /**
1144
     * {@inheritdoc}
1145
     */
1146 2472
    public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string
1147
    {
1148 2472
        $tableIdentifier = new Identifier($tableName);
1149
1150 2472
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1151
    }
1152
1153
    /**
1154
     * {@inheritdoc}
1155
     */
1156 1665
    public function getCreateSequenceSQL(Sequence $sequence) : string
1157
    {
1158 1665
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
1159 1665
            ' INCREMENT BY ' . $sequence->getAllocationSize() .
1160 1665
            ' START WITH ' . $sequence->getInitialValue() .
1161 1665
            ' MINVALUE ' . $sequence->getInitialValue();
1162
    }
1163
1164
    /**
1165
     * {@inheritdoc}
1166
     */
1167 1665
    public function getAlterSequenceSQL(Sequence $sequence) : string
1168
    {
1169 1665
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
1170 1665
            ' INCREMENT BY ' . $sequence->getAllocationSize();
1171
    }
1172
1173
    /**
1174
     * {@inheritdoc}
1175
     */
1176 1665
    public function getDropSequenceSQL($sequence) : string
1177
    {
1178 1665
        if ($sequence instanceof Sequence) {
1179 1665
            $sequence = $sequence->getQuotedName($this);
1180
        }
1181
1182 1665
        return 'DROP SEQUENCE ' . $sequence;
1183
    }
1184
1185
    /**
1186
     * {@inheritdoc}
1187
     */
1188
    public function getListSequencesSQL(string $database) : string
1189
    {
1190
        return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE';
1191
    }
1192
1193
    /**
1194
     * {@inheritdoc}
1195
     */
1196 1665
    public function getSequenceNextValSQL(string $sequenceName) : string
1197
    {
1198 1665
        return 'SELECT ' . $sequenceName . '.NEXTVAL';
1199
    }
1200
1201
    /**
1202
     * {@inheritdoc}
1203
     */
1204 1691
    public function supportsSequences() : bool
1205
    {
1206 1691
        return true;
1207
    }
1208
1209
    /**
1210
     * {@inheritdoc}
1211
     */
1212 1925
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) : string
1213
    {
1214 1925
        return 'TIMESTAMP WITH TIME ZONE';
1215
    }
1216
1217
    /**
1218
     * {@inheritdoc}
1219
     */
1220 2526
    public function getVarcharDefaultLength() : int
1221
    {
1222 2526
        return 1;
1223
    }
1224
1225
    /**
1226
     * {@inheritdoc}
1227
     */
1228 2821
    public function getVarcharMaxLength() : int
1229
    {
1230 2821
        return 32767;
1231
    }
1232
1233
    /**
1234
     * {@inheritdoc}
1235
     */
1236 2852
    public function hasNativeGuidType() : bool
1237
    {
1238 2852
        return true;
1239
    }
1240
1241
    /**
1242
     * {@inheritdoc}
1243
     */
1244 1717
    public function prefersIdentityColumns() : bool
1245
    {
1246 1717
        return true;
1247
    }
1248
1249
    /**
1250
     * {@inheritdoc}
1251
     */
1252 2821
    public function supportsCommentOnStatement() : bool
1253
    {
1254 2821
        return true;
1255
    }
1256
1257
    /**
1258
     * {@inheritdoc}
1259
     */
1260 1
    public function supportsIdentityColumns() : bool
1261
    {
1262 1
        return true;
1263
    }
1264
1265
    /**
1266
     * {@inheritdoc}
1267
     */
1268 2819
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string
1269
    {
1270 2819
        $unsigned      = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : '';
1271 2819
        $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : '';
1272
1273 2819
        return $unsigned . $columnDef['integer_type'] . $autoincrement;
1274
    }
1275
1276
    /**
1277
     * {@inheritdoc}
1278
     */
1279 2820
    protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array
1280
    {
1281 2820
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1282 2820
        $indexSql      = [];
1283
1284 2820
        if (! empty($options['uniqueConstraints'])) {
1285
            foreach ((array) $options['uniqueConstraints'] as $name => $definition) {
1286
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1287
            }
1288
        }
1289
1290 2820
        if (! empty($options['indexes'])) {
1291
            /** @var Index $index */
1292 2812
            foreach ((array) $options['indexes'] as $index) {
1293 2812
                $indexSql[] = $this->getCreateIndexSQL($index, $tableName);
1294
            }
1295
        }
1296
1297 2820
        if (! empty($options['primary'])) {
1298 2814
            $flags = '';
1299
1300 2814
            if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) {
1301
                $flags = ' CLUSTERED ';
1302
            }
1303
1304 2814
            $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')';
1305
        }
1306
1307 2820
        if (! empty($options['foreignKeys'])) {
1308 2784
            foreach ((array) $options['foreignKeys'] as $definition) {
1309 2784
                $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition);
1310
            }
1311
        }
1312
1313 2820
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1314 2820
        $check = $this->getCheckDeclarationSQL($columns);
1315
1316 2820
        if (! empty($check)) {
1317 2757
            $query .= ', ' . $check;
1318
        }
1319
1320 2820
        $query .= ')';
1321
1322 2820
        return array_merge([$query], $indexSql);
1323
    }
1324
1325
    /**
1326
     * {@inheritdoc}
1327
     */
1328 1847
    protected function _getTransactionIsolationLevelSQL(int $level) : string
1329
    {
1330
        switch ($level) {
1331 1847
            case TransactionIsolationLevel::READ_UNCOMMITTED:
1332 1847
                return '0';
1333 1847
            case TransactionIsolationLevel::READ_COMMITTED:
1334 1847
                return '1';
1335 1847
            case TransactionIsolationLevel::REPEATABLE_READ:
1336 1847
                return '2';
1337 1847
            case TransactionIsolationLevel::SERIALIZABLE:
1338 1847
                return '3';
1339
            default:
1340
                throw new InvalidArgumentException(sprintf('Invalid isolation level %d.', $level));
1341
        }
1342
    }
1343
1344
    /**
1345
     * {@inheritdoc}
1346
     */
1347 1826
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
1348
    {
1349 1826
        $limitOffsetClause = $this->getTopClauseSQL($limit, $offset);
1350
1351 1826
        if ($limitOffsetClause === '') {
1352 27
            return $query;
1353
        }
1354
1355 1825
        if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) {
1356
            return $query;
1357
        }
1358
1359 1825
        return $matches[1] . $limitOffsetClause . ' ' . $matches[3];
1360
    }
1361
1362 1826
    private function getTopClauseSQL(?int $limit, ?int $offset) : string
1363
    {
1364 1826
        if ($offset > 0) {
1365 1796
            return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1);
1366
        }
1367
1368 1824
        return $limit === null ? '' : 'TOP ' . $limit;
1369
    }
1370
1371
    /**
1372
     * Return the INDEX query section dealing with non-standard
1373
     * SQL Anywhere options.
1374
     *
1375
     * @param Index $index Index definition
1376
     */
1377 2817
    protected function getAdvancedIndexOptionsSQL(Index $index) : string
1378
    {
1379 2817
        if ($index->hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) {
1380 2107
            throw new UnexpectedValueException(
1381 2107
                'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.'
1382
            );
1383
        }
1384
1385 2816
        $sql = '';
1386
1387 2816
        if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) {
1388 2133
            $sql .= ' FOR OLAP WORKLOAD';
1389
        }
1390
1391 2816
        if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) {
1392 2133
            return ' WITH NULLS NOT DISTINCT' . $sql;
1393
        }
1394
1395 2816
        if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) {
1396 2133
            return ' WITH NULLS DISTINCT' . $sql;
1397
        }
1398
1399 2816
        return $sql;
1400
    }
1401
1402
    /**
1403
     * {@inheritdoc}
1404
     */
1405 1613
    protected function getBinaryTypeDeclarationSQLSnippet(int $length, bool $fixed) : string
1406
    {
1407 1613
        return $fixed
1408 1613
            ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'
1409 1613
            : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')';
1410
    }
1411
1412
    /**
1413
     * Returns the SQL snippet for creating a table constraint.
1414
     *
1415
     * @param Constraint  $constraint The table constraint to create the SQL snippet for.
1416
     * @param string|null $name       The table constraint name to use if any.
1417
     *
1418
     * @throws InvalidArgumentException If the given table constraint type is not supported by this method.
1419
     */
1420 2451
    protected function getTableConstraintDeclarationSQL(Constraint $constraint, ?string $name = null) : string
1421
    {
1422 2451
        if ($constraint instanceof ForeignKeyConstraint) {
1423
            return $this->getForeignKeyDeclarationSQL($constraint);
1424
        }
1425
1426 2451
        if (! $constraint instanceof Index) {
1427 2159
            throw new InvalidArgumentException(sprintf('Unsupported constraint type %s.', get_class($constraint)));
1428
        }
1429
1430 2450
        if (! $constraint->isPrimary() && ! $constraint->isUnique()) {
1431 2185
            throw new InvalidArgumentException(
1432
                'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' .
1433 2185
                'with getTableConstraintDeclarationSQL().'
1434
            );
1435
        }
1436
1437 2449
        $constraintColumns = $constraint->getQuotedColumns($this);
1438
1439 2449
        if (empty($constraintColumns)) {
1440 2419
            throw new InvalidArgumentException('Incomplete definition. "columns" required.');
1441
        }
1442
1443 2448
        $sql   = '';
1444 2448
        $flags = '';
1445
1446 2448
        if (! empty($name)) {
1447 2446
            $name = new Identifier($name);
1448 2446
            $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' ';
1449
        }
1450
1451 2448
        if ($constraint->hasFlag('clustered')) {
1452 2446
            $flags = 'CLUSTERED ';
1453
        }
1454
1455 2448
        if ($constraint->isPrimary()) {
1456 2448
            return $sql . 'PRIMARY KEY ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1457
        }
1458
1459 911
        return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1460
    }
1461
1462
    /**
1463
     * {@inheritdoc}
1464
     */
1465 2817
    protected function getCreateIndexSQLFlags(Index $index) : string
1466
    {
1467 2817
        $type = '';
1468 2817
        if ($index->hasFlag('virtual')) {
1469 2133
            $type .= 'VIRTUAL ';
1470
        }
1471
1472 2817
        if ($index->isUnique()) {
1473 2135
            $type .= 'UNIQUE ';
1474
        }
1475
1476 2817
        if ($index->hasFlag('clustered')) {
1477 2133
            $type .= 'CLUSTERED ';
1478
        }
1479
1480 2817
        return $type;
1481
    }
1482
1483
    /**
1484
     * {@inheritdoc}
1485
     */
1486 369
    protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array
1487
    {
1488 369
        return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)];
1489
    }
1490
1491
    /**
1492
     * {@inheritdoc}
1493
     */
1494 2862
    protected function getReservedKeywordsClass() : string
1495
    {
1496 2862
        return Keywords\SQLAnywhereKeywords::class;
1497
    }
1498
1499
    /**
1500
     * {@inheritdoc}
1501
     */
1502 2820
    protected function getVarcharTypeDeclarationSQLSnippet(int $length, bool $fixed) : string
1503
    {
1504 2820
        return $fixed
1505 79
            ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')')
1506 2820
            : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')');
1507
    }
1508
1509
    /**
1510
     * {@inheritdoc}
1511
     */
1512 1904
    protected function initializeDoctrineTypeMappings() : void
1513
    {
1514 1904
        $this->doctrineTypeMapping = [
1515
            'bigint'                   => 'bigint',
1516
            'binary'                   => 'binary',
1517
            'bit'                      => 'boolean',
1518
            'char'                     => 'string',
1519
            'decimal'                  => 'decimal',
1520
            'date'                     => 'date',
1521
            'datetime'                 => 'datetime',
1522
            'double'                   => 'float',
1523
            'float'                    => 'float',
1524
            'image'                    => 'blob',
1525
            'int'                      => 'integer',
1526
            'integer'                  => 'integer',
1527
            'long binary'              => 'blob',
1528
            'long nvarchar'            => 'text',
1529
            'long varbit'              => 'text',
1530
            'long varchar'             => 'text',
1531
            'money'                    => 'decimal',
1532
            'nchar'                    => 'string',
1533
            'ntext'                    => 'text',
1534
            'numeric'                  => 'decimal',
1535
            'nvarchar'                 => 'string',
1536
            'smalldatetime'            => 'datetime',
1537
            'smallint'                 => 'smallint',
1538
            'smallmoney'               => 'decimal',
1539
            'text'                     => 'text',
1540
            'time'                     => 'time',
1541
            'timestamp'                => 'datetime',
1542
            'timestamp with time zone' => 'datetime',
1543
            'tinyint'                  => 'smallint',
1544
            'uniqueidentifier'         => 'guid',
1545
            'uniqueidentifierstr'      => 'guid',
1546
            'unsigned bigint'          => 'bigint',
1547
            'unsigned int'             => 'integer',
1548
            'unsigned smallint'        => 'smallint',
1549
            'unsigned tinyint'         => 'smallint',
1550
            'varbinary'                => 'binary',
1551
            'varbit'                   => 'string',
1552
            'varchar'                  => 'string',
1553
            'xml'                      => 'text',
1554
        ];
1555 1904
    }
1556
}
1557