Completed
Pull Request — 2.11.x (#3956)
by David
14:33
created

SQLAnywherePlatform::getTrimExpression()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 23
ccs 12
cts 12
cp 1
rs 9.0777
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 count;
21
use function explode;
22
use function func_get_args;
23
use function get_class;
24
use function implode;
25
use function is_string;
26
use function preg_match;
27
use function sprintf;
28
use function strlen;
29
use function strpos;
30
use function strtoupper;
31
use function substr;
32
33
/**
34
 * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the
35
 * SAP Sybase SQL Anywhere 10 database platform.
36
 *
37
 * @deprecated Use SQLAnywhere 16 or newer
38
 */
39
class SQLAnywherePlatform extends AbstractPlatform
40
{
41
    public const FOREIGN_KEY_MATCH_SIMPLE        = 1;
42
    public const FOREIGN_KEY_MATCH_FULL          = 2;
43
    public const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
44
    public const FOREIGN_KEY_MATCH_FULL_UNIQUE   = 130;
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 10703
    public function appendLockHint($fromClause, $lockMode)
50
    {
51 10703
        switch (true) {
52
            case $lockMode === LockMode::NONE:
53 10654
                return $fromClause . ' WITH (NOLOCK)';
54
55 10699
            case $lockMode === LockMode::PESSIMISTIC_READ:
56 10604
                return $fromClause . ' WITH (UPDLOCK)';
57
58 10695
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
59 10579
                return $fromClause . ' WITH (XLOCK)';
60
61
            default:
62 10691
                return $fromClause;
63
        }
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     *
69
     * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
70
     */
71 10529
    public function fixSchemaElementName($schemaElementName)
72
    {
73 10529
        $maxIdentifierLength = $this->getMaxIdentifierLength();
74
75 10529
        if (strlen($schemaElementName) > $maxIdentifierLength) {
76 10529
            return substr($schemaElementName, 0, $maxIdentifierLength);
77
        }
78
79 10529
        return $schemaElementName;
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 10778
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
86
    {
87 10778
        $query = '';
88
89 10778
        if ($foreignKey->hasOption('match')) {
90 10304
            $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
91
        }
92
93 10778
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
94
95 10778
        if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
96 10304
            $query .= ' CHECK ON COMMIT';
97
        }
98
99 10778
        if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
100 10304
            $query .= ' CLUSTERED';
101
        }
102
103 10778
        if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
104 10304
            $query .= ' FOR OLAP WORKLOAD';
105
        }
106
107 10778
        return $query;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 10760
    public function getAlterTableSQL(TableDiff $diff)
114
    {
115 10760
        $sql          = [];
116 10760
        $columnSql    = [];
117 10760
        $commentsSQL  = [];
118 10760
        $tableSql     = [];
119 10760
        $alterClauses = [];
120
121 10760
        foreach ($diff->addedColumns as $column) {
122 8891
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
123
                continue;
124
            }
125
126 8891
            $alterClauses[] = $this->getAlterTableAddColumnClause($column);
127
128 8891
            $comment = $this->getColumnComment($column);
129
130 8891
            if ($comment === null || $comment === '') {
131 8887
                continue;
132
            }
133
134 8779
            $commentsSQL[] = $this->getCommentOnColumnSQL(
135 8779
                $diff->getName($this)->getQuotedName($this),
136 8779
                $column->getQuotedName($this),
137
                $comment
138
            );
139
        }
140
141 10760
        foreach ($diff->removedColumns as $column) {
142 8887
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
143
                continue;
144
            }
145
146 8887
            $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
147
        }
148
149 10760
        foreach ($diff->changedColumns as $columnDiff) {
150 10732
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
151
                continue;
152
            }
153
154 10732
            $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
155
156 10732
            if ($alterClause !== null) {
157 8895
                $alterClauses[] = $alterClause;
158
            }
159
160 10732
            if (! $columnDiff->hasChanged('comment')) {
161 8895
                continue;
162
            }
163
164 10712
            $column = $columnDiff->column;
165
166 10712
            $commentsSQL[] = $this->getCommentOnColumnSQL(
167 10712
                $diff->getName($this)->getQuotedName($this),
168 10712
                $column->getQuotedName($this),
169 10712
                $this->getColumnComment($column)
170
            );
171
        }
172
173 10760
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
174 8841
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
175
                continue;
176
            }
177
178 8841
            $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
179 8841
                $this->getAlterTableRenameColumnClause($oldColumnName, $column);
180
        }
