Completed
Pull Request — 2.10.x (#4009)
by Grégoire
08:50
created

SQLAnywherePlatform::getTrimExpression()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

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

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1479
            'char' => 'string',
1480
            'long nvarchar' => 'text',
1481
            'long varchar' => 'text',
1482
            'nchar' => 'string',
1483
            'ntext' => 'text',
1484
            'nvarchar' => 'string',
1485
            'text' => 'text',
1486
            'uniqueidentifierstr' => 'guid',
1487
            'varchar' => 'string',
1488
            'xml' => 'text',
1489
            'bigint' => 'bigint',
1490
            'unsigned bigint' => 'bigint',
1491
            'bit' => 'boolean',
1492
            'decimal' => 'decimal',
1493
            'double' => 'float',
1494
            'float' => 'float',
1495
            'int' => 'integer',
1496
            'integer' => 'integer',
1497
            'unsigned int' => 'integer',
1498
            'numeric' => 'decimal',
1499
            'smallint' => 'smallint',
1500
            'unsigned smallint' => 'smallint',
1501
            'tinyint' => 'smallint',
1502
            'unsigned tinyint' => 'smallint',
1503
            'money' => 'decimal',
1504
            'smallmoney' => 'decimal',
1505
            'long varbit' => 'text',
1506
            'varbit' => 'string',
1507
            'date' => 'date',
1508
            'datetime' => 'datetime',
1509
            'smalldatetime' => 'datetime',
1510
            'time' => 'time',
1511
            'timestamp' => 'datetime',
1512
            'binary' => 'binary',
1513
            'image' => 'blob',
1514
            'long binary' => 'blob',
1515
            'uniqueidentifier' => 'guid',
1516
            'varbinary' => 'binary',
1517
        ];
1518 8854
    }
1519
}
1520