Failed Conditions
Push — 3.0.x ( f82f5c...40cc9b )
by Grégoire
24s queued 15s
created

SQLAnywhere16Platform   F

Complexity

Total Complexity 211

Size/Duplication

Total Lines 1560
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 211
eloc 451
dl 0
loc 1560
rs 2
c 0
b 0
f 0

96 Methods

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

How to fix   Complexity   

Complex Class

Complex classes like SQLAnywhere16Platform 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 SQLAnywhere16Platform, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\LockMode;
7
use Doctrine\DBAL\Schema\Column;
8
use Doctrine\DBAL\Schema\ColumnDiff;
9
use Doctrine\DBAL\Schema\Constraint;
10
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
11
use Doctrine\DBAL\Schema\Identifier;
12
use Doctrine\DBAL\Schema\Index;
13
use Doctrine\DBAL\Schema\Sequence;
14
use Doctrine\DBAL\Schema\Table;
15
use Doctrine\DBAL\Schema\TableDiff;
16
use Doctrine\DBAL\TransactionIsolationLevel;
17
use InvalidArgumentException;
18
use UnexpectedValueException;
19
use function array_merge;
20
use function array_unique;
21
use function array_values;
22
use function count;
23
use function explode;
24
use function func_get_args;
25
use function get_class;
26
use function implode;
27
use function is_string;
28
use function preg_match;
29
use function sprintf;
30
use function strlen;
31
use function strpos;
32
use function strtoupper;
33
use function substr;
34
35
/**
36
 * Provides the behavior, features and SQL dialect of the SAP Sybase SQL Anywhere 16 database platform.
37
 */
38
class SQLAnywhere16Platform extends AbstractPlatform
39
{
40
    public const FOREIGN_KEY_MATCH_SIMPLE        = 1;
41
    public const FOREIGN_KEY_MATCH_FULL          = 2;
42
    public const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
43
    public const FOREIGN_KEY_MATCH_FULL_UNIQUE   = 130;
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function appendLockHint($fromClause, $lockMode)
49
    {
50
        switch (true) {
51
            case $lockMode === LockMode::NONE:
52
                return $fromClause . ' WITH (NOLOCK)';
53
54
            case $lockMode === LockMode::PESSIMISTIC_READ:
55
                return $fromClause . ' WITH (UPDLOCK)';
56
57
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
58
                return $fromClause . ' WITH (XLOCK)';
59
60
            default:
61
                return $fromClause;
62
        }
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     *
68
     * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
69
     */
70
    public function fixSchemaElementName($schemaElementName)
71
    {
72
        $maxIdentifierLength = $this->getMaxIdentifierLength();
73
74
        if (strlen($schemaElementName) > $maxIdentifierLength) {
75
            return substr($schemaElementName, 0, $maxIdentifierLength);
76
        }
77
78
        return $schemaElementName;
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
85
    {
86
        $query = '';
87
88
        if ($foreignKey->hasOption('match')) {
89
            $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
90
        }
91
92
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
93
94
        if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
95
            $query .= ' CHECK ON COMMIT';
96
        }
97
98
        if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
99
            $query .= ' CLUSTERED';
100
        }
101
102
        if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
103
            $query .= ' FOR OLAP WORKLOAD';
104
        }
105
106
        return $query;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function getAlterTableSQL(TableDiff $diff)
113
    {
114
        $sql          = [];
115
        $columnSql    = [];
116
        $commentsSQL  = [];
117
        $tableSql     = [];
118
        $alterClauses = [];
119
120
        foreach ($diff->addedColumns as $column) {
121
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
122
                continue;
123
            }
124
125
            $alterClauses[] = $this->getAlterTableAddColumnClause($column);
126
127
            $comment = $this->getColumnComment($column);
128
129
            if ($comment === null || $comment === '') {
130
                continue;
131
            }
132
133
            $commentsSQL[] = $this->getCommentOnColumnSQL(
134
                $diff->getName($this)->getQuotedName($this),
135
                $column->getQuotedName($this),
136
                $comment
137
            );
138
        }
139
140
        foreach ($diff->removedColumns as $column) {
141
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
142
                continue;
143
            }
144
145
            $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
146
        }
147
148
        foreach ($diff->changedColumns as $columnDiff) {
149
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
150
                continue;
151
            }
152
153
            $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
154
155
            if ($alterClause !== null) {
156
                $alterClauses[] = $alterClause;
157
            }
158
159
            if (! $columnDiff->hasChanged('comment')) {
160
                continue;
161
            }
162
163
            $column = $columnDiff->column;
164
165
            $commentsSQL[] = $this->getCommentOnColumnSQL(
166
                $diff->getName($this)->getQuotedName($this),
167
                $column->getQuotedName($this),
168
                $this->getColumnComment($column)
169
            );
170
        }
171
172
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
173
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
174
                continue;
175
            }
176
177
            $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
178
                $this->getAlterTableRenameColumnClause($oldColumnName, $column);
179
        }
180
181
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
182
            if (! empty($alterClauses)) {
183
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
184
            }
185
186
            $sql = array_merge($sql, $commentsSQL);
187
188
            $newName = $diff->getNewName();
189
190
            if ($newName !== false) {
191
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
192
                    $this->getAlterTableRenameTableClause($newName);
193
            }
194
195
            $sql = array_merge(
196
                $this->getPreAlterTableIndexForeignKeySQL($diff),
197
                $sql,
198
                $this->getPostAlterTableIndexForeignKeySQL($diff)
199
            );
200
        }
201
202
        return array_merge($sql, $tableSql, $columnSql);
203
    }
204
205
    /**
206
     * Returns the SQL clause for creating a column in a table alteration.
207
     *
208
     * @param Column $column The column to add.
209
     *
210
     * @return string
211
     */