181
182 10760
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
183 10760
            if (! empty($alterClauses)) {
184 8899
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
185
            }
186
187 10760
            $sql = array_merge($sql, $commentsSQL);
188
189 10760
            $newName = $diff->getNewName();
190
191 10760
            if ($newName !== false) {
192 8883
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
193 8883
                    $this->getAlterTableRenameTableClause($newName);
194
            }
195
196 10760
            $sql = array_merge(
197 10760
                $this->getPreAlterTableIndexForeignKeySQL($diff),
198 10760
                $sql,
199 10760
                $this->getPostAlterTableIndexForeignKeySQL($diff)
200
            );
201
        }
202
203 10760
        return array_merge($sql, $tableSql, $columnSql);
204
    }
205
206
    /**
207
     * Returns the SQL clause for creating a column in a table alteration.
208
     *
209
     * @param Column $column The column to add.
210
     *
211
     * @return string
212
     */
213 8891
    protected function getAlterTableAddColumnClause(Column $column)
214
    {
215 8891
        return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
216
    }
217
218
    /**
219
     * Returns the SQL clause for altering a table.
220
     *
221
     * @param Identifier $tableName The quoted name of the table to alter.
222
     *
223
     * @return string
224
     */
225 8907
    protected function getAlterTableClause(Identifier $tableName)
226
    {
227 8907
        return 'ALTER TABLE ' . $tableName->getQuotedName($this);
228
    }
229
230
    /**
231
     * Returns the SQL clause for dropping a column in a table alteration.
232
     *
233
     * @param Column $column The column to drop.
234
     *
235
     * @return string
236
     */
237 8887
    protected function getAlterTableRemoveColumnClause(Column $column)
238
    {
239 8887
        return 'DROP ' . $column->getQuotedName($this);
240
    }
241
242
    /**
243
     * Returns the SQL clause for renaming a column in a table alteration.
244
     *
245
     * @param string $oldColumnName The quoted name of the column to rename.
246
     * @param Column $column        The column to rename to.
247
     *
248
     * @return string
249
     */
250 8841
    protected function getAlterTableRenameColumnClause($oldColumnName, Column $column)
251
    {
252 8841
        $oldColumnName = new Identifier($oldColumnName);
253
254 8841
        return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
255
    }
256
257
    /**
258
     * Returns the SQL clause for renaming a table in a table alteration.
259
     *
260
     * @param Identifier $newTableName The quoted name of the table to rename to.
261
     *
262
     * @return string
263
     */
264 8883
    protected function getAlterTableRenameTableClause(Identifier $newTableName)
265
    {
266 8883
        return 'RENAME ' . $newTableName->getQuotedName($this);
267
    }
268
269
    /**
270
     * Returns the SQL clause for altering a column in a table alteration.
271
     *
272
     * This method returns null in case that only the column comment has changed.
273
     * Changes in column comments have to be handled differently.
274
     *
275
     * @param ColumnDiff $columnDiff The diff of the column to alter.
276
     *
277
     * @return string|null
278
     */
279 10732
    protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff)
280
    {
281 10732
        $column = $columnDiff->column;
282
283
        // Do not return alter clause if only comment has changed.
284 10732
        if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) {
285
            $columnAlterationClause = 'ALTER ' .
286 8895
                $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
287
288 8895
            if ($columnDiff->hasChanged('default') && $column->getDefault() === null) {
289
                $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT';
290
            }
291
292 8895
            return $columnAlterationClause;
293
        }
294
295 10712
        return null;
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301 10504
    public function getBigIntTypeDeclarationSQL(array $columnDef)
