Passed
Push — master ( 5a192c...be45cd )
by Sergei
28:04
created

getDefaultTransactionIsolationLevel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
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\Table;
14
use Doctrine\DBAL\Schema\TableDiff;
15
use Doctrine\DBAL\TransactionIsolationLevel;
16
use InvalidArgumentException;
17
use function array_merge;
18
use function array_unique;
19
use function array_values;
20
use function count;
21
use function explode;
22
use function func_get_args;
23
use function get_class;
24
use function implode;
25
use function is_string;
26
use function preg_replace;
27
use function sprintf;
28
use function strlen;
29
use function strpos;
30
use function strtoupper;
31
use function substr;
32
33
/**
34
 * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the
35
 * SAP Sybase SQL Anywhere 10 database platform.
36
 */
37
class SQLAnywherePlatform extends AbstractPlatform
38
{
39
    public const FOREIGN_KEY_MATCH_SIMPLE        = 1;
40
    public const FOREIGN_KEY_MATCH_FULL          = 2;
41
    public const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
42
    public const FOREIGN_KEY_MATCH_FULL_UNIQUE   = 130;
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 756
    public function appendLockHint($fromClause, $lockMode)
48
    {
49 756
        switch (true) {
50
            case $lockMode === LockMode::NONE:
51 108
                return $fromClause . ' WITH (NOLOCK)';
52
53 648
            case $lockMode === LockMode::PESSIMISTIC_READ:
54 108
                return $fromClause . ' WITH (UPDLOCK)';
55
56 540
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
57 108
                return $fromClause . ' WITH (XLOCK)';
58
59
            default:
60 432
                return $fromClause;
61
        }
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     *
67
     * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
68
     */
69 108
    public function fixSchemaElementName($schemaElementName)
70
    {
71 108
        $maxIdentifierLength = $this->getMaxIdentifierLength();
72
73 108
        if (strlen($schemaElementName) > $maxIdentifierLength) {
74 108
            return substr($schemaElementName, 0, $maxIdentifierLength);
75
        }
76
77 108
        return $schemaElementName;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 756
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
84
    {
85 756
        $query = '';
86
87 756
        if ($foreignKey->hasOption('match')) {
88 108
            $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
89
        }
90
91 756
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
92
93 756
        if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
94 108
            $query .= ' CHECK ON COMMIT';
95
        }
96
97 756
        if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
98 108
            $query .= ' CLUSTERED';
99
        }
100
101 756
        if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
102 108
            $query .= ' FOR OLAP WORKLOAD';
103
        }
104
105 756
        return $query;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 1620
    public function getAlterTableSQL(TableDiff $diff)
112
    {
113 1620
        $sql          = [];
114 1620
        $columnSql    = [];
115 1620
        $commentsSQL  = [];
116 1620
        $tableSql     = [];
117 1620
        $alterClauses = [];
118
119 1620
        foreach ($diff->addedColumns as $column) {
120 432
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
121
                continue;
122
            }
123
124 432
            $alterClauses[] = $this->getAlterTableAddColumnClause($column);
125
126 432
            $comment = $this->getColumnComment($column);
127
128 432
            if ($comment === null || $comment === '') {
129 324
                continue;
130
            }
131
132 108
            $commentsSQL[] = $this->getCommentOnColumnSQL(
133 108
                $diff->getName($this)->getQuotedName($this),
134 108
                $column->getQuotedName($this),
135 108
                $comment
136
            );
137
        }
138
139 1620
        foreach ($diff->removedColumns as $column) {
140 324
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
141
                continue;
142
            }
143
144 324
            $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
145
        }
146
147 1620
        foreach ($diff->changedColumns as $columnDiff) {
148 864
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
149
                continue;
150
            }
151
152 864
            $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
153
154 864
            if ($alterClause !== null) {
155 540
                $alterClauses[] = $alterClause;
156
            }
157
158 864
            if (! $columnDiff->hasChanged('comment')) {
159 540
                continue;
160
            }
161
162 324
            $column = $columnDiff->column;
163
164 324
            $commentsSQL[] = $this->getCommentOnColumnSQL(
165 324
                $diff->getName($this)->getQuotedName($this),
166 324
                $column->getQuotedName($this),
167 324
                $this->getColumnComment($column)
168
            );
169
        }
170
171 1620
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
172 432
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
173
                continue;
174
            }
175
176 432
            $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
177 432
                $this->getAlterTableRenameColumnClause($oldColumnName, $column);
178
        }
179
180 1620
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
181 1620
            if (! empty($alterClauses)) {
182 648
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
183
            }
184
185 1620
            $sql = array_merge($sql, $commentsSQL);
186
187 1620
            if ($diff->newName !== false) {
188 216
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
189 216
                    $this->getAlterTableRenameTableClause($diff->getNewName());
0 ignored issues
show
Bug introduced by
It seems like $diff->getNewName() can also be of type string; however, parameter $newTableName of Doctrine\DBAL\Platforms\...ableRenameTableClause() does only seem to accept Doctrine\DBAL\Schema\Identifier, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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