212
    protected function getAlterTableAddColumnClause(Column $column)
213
    {
214
        return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
215
    }
216
217
    /**
218
     * Returns the SQL clause for altering a table.
219
     *
220
     * @param Identifier $tableName The quoted name of the table to alter.
221
     *
222
     * @return string
223
     */
224
    protected function getAlterTableClause(Identifier $tableName)
225
    {
226
        return 'ALTER TABLE ' . $tableName->getQuotedName($this);
227
    }
228
229
    /**
230
     * Returns the SQL clause for dropping a column in a table alteration.
231
     *
232
     * @param Column $column The column to drop.
233
     *
234
     * @return string
235
     */
236
    protected function getAlterTableRemoveColumnClause(Column $column)
237
    {
238
        return 'DROP ' . $column->getQuotedName($this);
239
    }
240
241
    /**
242
     * Returns the SQL clause for renaming a column in a table alteration.
243
     *
244
     * @param string $oldColumnName The quoted name of the column to rename.
245
     * @param Column $column        The column to rename to.
246
     *
247
     * @return string
248
     */
249
    protected function getAlterTableRenameColumnClause($oldColumnName, Column $column)
250
    {
251
        $oldColumnName = new Identifier($oldColumnName);
252
253
        return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
254
    }
255
256
    /**
257
     * Returns the SQL clause for renaming a table in a table alteration.
258
     *
259
     * @param Identifier $newTableName The quoted name of the table to rename to.
260
     *
261
     * @return string
262
     */
263
    protected function getAlterTableRenameTableClause(Identifier $newTableName)
264
    {
265
        return 'RENAME ' . $newTableName->getQuotedName($this);
266
    }
267
268
    /**
269
     * Returns the SQL clause for altering a column in a table alteration.
270
     *
271
     * This method returns null in case that only the column comment has changed.
272
     * Changes in column comments have to be handled differently.
273
     *
274
     * @param ColumnDiff $columnDiff The diff of the column to alter.
275
     *
276
     * @return string|null
277
     */
278
    protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff)
279
    {
280
        $column = $columnDiff->column;
281
282
        // Do not return alter clause if only comment has changed.
283
        if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) {
284
            $columnAlterationClause = 'ALTER ' .
285
                $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
286
287
            if ($columnDiff->hasChanged('default') && $column->getDefault() === null) {
288
                $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT';
289
            }
290
291
            return $columnAlterationClause;
292
        }
293
294
        return null;
295
    }
296
297
    /**
298
     * {@inheritdoc}
299
     */
300
    public function getBigIntTypeDeclarationSQL(array $columnDef)