302
    {
303 10504
        $columnDef['integer_type'] = 'BIGINT';
304
305 10504
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311 9683
    public function getBinaryDefaultLength()
312
    {
313 9683
        return 1;
314
    }
315
316
    /**
317
     * {@inheritdoc}
318
     */
319 9687
    public function getBinaryMaxLength()
320
    {
321 9687
        return 32767;
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327 10508
    public function getBlobTypeDeclarationSQL(array $field)
328
    {
329 10508
        return 'LONG BINARY';
330
    }
331
332
    /**
333
     * {@inheritdoc}
334
     *
335
     * BIT type columns require an explicit NULL declaration
336
     * in SQL Anywhere if they shall be nullable.
337
     * Otherwise by just omitting the NOT NULL clause,
338
     * SQL Anywhere will declare them NOT NULL nonetheless.
339
     */
340 10508
    public function getBooleanTypeDeclarationSQL(array $columnDef)
341
    {
342 10508
        $nullClause = isset($columnDef['notnull']) && (bool) $columnDef['notnull'] === false ? ' NULL' : '';
343
344 10508
        return 'BIT' . $nullClause;
345
    }
346
347
    /**
348
     * {@inheritdoc}
349
     */
350 10512
    public function getClobTypeDeclarationSQL(array $field)
351
    {
352 10512
        return 'TEXT';
353
    }
354
355
    /**
356
     * {@inheritdoc}
357
     */
358 10732
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
359
    {
360 10732
        $tableName  = new Identifier($tableName);
361 10732
        $columnName = new Identifier($columnName);
362 10732
        $comment    = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
363
364 10732
        return sprintf(
365 32
            'COMMENT ON COLUMN %s.%s IS %s',
366 10732
            $tableName->getQuotedName($this),
367 10732
            $columnName->getQuotedName($this),
368 10732
            $comment
369
        );
370
    }
371
372
    /**
373
     * {@inheritdoc}
374
     */
375 9979
    public function getConcatExpression()
376
    {
377 9979
        return 'STRING(' . implode(', ', (array) func_get_args()) . ')';
378
    }
379
380
    /**
381
     * {@inheritdoc}
382
     */
383 10162
    public function getCreateConstraintSQL(Constraint $constraint, $table)
384
    {
385 10162
        if ($constraint instanceof ForeignKeyConstraint) {
386 8929
            return $this->getCreateForeignKeySQL($constraint, $table);
387
        }
388
389 10162
        if ($table instanceof Table) {
390 10154
            $table = $table->getQuotedName($this);
391
        }
392
393 10162
        return 'ALTER TABLE ' . $table .
394 10162
               ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this));
395
    }
396
397
    /**
398
     * {@inheritdoc}
399
     */
400 10454
    public function getCreateDatabaseSQL($database)
401
    {
402 10454
        $database = new Identifier($database);
403
404 10454
        return "CREATE DATABASE '" . $database->getName() . "'";
405
    }
406
407
    /**
408
     * {@inheritdoc}
409
     *
410
     * Appends SQL Anywhere specific flags if given.
411
     */
412 10808
    public function getCreateIndexSQL(Index $index, $table)
413
    {
414 10808
        return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index);
415
    }
416
417
    /**
418
     * {@inheritdoc}
419
     */
420 10381
    public function getCreatePrimaryKeySQL(Index $index, $table)
421
    {
422 10381
        if ($table instanceof Table) {
423 10379
            $table = $table->getQuotedName($this);
424
        }
425
426 10381
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
427
    }
428
429
    /**
430
     * {@inheritdoc}
431
     */
432 10454
    public function getCreateTemporaryTableSnippetSQL()
433
    {
434 10454
        return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE';
435
    }
436
437
    /**
438
     * {@inheritdoc}
439
     */
440 10454
    public function getCreateViewSQL($name, $sql)
441
    {
442 10454
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
443
    }
444
445
    /**
446
     * {@inheritdoc}
447
     */
448 9983
    public function getCurrentDateSQL()
449
    {
450 9983
        return 'CURRENT DATE';
451
    }
452
453
    /**
454
     * {@inheritdoc}
455
     */
456 9979
    public function getCurrentTimeSQL()
457
    {
458 9979
        return 'CURRENT TIME';
459
    }
460
461
    /**
462
     * {@inheritdoc}
463
     */
464 9983
    public function getCurrentTimestampSQL()
465
    {
466 9983
        return 'CURRENT TIMESTAMP';
467
    }
468
469
    /**
470
     * {@inheritdoc}
471
     */
472 9979
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
473
    {
474 9979
        $factorClause = '';
475
476 9979
        if ($operator === '-') {
477 9979
            $factorClause = '-1 * ';
478
        }
479
480 9979
        return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
481
    }
482
483
    /**
484
     * {@inheritdoc}
485
     */
486 9979
    public function getDateDiffExpression($date1, $date2)
487
    {
488 9979
        return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')';
489
    }
490
491
    /**
492
     * {@inheritdoc}
493
     */
494 9981
    public function getDateTimeFormatString()
495
    {
496 9981
        return 'Y-m-d H:i:s.u';
497
    }
498
499
    /**
500
     * {@inheritdoc}
501
     */
502 10504
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
503
    {
504 10504
        return 'DATETIME';
505
    }
506
507
    /**
508
     * {@inheritdoc}
509
     */
510 9952
    public function getDateTimeTzFormatString()
511
    {
512 9952
        return $this->getDateTimeFormatString();
513
    }
