Completed
Pull Request — develop (#3211)
by Sergei
62:26
created

SQLAnywherePlatform::getDropIndexSQL()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 29
rs 9.2222
c 0
b 0
f 0
cc 6
nc 12
nop 2
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 UnexpectedValueException;
18
use function array_merge;
19
use function array_unique;
20
use function array_values;
21
use function count;
22
use function explode;
23
use function func_get_args;
24
use function get_class;
25
use function implode;
26
use function is_string;
27
use function preg_replace;
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 12 database platform.
36
 *
37
 * @author Steve Müller <[email protected]>
38
 * @link   www.doctrine-project.org
39
 * @since  2.5
40
 */
41
class SQLAnywherePlatform extends AbstractPlatform
42
{
43
    /**
44
     * @var int
45
     */
46
    const FOREIGN_KEY_MATCH_SIMPLE = 1;
47
    /**
48
     * @var int
49
     */
50
    const FOREIGN_KEY_MATCH_FULL = 2;
51
    /**
52
     * @var int
53
     */
54
    const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
55
    /**
56
     * @var int
57
     */
58
    const FOREIGN_KEY_MATCH_FULL_UNIQUE = 130;
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function appendLockHint($fromClause, $lockMode)
64
    {
65
        switch (true) {
66
            case $lockMode === LockMode::NONE:
67
                return $fromClause . ' WITH (NOLOCK)';
68
69
            case $lockMode === LockMode::PESSIMISTIC_READ:
70
                return $fromClause . ' WITH (UPDLOCK)';
71
72
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
73
                return $fromClause . ' WITH (XLOCK)';
74
75
            default:
76
                return $fromClause;
77
        }
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     *
83
     * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
84
     */
85
    public function fixSchemaElementName($schemaElementName)
86
    {
87
        $maxIdentifierLength = $this->getMaxIdentifierLength();
88
89
        if (strlen($schemaElementName) > $maxIdentifierLength) {
90
            return substr($schemaElementName, 0, $maxIdentifierLength);
91
        }
92
93
        return $schemaElementName;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
100
    {
101
        $query = '';
102
103
        if ($foreignKey->hasOption('match')) {
104
            $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
105
        }
106
107
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
108
109
        if ($foreignKey->hasOption('check_on_commit') && (boolean) $foreignKey->getOption('check_on_commit')) {
110
            $query .= ' CHECK ON COMMIT';
111
        }
112
113
        if ($foreignKey->hasOption('clustered') && (boolean) $foreignKey->getOption('clustered')) {
114
            $query .= ' CLUSTERED';
115
        }
116
117
        if ($foreignKey->hasOption('for_olap_workload') && (boolean) $foreignKey->getOption('for_olap_workload')) {
118
            $query .= ' FOR OLAP WORKLOAD';
119
        }
120
121
        return $query;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function getAlterTableSQL(TableDiff $diff)
128
    {
129
        $sql          = [];
130
        $columnSql    = [];
131
        $commentsSQL  = [];
132
        $tableSql     = [];
133
        $alterClauses = [];
134
135
        /** @var \Doctrine\DBAL\Schema\Column $column */
136
        foreach ($diff->addedColumns as $column) {
137
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
138
                continue;
139
            }
140
141
            $alterClauses[] = $this->getAlterTableAddColumnClause($column);
142
143
            $comment = $this->getColumnComment($column);
144
145
            if (null !== $comment && '' !== $comment) {
146
                $commentsSQL[] = $this->getCommentOnColumnSQL(
147
                    $diff->getName($this)->getQuotedName($this),
148
                    $column->getQuotedName($this),
149
                    $comment
150
                );
151
            }
152
        }
153
154
        /** @var \Doctrine\DBAL\Schema\Column $column */
155
        foreach ($diff->removedColumns as $column) {
156
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
157
                continue;
158
            }
159
160
            $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
161
        }
162
163
        /** @var \Doctrine\DBAL\Schema\ColumnDiff $columnDiff */
164
        foreach ($diff->changedColumns as $columnDiff) {
165
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
166
                continue;
167
            }
168
169
            $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
170
171
            if (null !== $alterClause) {
172
                $alterClauses[] = $alterClause;
173
            }
174
175
            if ($columnDiff->hasChanged('comment')) {
176
                $column = $columnDiff->column;
177
178
                $commentsSQL[] = $this->getCommentOnColumnSQL(
179
                    $diff->getName($this)->getQuotedName($this),
180
                    $column->getQuotedName($this),
181
                    $this->getColumnComment($column)
182
                );
183
            }
184
        }
185
186
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
187
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
188
                continue;
189
            }
190
191
            $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
192
                $this->getAlterTableRenameColumnClause($oldColumnName, $column);
193
        }
194
195
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
196
            if ( ! empty($alterClauses)) {
197
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(", ", $alterClauses);
198
            }
199
200
            $sql = array_merge($sql, $commentsSQL);
201
202
            if ($diff->newName !== false) {
203
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
204
                    $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

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