301
    {
302
        $columnDef['integer_type'] = 'BIGINT';
303
304
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    public function getBinaryDefaultLength()
311
    {
312
        return 1;
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     */
318
    public function getBinaryMaxLength()
319
    {
320
        return 32767;
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326
    public function getBlobTypeDeclarationSQL(array $field)
327
    {
328
        return 'LONG BINARY';
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     *
334
     * BIT type columns require an explicit NULL declaration
335
     * in SQL Anywhere if they shall be nullable.
336
     * Otherwise by just omitting the NOT NULL clause,
337
     * SQL Anywhere will declare them NOT NULL nonetheless.
338
     */
339
    public function getBooleanTypeDeclarationSQL(array $columnDef)
340
    {
341
        $nullClause = isset($columnDef['notnull']) && (bool) $columnDef['notnull'] === false ? ' NULL' : '';
342
343
        return 'BIT' . $nullClause;
344
    }
345
346
    /**
347
     * {@inheritdoc}
348
     */
349
    public function getClobTypeDeclarationSQL(array $field)
350
    {
351
        return 'TEXT';
352
    }
353
354
    /**
355
     * {@inheritdoc}
356
     */
357
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
358
    {
359
        $tableName  = new Identifier($tableName);
360
        $columnName = new Identifier($columnName);
361
        $comment    = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
362
363
        return sprintf(
364
            'COMMENT ON COLUMN %s.%s IS %s',
365
            $tableName->getQuotedName($this),
366
            $columnName->getQuotedName($this),
367
            $comment
368
        );
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     */
374
    public function getConcatExpression()
375
    {
376
        return 'STRING(' . implode(', ', (array) func_get_args()) . ')';
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function getCreateConstraintSQL(Constraint $constraint, $table)
383
    {
384
        if ($constraint instanceof ForeignKeyConstraint) {
385
            return $this->getCreateForeignKeySQL($constraint, $table);
386
        }
387
388
        if ($table instanceof Table) {
389
            $table = $table->getQuotedName($this);
390
        }
391
392
        return 'ALTER TABLE ' . $table .
393
               ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this));
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
399
    public function getCreateDatabaseSQL($database)
400
    {
401
        $database = new Identifier($database);
402
403
        return "CREATE DATABASE '" . $database->getName() . "'";
404
    }
405
406
    /**
407
     * {@inheritdoc}
408
     *
409
     * Appends SQL Anywhere specific flags if given.
410
     */
411
    public function getCreateIndexSQL(Index $index, $table)
412
    {
413
        return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index);
414
    }
415
416
    /**
417
     * {@inheritdoc}
418
     */
419
    public function getCreatePrimaryKeySQL(Index $index, $table)
420
    {
421
        if ($table instanceof Table) {
422
            $table = $table->getQuotedName($this);
423
        }
424
425
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
426
    }
427
428
    /**
429
     * {@inheritdoc}
430
     */
431
    public function getCreateTemporaryTableSnippetSQL()
432
    {
433
        return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE';
434
    }
435
436
    /**
437
     * {@inheritdoc}
438
     */
439
    public function getCreateViewSQL($name, $sql)
440
    {
441
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
442
    }
443
444
    /**
445
     * {@inheritdoc}
446
     */
447
    public function getCurrentDateSQL()
448
    {
449
        return 'CURRENT DATE';
450
    }
451
452
    /**
453
     * {@inheritdoc}
454
     */
455
    public function getCurrentTimeSQL()
456
    {
457
        return 'CURRENT TIME';
458
    }
459
460
    /**
461
     * {@inheritdoc}
462
     */
463
    public function getCurrentTimestampSQL()
464
    {
465
        return 'CURRENT TIMESTAMP';
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
472
    {
473
        $factorClause = '';
474
475
        if ($operator === '-') {
476
            $factorClause = '-1 * ';
477
        }
478
479
        return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
480
    }
481
482
    /**
483
     * {@inheritdoc}
484
     */
485
    public function getDateDiffExpression($date1, $date2)
486
    {
487
        return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')';
488
    }
489
490
    /**
491
     * {@inheritdoc}
492
     */
493
    public function getDateTimeFormatString()
494
    {
495
        return 'Y-m-d H:i:s.u';
496
    }
497
498
    /**
499
     * {@inheritdoc}
500
     */
501
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
502
    {
503
        return 'DATETIME';
504
    }
505
506
    /**
507
     * {@inheritdoc}
508
     */
509
    public function getDateTimeTzFormatString()
510
    {
511
        return 'Y-m-d H:i:s.uP';
512
    }
513
514
    /**
515
     * {@inheritdoc}
516
     */
517
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
518
    {
519
        return 'DATE';
520
    }
521
522
    /**
523
     * {@inheritdoc}
524
     */
525
    public function getDefaultTransactionIsolationLevel()
526
    {
527
        return TransactionIsolationLevel::READ_UNCOMMITTED;
528
    }
529
530
    /**
531
     * {@inheritdoc}
532
     */
533
    public function getDropDatabaseSQL($database)
534
    {
535
        $database = new Identifier($database);
536
537
        return "DROP DATABASE '" . $database->getName() . "'";
538
    }
539
540
    /**
541
     * {@inheritdoc}
542
     */
543
    public function getDropIndexSQL($index, $table = null)
544
    {
545
        if ($index instanceof Index) {
546
            $index = $index->getQuotedName($this);
547
        }
548
549
        if (! is_string($index)) {
550
            throw new InvalidArgumentException(
551
                'AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or ' . Index::class . '.'
552
            );
553
        }
554
555
        if (! isset($table)) {
556
            return 'DROP INDEX ' . $index;
557
        }
558
559
        if ($table instanceof Table) {
560
            $table = $table->getQuotedName($this);
561
        }
562
563
        if (! is_string($table)) {
564
            throw new InvalidArgumentException(
565
                'AbstractPlatform::getDropIndexSQL() expects $table parameter to be string or ' . Index::class . '.'
566
            );
567
        }
568
569
        return 'DROP INDEX ' . $table . '.' . $index;
570
    }
571
572
    /**
573
     * {@inheritdoc}
574
     */
575
    public function getDropViewSQL($name)
576
    {
577
        return 'DROP VIEW ' . $name;
578
    }
579
580
    /**
581
     * {@inheritdoc}
582
     */
583
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
584
    {
585
        $sql              = '';
586
        $foreignKeyName   = $foreignKey->getName();
587
        $localColumns     = $foreignKey->getQuotedLocalColumns($this);
588
        $foreignColumns   = $foreignKey->getQuotedForeignColumns($this);
589
        $foreignTableName = $foreignKey->getQuotedForeignTableName($this);
590
591
        if (! empty($foreignKeyName)) {
592
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
593
        }
594
595
        if (empty($localColumns)) {
596
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
597
        }
598
599
        if (empty($foreignColumns)) {
600
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
601
        }
602
603
        if (empty($foreignTableName)) {
604
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
605
        }
606
607
        if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) {
608
            $sql .= 'NOT NULL ';
609
        }
610
611
        return $sql .
612
            'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' .
613
            'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) .
614
            ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')';
615
    }
616
617
    /**
618
     * Returns foreign key MATCH clause for given type.
619
     *
620
     * @param int $type The foreign key match type
621
     *
622
     * @return string
623
     *
624
     * @throws InvalidArgumentException If unknown match type given.
625
     */
626
    public function getForeignKeyMatchClauseSQL($type)
627
    {
628
        switch ((int) $type) {
629
            case self::FOREIGN_KEY_MATCH_SIMPLE:
630
                return 'SIMPLE';
631
632
                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...
633
            case self::FOREIGN_KEY_MATCH_FULL:
634
                return 'FULL';
635
636
                break;
637
            case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE:
638
                return 'UNIQUE SIMPLE';
639
640
                break;
641
            case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
642
                return 'UNIQUE FULL';
643
            default:
644
                throw new InvalidArgumentException('Invalid foreign key match type: ' . $type);
645
        }
646
    }
647
648
    /**
649
     * {@inheritdoc}
650
     */
651
    public function getForeignKeyReferentialActionSQL($action)
652
    {
653
        // NO ACTION is not supported, therefore falling back to RESTRICT.
654
        if (strtoupper($action) === 'NO ACTION') {
655
            return 'RESTRICT';
656
        }
657
658
        return parent::getForeignKeyReferentialActionSQL($action);
659
    }
660
661
    /**
662
     * {@inheritdoc}
663
     */
664
    public function getForUpdateSQL()
665
    {
666
        return '';
667
    }
668
669
    /**
670
     * {@inheritdoc}
671
     *
672
     * @deprecated Use application-generated UUIDs instead
673
     */
674
    public function getGuidExpression()
675
    {
676
        return 'NEWID()';
677
    }
678
679
    /**
680
     * {@inheritdoc}
681
     */
682
    public function getGuidTypeDeclarationSQL(array $field)
683
    {
684
        return 'UNIQUEIDENTIFIER';
685
    }
686
687
    /**
688
     * {@inheritdoc}
689
     */
690
    public function getIndexDeclarationSQL($name, Index $index)
691
    {
692
        // Index declaration in statements like CREATE TABLE is not supported.
693
        throw DBALException::notSupported(__METHOD__);
694
    }
695
696
    /**
697
     * {@inheritdoc}
698
     */
699
    public function getIntegerTypeDeclarationSQL(array $columnDef)
700
    {
701
        $columnDef['integer_type'] = 'INT';
702
703
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
704
    }
705
706
    /**
707
     * {@inheritdoc}
708
     */
709
    public function getListDatabasesSQL()
710
    {
711
        return 'SELECT db_name(number) AS name FROM sa_db_list()';
712
    }
713
714
    /**
715
     * {@inheritdoc}
716
     */
717
    public function getListTableColumnsSQL($table, $database = null)
718
    {
719
        $user = 'USER_NAME()';
720
721
        if (strpos($table, '.') !== false) {
722
            [$user, $table] = explode('.', $table);
723
            $user           = $this->quoteStringLiteral($user);
724
        }
725
726
        return sprintf(
727
            <<<'SQL'
728
SELECT    col.column_name,
729
          COALESCE(def.user_type_name, def.domain_name) AS 'type',
730
          def.declared_width AS 'length',
731
          def.scale,
732
          CHARINDEX('unsigned', def.domain_name) AS 'unsigned',
733
          IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull',
734
          col."default",
735
          def.is_autoincrement AS 'autoincrement',
736
          rem.remarks AS 'comment'
737
FROM      sa_describe_query('SELECT * FROM "%s"') AS def
738
JOIN      SYS.SYSTABCOL AS col
739
ON        col.table_id = def.base_table_id AND col.column_id = def.base_column_id
740
LEFT JOIN SYS.SYSREMARK AS rem
741
ON        col.object_id = rem.object_id
742
WHERE     def.base_owner_name = %s
743
ORDER BY  def.base_column_id ASC
744
SQL
745
            ,
746
            $table,
747
            $user
748
        );
749
    }
750
751
    /**
752
     * {@inheritdoc}
753
     *
754
     * @todo Where is this used? Which information should be retrieved?
755
     */
756
    public function getListTableConstraintsSQL($table)
757
    {
758
        $user = '';
759
760
        if (strpos($table, '.') !== false) {
761
            [$user, $table] = explode('.', $table);
762
            $user           = $this->quoteStringLiteral($user);
763
            $table          = $this->quoteStringLiteral($table);
764
        } else {
765
            $table = $this->quoteStringLiteral($table);
766
        }
767
768
        return sprintf(
769
            <<<'SQL'
770
SELECT con.*
771
FROM   SYS.SYSCONSTRAINT AS con
772
JOIN   SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id
773
WHERE  tab.table_name = %s
774
AND    tab.creator = USER_ID(%s)
775
SQL
776
            ,
777
            $table,
778
            $user
779
        );
780
    }
781
782
    /**
783
     * {@inheritdoc}
784
     */
785
    public function getListTableForeignKeysSQL($table)
786
    {
787
        $user = '';
788
789
        if (strpos($table, '.') !== false) {
790
            [$user, $table] = explode('.', $table);
791
            $user           = $this->quoteStringLiteral($user);
792
            $table          = $this->quoteStringLiteral($table);
793
        } else {
794
            $table = $this->quoteStringLiteral($table);
795
        }
796
797
        return sprintf(
798
            <<<'SQL'
799
SELECT    fcol.column_name AS local_column,
800
          ptbl.table_name AS foreign_table,
801
          pcol.column_name AS foreign_column,
802
          idx.index_name,
803
          IF fk.nulls = 'N'
804
              THEN 1
805
              ELSE NULL
806
          ENDIF AS notnull,
807
          CASE ut.referential_action
808
              WHEN 'C' THEN 'CASCADE'
809
              WHEN 'D' THEN 'SET DEFAULT'
810
              WHEN 'N' THEN 'SET NULL'
811
              WHEN 'R' THEN 'RESTRICT'
812
              ELSE NULL
813
          END AS  on_update,
814
          CASE dt.referential_action
815
              WHEN 'C' THEN 'CASCADE'
816
              WHEN 'D' THEN 'SET DEFAULT'
817
              WHEN 'N' THEN 'SET NULL'
818
              WHEN 'R' THEN 'RESTRICT'
819
              ELSE NULL
820
          END AS on_delete,
821
          IF fk.check_on_commit = 'Y'
822
              THEN 1
823
              ELSE NULL
824
          ENDIF AS check_on_commit, -- check_on_commit flag
825
          IF ftbl.clustered_index_id = idx.index_id
826
              THEN 1
827
              ELSE NULL
828
          ENDIF AS 'clustered', -- clustered flag
829
          IF fk.match_type = 0
830
              THEN NULL
831
              ELSE fk.match_type
832
          ENDIF AS 'match', -- match option
833
          IF pidx.max_key_distance = 1
834
              THEN 1
835
              ELSE NULL
836
          ENDIF AS for_olap_workload -- for_olap_workload flag
837
FROM      SYS.SYSFKEY AS fk
838
JOIN      SYS.SYSIDX AS idx
839
ON        fk.foreign_table_id = idx.table_id
840
AND       fk.foreign_index_id = idx.index_id
841
JOIN      SYS.SYSPHYSIDX pidx
842
ON        idx.table_id = pidx.table_id
843
AND       idx.phys_index_id = pidx.phys_index_id
844
JOIN      SYS.SYSTAB AS ptbl
845
ON        fk.primary_table_id = ptbl.table_id
846
JOIN      SYS.SYSTAB AS ftbl
847
ON        fk.foreign_table_id = ftbl.table_id
848
JOIN      SYS.SYSIDXCOL AS idxcol
849
ON        idx.table_id = idxcol.table_id
850
AND       idx.index_id = idxcol.index_id
851
JOIN      SYS.SYSTABCOL AS pcol
852
ON        ptbl.table_id = pcol.table_id
853
AND       idxcol.primary_column_id = pcol.column_id
854
JOIN      SYS.SYSTABCOL AS fcol
855
ON        ftbl.table_id = fcol.table_id
856
AND       idxcol.column_id = fcol.column_id
857
LEFT JOIN SYS.SYSTRIGGER ut
858
ON        fk.foreign_table_id = ut.foreign_table_id
859
AND       fk.foreign_index_id = ut.foreign_key_id
860
AND       ut.event = 'C'
861
LEFT JOIN SYS.SYSTRIGGER dt
862
ON        fk.foreign_table_id = dt.foreign_table_id
863
AND       fk.foreign_index_id = dt.foreign_key_id
864
AND       dt.event = 'D'
865
WHERE     ftbl.table_name = %s
866
AND       ftbl.creator = USER_ID(%s)
867
ORDER BY  fk.foreign_index_id ASC, idxcol.sequence ASC
868
SQL
869
            ,
870
            $table,
871
            $user
872
        );
873
    }
874
875
    /**
876
     * {@inheritdoc}
877
     */
878
    public function getListTableIndexesSQL($table, $currentDatabase = null)
879
    {
880
        $user = '';
881
882
        if (strpos($table, '.') !== false) {
883
            [$user, $table] = explode('.', $table);
884
            $user           = $this->quoteStringLiteral($user);
885
            $table          = $this->quoteStringLiteral($table);
886
        } else {
887
            $table = $this->quoteStringLiteral($table);
888
        }
889
890
        return sprintf(
891
            <<<'SQL'
892
SELECT   idx.index_name AS key_name,
893
         IF idx.index_category = 1
894
             THEN 1
895
             ELSE 0
896
         ENDIF AS 'primary',
897
         col.column_name,
898
         IF idx."unique" IN(1, 2, 5)
899
             THEN 0
900
             ELSE 1
901
         ENDIF AS non_unique,
902
         IF tbl.clustered_index_id = idx.index_id
903
             THEN 1
904
             ELSE NULL
905
         ENDIF AS 'clustered', -- clustered flag
906
         IF idx."unique" = 5
907
             THEN 1
908
             ELSE NULL
909
         ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag
910
         IF pidx.max_key_distance = 1
911
              THEN 1
912
              ELSE NULL
913
          ENDIF AS for_olap_workload -- for_olap_workload flag
914
FROM     SYS.SYSIDX AS idx
915
JOIN     SYS.SYSPHYSIDX pidx
916
ON       idx.table_id = pidx.table_id
917
AND      idx.phys_index_id = pidx.phys_index_id
918
JOIN     SYS.SYSIDXCOL AS idxcol
919
ON       idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id
920
JOIN     SYS.SYSTABCOL AS col
921
ON       idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id
922
JOIN     SYS.SYSTAB AS tbl
923
ON       idx.table_id = tbl.table_id
924
WHERE    tbl.table_name = %s
925
AND      tbl.creator = USER_ID(%s)
926
AND      idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints
927
ORDER BY idx.index_id ASC, idxcol.sequence ASC
928
SQL
929
            ,
930
            $table,
931
            $user
932
        );
933
    }
934
935
    /**
936
     * {@inheritdoc}
937
     */
938
    public function getListTablesSQL()
939
    {
940
        return "SELECT   tbl.table_name
941
                FROM     SYS.SYSTAB AS tbl
942
                JOIN     SYS.SYSUSER AS usr ON tbl.creator = usr.user_id
943
                JOIN     dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id
944
                WHERE    tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP'
945
                AND      usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
946
                AND      obj.type = 'U' -- user created tables only
947
                ORDER BY tbl.table_name ASC";
948
    }
949
950
    /**
951
     * {@inheritdoc}
952
     *
953
     * @todo Where is this used? Which information should be retrieved?
954
     */
955
    public function getListUsersSQL()
956
    {
957
        return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC';
958
    }
959
960
    /**
961
     * {@inheritdoc}
962
     */
963
    public function getListViewsSQL($database)
964
    {
965
        return "SELECT   tbl.table_name, v.view_def
966
                FROM     SYS.SYSVIEW v
967
                JOIN     SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id
968
                JOIN     SYS.SYSUSER usr ON tbl.creator = usr.user_id
969
                JOIN     dbo.SYSOBJECTS obj ON tbl.object_id = obj.id
970
                WHERE    usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
971
                ORDER BY tbl.table_name ASC";
972
    }
973
974
    /**
975
     * {@inheritdoc}
976
     */
977
    public function getLocateExpression($str, $substr, $startPos = false)
978
    {
979
        if ($startPos === false) {
980
            return 'LOCATE(' . $str . ', ' . $substr . ')';
981
        }
982
983
        return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
984
    }
985
986
    /**
987
     * {@inheritdoc}
988
     */
989
    public function getMaxIdentifierLength()
990
    {
991
        return 128;
992
    }
993
994
    /**
995
     * {@inheritdoc}
996
     */
997
    public function getMd5Expression($column)
998
    {
999
        return 'HASH(' . $column . ", 'MD5')";
1000
    }
1001
1002
    /**
1003
     * {@inheritdoc}
1004
     */
1005
    public function getRegexpExpression()
1006
    {
1007
        return 'REGEXP';
1008
    }
1009
1010
    /**
1011
     * {@inheritdoc}
1012
     */
1013
    public function getName()
1014
    {
1015
        return 'sqlanywhere';
1016
    }
1017
1018
    /**
1019
     * Obtain DBMS specific SQL code portion needed to set a primary key
1020
     * declaration to be used in statements like ALTER TABLE.
1021
     *
1022
     * @param Index  $index Index definition
1023
     * @param string $name  Name of the primary key
1024
     *
1025
     * @return string DBMS specific SQL code portion needed to set a primary key
1026
     *
1027
     * @throws InvalidArgumentException If the given index is not a primary key.
1028
     */
1029
    public function getPrimaryKeyDeclarationSQL(Index $index, $name = null)
1030
    {
1031
        if (! $index->isPrimary()) {
1032
            throw new InvalidArgumentException(
1033
                'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()'
1034
            );
1035
        }
1036
1037
        return $this->getTableConstraintDeclarationSQL($index, $name);
1038
    }
1039
1040
    /**
1041
     * {@inheritdoc}
1042
     */
1043
    public function getSetTransactionIsolationSQL($level)
1044
    {
1045
        return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level);
1046
    }
1047
1048
    /**
1049
     * {@inheritdoc}
1050
     */
1051
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
1052
    {
1053
        $columnDef['integer_type'] = 'SMALLINT';
1054
1055
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
1056
    }
1057
1058
    /**
1059
     * Returns the SQL statement for starting an existing database.
1060
     *
1061
     * In SQL Anywhere you can start and stop databases on a
1062
     * database server instance.
1063
     * This is a required statement after having created a new database
1064
     * as it has to be explicitly started to be usable.
1065
     * SQL Anywhere does not automatically start a database after creation!
1066
     *
1067
     * @param string $database Name of the database to start.
1068
     *
1069
     * @return string
1070
     */
1071
    public function getStartDatabaseSQL($database)
1072
    {
1073
        $database = new Identifier($database);
1074
1075
        return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF";
1076
    }
1077
1078
    /**
1079
     * Returns the SQL statement for stopping a running database.
1080
     *
1081
     * In SQL Anywhere you can start and stop databases on a
1082
     * database server instance.
1083
     * This is a required statement before dropping an existing database
1084
     * as it has to be explicitly stopped before it can be dropped.
1085
     *
1086
     * @param string $database Name of the database to stop.
1087
     *
1088
     * @return string
1089
     */
1090
    public function getStopDatabaseSQL($database)
1091
    {
1092
        $database = new Identifier($database);
1093
1094
        return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY';
1095
    }
1096
1097
    /**
1098
     * {@inheritdoc}
1099
     */
1100
    public function getSubstringExpression($value, $from, $length = null)
1101
    {
1102
        if ($length === null) {
1103
            return 'SUBSTRING(' . $value . ', ' . $from . ')';
1104
        }
1105
1106
        return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
1107
    }
1108
1109
    /**
1110
     * {@inheritdoc}
1111
     */
1112
    public function getTemporaryTableSQL()
1113
    {
1114
        return 'GLOBAL TEMPORARY';
1115
    }
1116
1117
    /**
1118
     * {@inheritdoc}
1119
     */
1120
    public function getTimeFormatString()
1121
    {
1122
        return 'H:i:s.u';
1123
    }
1124
1125
    /**
1126
     * {@inheritdoc}
1127
     */
1128
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
1129
    {
1130
        return 'TIME';
1131
    }
1132
1133
    /**
1134
     * {@inheritdoc}
1135
     */
1136
    public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
1137
    {
1138
        if (! $char) {
1139
            switch ($pos) {
1140
                case TrimMode::LEADING:
1141
                    return $this->getLtrimExpression($str);
1142
                case TrimMode::TRAILING:
1143
                    return $this->getRtrimExpression($str);
1144
                default:
1145
                    return 'TRIM(' . $str . ')';
1146
            }
1147
        }
1148
1149
        $pattern = "'%[^' + " . $char . " + ']%'";
1150
1151
        switch ($pos) {
1152
            case TrimMode::LEADING:
1153
                return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
1154
            case TrimMode::TRAILING:
1155
                return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
1156
            default:
1157
                return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
1158
                    'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
1159
        }
1160
    }
1161
1162
    /**
1163
     * {@inheritdoc}
1164
     */
1165
    public function getTruncateTableSQL($tableName, $cascade = false)
1166
    {
1167
        $tableIdentifier = new Identifier($tableName);
1168
1169
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1170
    }
1171
1172
    /**
1173
     * {@inheritdoc}
1174
     */
1175
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
1176
    {
1177
        if ($index->isPrimary()) {
1178
            throw new InvalidArgumentException(
1179
                'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().'
1180
            );
1181
        }
1182
1183
        if (! $index->isUnique()) {
1184
            throw new InvalidArgumentException(
1185
                'Can only create unique constraint declarations, no common index declarations with ' .
1186
                'getUniqueConstraintDeclarationSQL().'
1187
            );
1188
        }
1189
1190
        return $this->getTableConstraintDeclarationSQL($index, $name);
1191
    }
1192
1193
    /**
1194
     * {@inheritdoc}
1195
     */
1196
    public function getCreateSequenceSQL(Sequence $sequence)
1197
    {
1198
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
1199
            ' INCREMENT BY ' . $sequence->getAllocationSize() .
1200
            ' START WITH ' . $sequence->getInitialValue() .
1201
            ' MINVALUE ' . $sequence->getInitialValue();
1202
    }
1203
1204
    /**
1205
     * {@inheritdoc}
1206
     */
1207
    public function getAlterSequenceSQL(Sequence $sequence)
1208
    {
1209
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
1210
            ' INCREMENT BY ' . $sequence->getAllocationSize();
1211
    }
1212
1213
    /**
1214
     * {@inheritdoc}
1215
     */
1216
    public function getDropSequenceSQL($sequence)
1217
    {
1218
        if ($sequence instanceof Sequence) {
1219
            $sequence = $sequence->getQuotedName($this);
1220
        }
1221
1222
        return 'DROP SEQUENCE ' . $sequence;
1223
    }
1224
1225
    /**
1226
     * {@inheritdoc}
1227
     */
1228
    public function getListSequencesSQL($database)
1229
    {
1230
        return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE';
1231
    }
1232
1233
    /**
1234
     * {@inheritdoc}
1235
     */
1236
    public function getSequenceNextValSQL($sequenceName)
1237
    {
1238
        return 'SELECT ' . $sequenceName . '.NEXTVAL';
1239
    }
1240
1241
    /**
1242
     * {@inheritdoc}
1243
     */
1244
    public function supportsSequences()
1245
    {
1246
        return true;
1247
    }
1248
1249
    /**
1250
     * {@inheritdoc}
1251
     */
1252
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
1253
    {
1254
        return 'TIMESTAMP WITH TIME ZONE';
1255
    }
1256
1257
    /**
1258
     * {@inheritdoc}
1259
     */
1260
    public function getVarcharDefaultLength()
1261
    {
1262
        return 1;
1263
    }
1264
1265
    /**
1266
     * {@inheritdoc}
1267
     */
1268
    public function getVarcharMaxLength()
1269
    {
1270
        return 32767;
1271
    }
1272
1273
    /**
1274
     * {@inheritdoc}
1275
     */
1276
    public function hasNativeGuidType()
1277
    {
1278
        return true;
1279
    }
1280
1281
    /**
1282
     * {@inheritdoc}
1283
     */
1284
    public function prefersIdentityColumns()
1285
    {
1286
        return true;
1287
    }
1288
1289
    /**
1290
     * {@inheritdoc}
1291
     */
1292
    public function supportsCommentOnStatement()
1293
    {
1294
        return true;
1295
    }
1296
1297
    /**
1298
     * {@inheritdoc}
1299
     */
1300
    public function supportsIdentityColumns()
1301
    {
1302
        return true;
1303
    }
1304
1305
    /**
1306
     * {@inheritdoc}
1307
     */
1308
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
1309
    {
1310
        $unsigned      = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : '';
1311
        $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : '';
1312
1313
        return $unsigned . $columnDef['integer_type'] . $autoincrement;
1314
    }
1315
1316
    /**
1317
     * {@inheritdoc}
1318
     */
1319
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1320
    {
1321
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1322
        $indexSql      = [];
1323
1324
        if (! empty($options['uniqueConstraints'])) {
1325
            foreach ((array) $options['uniqueConstraints'] as $name => $definition) {
1326
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1327
            }
1328
        }
1329
1330
        if (! empty($options['indexes'])) {
1331
            /** @var Index $index */
1332
            foreach ((array) $options['indexes'] as $index) {
1333
                $indexSql[] = $this->getCreateIndexSQL($index, $tableName);
1334
            }
1335
        }
1336
1337
        if (! empty($options['primary'])) {
1338
            $flags = '';
1339
1340
            if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) {
1341
                $flags = ' CLUSTERED ';
1342
            }
1343
1344
            $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')';
1345
        }
1346
1347
        if (! empty($options['foreignKeys'])) {
1348
            foreach ((array) $options['foreignKeys'] as $definition) {
1349
                $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition);
1350
            }
1351
        }
1352
1353
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1354
        $check = $this->getCheckDeclarationSQL($columns);
1355
1356
        if (! empty($check)) {
1357
            $query .= ', ' . $check;
1358
        }
1359
1360
        $query .= ')';
1361
1362
        return array_merge([$query], $indexSql);
1363
    }
1364
1365
    /**
1366
     * {@inheritdoc}
1367
     */
1368
    protected function _getTransactionIsolationLevelSQL($level)
1369
    {
1370
        switch ($level) {
1371
            case TransactionIsolationLevel::READ_UNCOMMITTED:
1372
                return 0;
1373
            case TransactionIsolationLevel::READ_COMMITTED:
1374
                return 1;
1375
            case TransactionIsolationLevel::REPEATABLE_READ:
1376
                return 2;
1377
            case TransactionIsolationLevel::SERIALIZABLE:
1378
                return 3;
1379
            default:
1380
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
1381
        }
1382
    }
1383
1384
    /**
1385
     * {@inheritdoc}
1386
     */
1387
    protected function doModifyLimitQuery($query, $limit, $offset)
1388
    {
1389
        $limitOffsetClause = $this->getTopClauseSQL($limit, $offset);
1390
1391
        if ($limitOffsetClause === '') {
1392
            return $query;
1393
        }
1394
1395
        if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) {
1396
            return $query;
1397
        }
1398
1399
        return $matches[1] . $limitOffsetClause . ' ' . $matches[3];
1400
    }