514
515
    /**
516
     * {@inheritdoc}
517
     */
518 10504
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
519
    {
520 10504
        return 'DATE';
521
    }
522
523
    /**
524
     * {@inheritdoc}
525
     */
526 9929
    public function getDefaultTransactionIsolationLevel()
527
    {
528 9929
        return TransactionIsolationLevel::READ_UNCOMMITTED;
529
    }
530
531
    /**
532
     * {@inheritdoc}
533
     */
534 10454
    public function getDropDatabaseSQL($database)
535
    {
536 10454
        $database = new Identifier($database);
537
538 10454
        return "DROP DATABASE '" . $database->getName() . "'";
539
    }
540
541
    /**
542
     * {@inheritdoc}
543
     */
544 10062
    public function getDropIndexSQL($index, $table = null)
545
    {
546 10062
        if ($index instanceof Index) {
547 10054
            $index = $index->getQuotedName($this);
548
        }
549
550 10062
        if (! is_string($index)) {
551 10029
            throw new InvalidArgumentException(
552 10029
                'SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be string or ' . Index::class . '.'
553
            );
554
        }
555
556 10058
        if (! isset($table)) {
557 10054
            return 'DROP INDEX ' . $index;
558
        }
559
560 10058
        if ($table instanceof Table) {
561 10054
            $table = $table->getQuotedName($this);
562
        }
563
564 10058
        if (! is_string($table)) {
565 10004
            throw new InvalidArgumentException(
566 10004
                'SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be string or ' . Index::class . '.'
567
            );
568
        }
569
570 10054
        return 'DROP INDEX ' . $table . '.' . $index;
571
    }
572
573
    /**
574
     * {@inheritdoc}
575
     */
576 10454
    public function getDropViewSQL($name)
577
    {
578 10454
        return 'DROP VIEW ' . $name;
579
    }
580
581
    /**
582
     * {@inheritdoc}
583
     */
584 10790
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
585
    {
586 10790
        $sql              = '';
587 10790
        $foreignKeyName   = $foreignKey->getName();
588 10790
        $localColumns     = $foreignKey->getQuotedLocalColumns($this);
589 10790
        $foreignColumns   = $foreignKey->getQuotedForeignColumns($this);
590 10790
        $foreignTableName = $foreignKey->getQuotedForeignTableName($this);
591
592 10790
        if (! empty($foreignKeyName)) {
593 10774
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
594
        }
595
596 10790
        if (empty($localColumns)) {
597 10229
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
598
        }
599
600 10786
        if (empty($foreignColumns)) {
601 10204
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
602
        }
603
604 10782
        if (empty($foreignTableName)) {
605 10179
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
606
        }
607
608 10778
        if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) {
609 10304
            $sql .= 'NOT NULL ';
610
        }
611
612
        return $sql .
613 10778
            'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' .
614 10778
            'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) .
615 10778
            ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')';
616
    }
617
618
    /**
619
     * Returns foreign key MATCH clause for given type.
620
     *
621
     * @param int $type The foreign key match type
622
     *
623
     * @return string
624
     *
625
     * @throws InvalidArgumentException If unknown match type given.
626
     */
627 10312
    public function getForeignKeyMatchClauseSQL($type)
628
    {
629 10312
        switch ((int) $type) {
630 10312
            case self::FOREIGN_KEY_MATCH_SIMPLE:
631 10279
                return 'SIMPLE';
632
633 10312
            case self::FOREIGN_KEY_MATCH_FULL:
634 10279
                return 'FULL';
635
636 10312
            case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE:
637 10308
                return 'UNIQUE SIMPLE';
638
639 10283
            case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
640 10279
                return 'UNIQUE FULL';
641
            default:
642 10254
                throw new InvalidArgumentException('Invalid foreign key match type: ' . $type);
643
        }
644
    }
645
646
    /**
647
     * {@inheritdoc}
648
     */
649 10332
    public function getForeignKeyReferentialActionSQL($action)
650
    {
651
        // NO ACTION is not supported, therefore falling back to RESTRICT.
652 10332
        if (strtoupper($action) === 'NO ACTION') {
653 9304
            return 'RESTRICT';
654
        }
655
656 10328
        return parent::getForeignKeyReferentialActionSQL($action);
657
    }
658
659
    /**
660
     * {@inheritdoc}
661
     */
662 9979
    public function getForUpdateSQL()
663
    {
664 9979
        return '';
665
    }
666
667
    /**
668
     * {@inheritdoc}
669
     *
670
     * @deprecated Use application-generated UUIDs instead
671
     */