1401
1402
    private function getTopClauseSQL(?int $limit, ?int $offset) : string
1403
    {
1404
        if ($offset > 0) {
1405
            return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1);
1406
        }
1407
1408
        return $limit === null ? '' : 'TOP ' . $limit;
1409
    }
1410
1411
    /**
1412
     * Return the INDEX query section dealing with non-standard
1413
     * SQL Anywhere options.
1414
     *
1415
     * @param Index $index Index definition
1416
     *
1417
     * @return string
1418
     */
1419
    protected function getAdvancedIndexOptionsSQL(Index $index)
1420
    {
1421
        if ($index->hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) {
1422
            throw new UnexpectedValueException(
1423
                'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.'
1424
            );
1425
        }
1426
1427
        $sql = '';
1428
1429
        if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) {
1430
            $sql .= ' FOR OLAP WORKLOAD';
1431
        }
1432
1433
        if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) {
1434
            return ' WITH NULLS NOT DISTINCT' . $sql;
1435
        }
1436
1437
        if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) {
1438
            return ' WITH NULLS DISTINCT' . $sql;
1439
        }
1440
1441
        return $sql;
1442
    }
1443
1444
    /**
1445
     * {@inheritdoc}
1446
     */
1447
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
1448
    {
1449
        return $fixed
1450
            ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'
1451
            : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')';
1452
    }