672 9979
    public function getGuidExpression()
673
    {
674 9979
        return 'NEWID()';
675
    }
676
677
    /**
678
     * {@inheritdoc}
679
     */
680 10508
    public function getGuidTypeDeclarationSQL(array $field)
681
    {
682 10508
        return 'UNIQUEIDENTIFIER';
683
    }
684
685
    /**
686
     * {@inheritdoc}
687
     */
688 10083
    public function getIndexDeclarationSQL($name, Index $index)
689
    {
690
        // Index declaration in statements like CREATE TABLE is not supported.
691 10083
        throw DBALException::notSupported(__METHOD__);
692
    }
693
694
    /**
695
     * {@inheritdoc}
696
     */
697 10819
    public function getIntegerTypeDeclarationSQL(array $columnDef)
698
    {
699 10819
        $columnDef['integer_type'] = 'INT';
700
701 10819
        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 9579
    public function getListTableColumnsSQL($table, $database = null)
716
    {
717 9579
        $user = 'USER_NAME()';
718
719 9579
        if (strpos($table, '.') !== false) {
720 9579
            [$user, $table] = explode('.', $table);
721 9579
            $user           = $this->quoteStringLiteral($user);
722
        }
723
724 9579
        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 9579
            $table,
745 9579
            $user
746
        );
747
    }
748
749
    /**
750
     * {@inheritdoc}
751
     *
752
     * @todo Where is this used? Which information should be retrieved?
753
     */
754 9558
    public function getListTableConstraintsSQL($table)
755
    {
756 9558
        $user = '';
757
758 9558
        if (strpos($table, '.') !== false) {
759 9529
            [$user, $table] = explode('.', $table);
760 9529
            $user           = $this->quoteStringLiteral($user);
761 9529
            $table          = $this->quoteStringLiteral($table);
762
        } else {
763 9554
            $table = $this->quoteStringLiteral($table);
764
        }
765
766 9558
        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 9558
            $table,
776 9558
            $user
777
        );
778
    }
779
780
    /**
781
     * {@inheritdoc}
782
     */
783 9508
    public function getListTableForeignKeysSQL($table)
784
    {
785 9508
        $user = '';
786
787 9508
        if (strpos($table, '.') !== false) {
788 9479
            [$user, $table] = explode('.', $table);
789 9479
            $user           = $this->quoteStringLiteral($user);
790 9479
            $table          = $this->quoteStringLiteral($table);
791
        } else {
792 9504
            $table = $this->quoteStringLiteral($table);
793
        }
794
795 9508
        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 9508
            $table,
869 9508
            $user
870
        );
871
    }
872
873
    /**
874
     * {@inheritdoc}
875
     */
876 9458
    public function getListTableIndexesSQL($table, $currentDatabase = null)
877
    {
878 9458
        $user = '';
879
880 9458
        if (strpos($table, '.') !== false) {
881 9429
            [$user, $table] = explode('.', $table);
882 9429
            $user           = $this->quoteStringLiteral($user);
883 9429
            $table          = $this->quoteStringLiteral($table);
884
        } else {
885 9454
            $table = $this->quoteStringLiteral($table);
886
        }
887
888 9458
        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 9458
            $table,
929 9458
            $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 9979
    public function getLocateExpression($str, $substr, $startPos = false)
976
    {
977 9979
        if ($startPos === false) {
978 9979
            return 'LOCATE(' . $str . ', ' . $substr . ')';
979
        }
980
981 9979
        return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
982
    }
983
984
    /**
985
     * {@inheritdoc}
986
     */
987 10558
    public function getMaxIdentifierLength()
988
    {
989 10558
        return 128;
990
    }
991
992
    /**
993
     * {@inheritdoc}
994
     */
995 9979
    public function getMd5Expression($column)
996
    {
997 9979
        return 'HASH(' . $column . ", 'MD5')";
998
    }
999
1000
    /**
1001
     * {@inheritdoc}
1002
     */
1003 9412
    public function getName()
1004
    {
1005 9412
        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 10439
    public function getPrimaryKeyDeclarationSQL(Index $index, $name = null)
1020
    {
1021 10439
        if (! $index->isPrimary()) {
1022
            throw new InvalidArgumentException(
1023
                'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()'
1024
            );
1025
        }
1026
1027 10439
        return $this->getTableConstraintDeclarationSQL($index, $name);
1028
    }
1029
1030
    /**
1031
     * {@inheritdoc}
1032
     */
1033 9908
    public function getSetTransactionIsolationSQL($level)
1034
    {
1035 9908
        return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level);
1036
    }
1037
1038
    /**
1039
     * {@inheritdoc}
1040
     */
1041 10504
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
1042
    {
1043 10504
        $columnDef['integer_type'] = 'SMALLINT';
1044
1045 10504
        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 10454
    public function getStartDatabaseSQL($database)
1062
    {
1063 10454
        $database = new Identifier($database);
1064
1065 10454
        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 10454
    public function getStopDatabaseSQL($database)
1081
    {
1082 10454
        $database = new Identifier($database);
1083
1084 10454
        return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY';
1085
    }
1086
1087
    /**
1088
     * {@inheritdoc}
1089
     */
1090 9979
    public function getSubstringExpression($value, $from, $length = null)
1091
    {
1092 9979
        if ($length === null) {
1093 9979
            return 'SUBSTRING(' . $value . ', ' . $from . ')';
1094
        }
1095
1096 9979
        return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
1097
    }
1098
1099
    /**
1100
     * {@inheritdoc}
1101
     */
1102 10458
    public function getTemporaryTableSQL()
1103
    {
1104 10458
        return 'GLOBAL TEMPORARY';
1105
    }
1106
1107
    /**
1108
     * {@inheritdoc}
1109
     */
1110 9979
    public function getTimeFormatString()
1111
    {
1112 9979
        return 'H:i:s.u';
1113
    }
1114
1115
    /**
1116
     * {@inheritdoc}
1117
     */
1118 10504
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
1119
    {
1120 10504
        return 'TIME';
1121
    }
1122
1123
    /**
1124
     * {@inheritdoc}
1125
     */
1126 9979
    public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
1127
    {
1128 9979
        if (! $char) {
1129 9979
            switch ($pos) {
1130
                case TrimMode::LEADING:
1131 9979
                    return $this->getLtrimExpression($str);
1132
                case TrimMode::TRAILING:
1133 9979
                    return $this->getRtrimExpression($str);
1134
                default:
1135 9979
                    return 'TRIM(' . $str . ')';
1136
            }
1137
        }
1138
1139 9979
        $pattern = "'%[^' + " . $char . " + ']%'";
1140
1141 9979
        switch ($pos) {
1142
            case TrimMode::LEADING:
1143 9979
                return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
1144
            case TrimMode::TRAILING:
1145 9979
                return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
1146
            default:
1147 9979
                return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
1148 9979
                    'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
1149
        }
1150
    }
1151
1152
    /**
1153
     * {@inheritdoc}
1154
     */
1155 10458
    public function getTruncateTableSQL($tableName, $cascade = false)
1156
    {
1157 10458
        $tableIdentifier = new Identifier($tableName);
1158
1159 10458
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1160
    }
1161
1162
    /**
1163
     * {@inheritdoc}
1164
     */
1165 10366
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
1166
    {
1167 10366
        if ($index->isPrimary()) {
1168
            throw new InvalidArgumentException(
1169
                'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().'
1170
            );
1171
        }
1172
1173 10366
        if (! $index->isUnique()) {
1174
            throw new InvalidArgumentException(
1175
                'Can only create unique constraint declarations, no common index declarations with ' .
1176
                'getUniqueConstraintDeclarationSQL().'
1177
            );
1178
        }
1179
1180 10366
        return $this->getTableConstraintDeclarationSQL($index, $name);
1181
    }
1182
1183
    /**
1184
     * {@inheritdoc}
1185
     */
1186 10516
    public function getVarcharDefaultLength()
1187
    {
1188 10516
        return 1;
1189
    }
1190
1191
    /**
1192
     * {@inheritdoc}
1193
     */
1194 10827
    public function getVarcharMaxLength()
1195
    {
1196 10827
        return 32767;
1197
    }
1198
1199
    /**
1200
     * {@inheritdoc}
1201
     */
1202 10955
    public function hasNativeGuidType()
1203
    {
1204 10955
        return true;
1205
    }
1206
1207
    /**
1208
     * {@inheritdoc}
1209
     */
1210 9729
    public function prefersIdentityColumns()
1211
    {
1212 9729
        return true;
1213
    }
1214
1215
    /**
1216
     * {@inheritdoc}
1217
     */
1218 10827
    public function supportsCommentOnStatement()
1219
    {
1220 10827
        return true;
1221
    }
1222
1223
    /**
1224
     * {@inheritdoc}
1225
     */
1226 4
    public function supportsIdentityColumns()
1227
    {
1228 4
        return true;
1229
    }
1230
1231
    /**
1232
     * {@inheritdoc}
1233
     */
1234 10819
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
1235
    {
1236 10819
        $unsigned      = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : '';
1237 10819
        $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : '';
1238
1239 10819
        return $unsigned . $columnDef['integer_type'] . $autoincrement;
1240
    }
1241
1242
    /**
1243
     * {@inheritdoc}
1244
     */
1245 10823
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1246
    {
1247 10823
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1248 10823
        $indexSql      = [];
1249
1250 10823
        if (! empty($options['uniqueConstraints'])) {
1251
            foreach ((array) $options['uniqueConstraints'] as $name => $definition) {
1252
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1253
            }
1254
        }
1255
1256 10823
        if (! empty($options['indexes'])) {
1257
            /** @var Index $index */
1258 10791
            foreach ((array) $options['indexes'] as $index) {
1259 10791
                $indexSql[] = $this->getCreateIndexSQL($index, $tableName);
1260
            }
1261
        }
1262
1263 10823
        if (! empty($options['primary'])) {
1264 10799
            $flags = '';
1265
1266 10799
            if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) {
1267
                $flags = ' CLUSTERED ';
1268
            }
1269
1270 10799
            $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')';
1271
        }
1272
1273 10823
        if (! empty($options['foreignKeys'])) {
1274 10758
            foreach ((array) $options['foreignKeys'] as $definition) {
1275 10758
                $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition);
1276
            }
1277
        }
1278
1279 10823
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1280 10823
        $check = $this->getCheckDeclarationSQL($columns);
1281
1282 10823
        if (! empty($check)) {
1283 10729
            $query .= ', ' . $check;
1284
        }
1285
1286 10823
        $query .= ')';
1287
1288 10823
        return array_merge([$query], $indexSql);
1289
    }
1290
1291
    /**
1292
     * {@inheritdoc}
1293
     */
1294 9908
    protected function _getTransactionIsolationLevelSQL($level)
1295
    {
1296 8
        switch ($level) {
1297 9900
            case TransactionIsolationLevel::READ_UNCOMMITTED:
1298 9904
                return '0';
1299 9900
            case TransactionIsolationLevel::READ_COMMITTED:
1300 9904
                return '1';
1301 9900
            case TransactionIsolationLevel::REPEATABLE_READ:
1302 9904
                return '2';
1303 9900
            case TransactionIsolationLevel::SERIALIZABLE:
1304 9904
                return '3';
1305
            default:
1306 9879
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
1307
        }
1308
    }
1309
1310
    /**
1311
     * {@inheritdoc}
1312
     */
1313 9874
    protected function doModifyLimitQuery($query, $limit, $offset)
1314
    {
1315 9874
        $limitOffsetClause = $this->getTopClauseSQL($limit, $offset);
1316
1317 9874
        if ($limitOffsetClause === '') {
1318 8154
            return $query;
1319
        }
1320
1321 9870
        if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) {
1322
            return $query;
1323
        }
1324
1325 9870
        return $matches[1] . $limitOffsetClause . ' ' . $matches[3];
1326
    }