1453
1454
    /**
1455
     * Returns the SQL snippet for creating a table constraint.
1456
     *
1457
     * @param Constraint  $constraint The table constraint to create the SQL snippet for.
1458
     * @param string|null $name       The table constraint name to use if any.
1459
     *
1460
     * @return string
1461
     *
1462
     * @throws InvalidArgumentException If the given table constraint type is not supported by this method.
1463
     */
1464
    protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null)
1465
    {
1466
        if ($constraint instanceof ForeignKeyConstraint) {
1467
            return $this->getForeignKeyDeclarationSQL($constraint);
1468
        }
1469
1470
        if (! $constraint instanceof Index) {
1471
            throw new InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint));
1472
        }
1473
1474
        if (! $constraint->isPrimary() && ! $constraint->isUnique()) {
1475
            throw new InvalidArgumentException(
1476
                'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' .
1477
                'with getTableConstraintDeclarationSQL().'
1478
            );
1479
        }
1480
1481
        $constraintColumns = $constraint->getQuotedColumns($this);
1482
1483
        if (empty($constraintColumns)) {
1484
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1485
        }
1486
1487
        $sql   = '';
1488
        $flags = '';
1489
1490
        if (! empty($name)) {
1491
            $name = new Identifier($name);
1492
            $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' ';
1493
        }