1327
1328 9874
    private function getTopClauseSQL(?int $limit, ?int $offset) : string
1329
    {
1330 9874
        if ($offset > 0) {
1331 9808
            return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1);
1332
        }
1333
1334 9866
        return $limit === null ? '' : 'TOP ' . $limit;
1335
    }
1336
1337
    /**
1338
     * Return the INDEX query section dealing with non-standard
1339
     * SQL Anywhere options.
1340
     *
1341
     * @param Index $index Index definition
1342
     *
1343
     * @return string
1344
     */
1345 10807
    protected function getAdvancedIndexOptionsSQL(Index $index)
1346
    {
1347 10807
        $sql = '';
1348
1349 10807
        if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) {
1350 10104
            $sql .= ' FOR OLAP WORKLOAD';
1351
        }
1352
1353 10807
        return $sql;
1354
    }
1355
1356
    /**
1357
     * {@inheritdoc}
1358
     */
1359 9679
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
1360
    {
1361 9679
        return $fixed
1362 9679
            ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'
1363 9679
            : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')';
1364
    }
1365
1366
    /**
1367
     * Returns the SQL snippet for creating a table constraint.
1368
     *
1369
     * @param Constraint  $constraint The table constraint to create the SQL snippet for.
1370
     * @param string|null $name       The table constraint name to use if any.
1371
     *
1372
     * @return string
1373
     *
1374
     * @throws InvalidArgumentException If the given table constraint type is not supported by this method.
1375
     */
1376 10467
    protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null)
1377
    {
1378 10467
        if ($constraint instanceof ForeignKeyConstraint) {
1379
            return $this->getForeignKeyDeclarationSQL($constraint);
1380
        }
1381
1382 10467
        if (! $constraint instanceof Index) {
1383 10129
            throw new InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint));
1384
        }
1385
1386 10463
        if (! $constraint->isPrimary() && ! $constraint->isUnique()) {
1387 10154
            throw new InvalidArgumentException(
1388
                'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' .
1389 10154
                'with getTableConstraintDeclarationSQL().'
1390
            );
1391
        }
1392
1393 10459
        $constraintColumns = $constraint->getQuotedColumns($this);
1394
1395 10459
        if (empty($constraintColumns)) {
1396 10408
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1397
        }
1398
1399 10451
        $sql   = '';
1400 10451
        $flags = '';
1401
1402 10451
        if (! empty($name)) {
1403 10445
            $name = new Identifier($name);
1404 10445
            $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' ';
1405
        }
1406
1407 10451
        if ($constraint->hasFlag('clustered')) {
1408 10437
            $flags = 'CLUSTERED ';
1409
        }
1410
1411 10451
        if ($constraint->isPrimary()) {
1412 10439
            return $sql . 'PRIMARY KEY ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1413
        }
1414
1415 10366
        return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1416
    }
1417
1418
    /**
1419
     * {@inheritdoc}
1420
     */
1421 10808
    protected function getCreateIndexSQLFlags(Index $index)
1422
    {
1423 10808
        $type = '';
1424 10808
        if ($index->hasFlag('virtual')) {
1425 10104
            $type .= 'VIRTUAL ';
1426
        }
1427
1428 10808
        if ($index->isUnique()) {
1429 10112
            $type .= 'UNIQUE ';
1430
        }
1431
1432 10808
        if ($index->hasFlag('clustered')) {
1433 10104
            $type .= 'CLUSTERED ';
1434
        }
1435
1436 10808
        return $type;
1437
    }
1438
1439
    /**
1440
     * {@inheritdoc}
1441
     */
1442 8445
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
1443
    {
1444 8445
        return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)];
1445
    }
1446
1447
    /**
1448
     * {@inheritdoc}
1449
     */
1450 2777
    protected function getReservedKeywordsClass()
1451
    {
1452 2777
        return Keywords\SQLAnywhereKeywords::class;
1453
    }
1454
1455
    /**
1456
     * {@inheritdoc}
1457
     */
1458 10823
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
1459
    {
1460 10823
        return $fixed
1461 8204
            ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')')
1462 10823
            : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')');
1463
    }
1464
1465
    /**
1466
     * {@inheritdoc}
1467
     */
1468 9722
    protected function initializeDoctrineTypeMappings()
1469
    {
1470 9722
        $this->doctrineTypeMapping = [
1471
            'char' => 'string',
1472
            'long nvarchar' => 'text',
1473
            'long varchar' => 'text',
1474
            'nchar' => 'string',
1475
            'ntext' => 'text',
1476
            'nvarchar' => 'string',
1477
            'text' => 'text',
1478
            'uniqueidentifierstr' => 'guid',
1479
            'varchar' => 'string',
1480
            'xml' => 'text',
1481
            'bigint' => 'bigint',
1482
            'unsigned bigint' => 'bigint',
1483
            'bit' => 'boolean',
1484
            'decimal' => 'decimal',
1485
            'double' => 'float',
1486
            'float' => 'float',
1487
            'int' => 'integer',
1488
            'integer' => 'integer',
1489
            'unsigned int' => 'integer',
1490
            'numeric' => 'decimal',
1491
            'smallint' => 'smallint',
1492
            'unsigned smallint' => 'smallint',
1493
            'tinyint' => 'smallint',
1494
            'unsigned tinyint' => 'smallint',
1495
            'money' => 'decimal',
1496
            'smallmoney' => 'decimal',
1497
            'long varbit' => 'text',
1498
            'varbit' => 'string',
1499
            'date' => 'date',
1500
            'datetime' => 'datetime',
1501
            'smalldatetime' => 'datetime',
1502
            'time' => 'time',
1503
            'timestamp' => 'datetime',
1504
            'binary' => 'binary',
1505
            'image' => 'blob',
1506
            'long binary' => 'blob',
1507
            'uniqueidentifier' => 'guid',
1508
            'varbinary' => 'binary',
1509
        ];
1510 9722
    }
1511
}
1512