1494
1495
        if ($constraint->hasFlag('clustered')) {
1496
            $flags = 'CLUSTERED ';
1497
        }
1498
1499
        if ($constraint->isPrimary()) {
1500
            return $sql . 'PRIMARY KEY ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1501
        }
1502
1503
        return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1504
    }
1505
1506
    /**
1507
     * {@inheritdoc}
1508
     */
1509
    protected function getCreateIndexSQLFlags(Index $index)
1510
    {
1511
        $type = '';
1512
        if ($index->hasFlag('virtual')) {
1513
            $type .= 'VIRTUAL ';
1514
        }
1515
1516
        if ($index->isUnique()) {
1517
            $type .= 'UNIQUE ';
1518
        }
1519
1520
        if ($index->hasFlag('clustered')) {
1521
            $type .= 'CLUSTERED ';
1522
        }
1523
1524
        return $type;
1525
    }
1526
1527
    /**
1528
     * {@inheritdoc}
1529
     */
1530
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
1531
    {
1532
        return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)];
1533
    }
1534
1535
    /**
1536
     * {@inheritdoc}
1537
     */
1538
    protected function getReservedKeywordsClass()
1539
    {
1540
        return Keywords\SQLAnywhereKeywords::class;
1541
    }
1542
1543
    /**
1544
     * {@inheritdoc}
1545
     */
1546
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
1547
    {
1548
        return $fixed
1549
            ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')')
1550
            : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')');
1551
    }
1552
1553
    /**
1554
     * {@inheritdoc}
1555
     */
1556
    protected function initializeDoctrineTypeMappings()
1557
    {
1558
        $this->doctrineTypeMapping = [
1559
            'bigint'                   => 'bigint',
1560
            'binary'                   => 'binary',
1561
            'bit'                      => 'boolean',
1562
            'char'                     => 'string',
1563
            'decimal'                  => 'decimal',
1564
            'date'                     => 'date',
1565
            'datetime'                 => 'datetime',
1566
            'double'                   => 'float',
1567
            'float'                    => 'float',
1568
            'image'                    => 'blob',
1569
            'int'                      => 'integer',
1570
            'integer'                  => 'integer',
1571
            'long binary'              => 'blob',
1572
            'long nvarchar'            => 'text',
1573
            'long varbit'              => 'text',
1574
            'long varchar'             => 'text',
1575
            'money'                    => 'decimal',
1576
            'nchar'                    => 'string',
1577
            'ntext'                    => 'text',
1578
            'numeric'                  => 'decimal',
1579
            'nvarchar'                 => 'string',
1580
            'smalldatetime'            => 'datetime',
1581
            'smallint'                 => 'smallint',
1582
            'smallmoney'               => 'decimal',
1583
            'text'                     => 'text',
1584
            'time'                     => 'time',
1585
            'timestamp'                => 'datetime',
1586
            'timestamp with time zone' => 'datetime',
1587
            'tinyint'                  => 'smallint',
1588
            'uniqueidentifier'         => 'guid',
1589
            'uniqueidentifierstr'      => 'guid',
1590
            'unsigned bigint'          => 'bigint',
1591
            'unsigned int'             => 'integer',
1592
            'unsigned smallint'        => 'smallint',
1593
            'unsigned tinyint'         => 'smallint',
1594
            'varbinary'                => 'binary',
1595
            'varbit'                   => 'string',
1596
            'varchar'                  => 'string',
1597
            'xml'                      => 'text',
1598
        ];
1599
    }
1600
}
1601