Completed
Push — master ( 208059...77ecc6 )
by Marco
34:46 queued 14s
created

SQLAnywherePlatform   F

Complexity

Total Complexity 194

Size/Duplication

Total Lines 1470
Duplicated Lines 0 %

Test Coverage

Coverage 94.63%

Importance

Changes 0
Metric Value
wmc 194
eloc 429
dl 0
loc 1470
ccs 423
cts 447
cp 0.9463
rs 2
c 0
b 0
f 0

88 Methods

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

How to fix   Complexity   

Complex Class

Complex classes like SQLAnywherePlatform often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SQLAnywherePlatform, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\LockMode;
7
use Doctrine\DBAL\Schema\Column;
8
use Doctrine\DBAL\Schema\ColumnDiff;
9
use Doctrine\DBAL\Schema\Constraint;
10
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
11
use Doctrine\DBAL\Schema\Identifier;
12
use Doctrine\DBAL\Schema\Index;
13
use Doctrine\DBAL\Schema\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
class SQLAnywherePlatform extends AbstractPlatform
38
{
39
    public const FOREIGN_KEY_MATCH_SIMPLE        = 1;
40
    public const FOREIGN_KEY_MATCH_FULL          = 2;
41
    public const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
42
    public const FOREIGN_KEY_MATCH_FULL_UNIQUE   = 130;
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 12439
    public function appendLockHint($fromClause, $lockMode)
48
    {
49 12439
        switch (true) {
50
            case $lockMode === LockMode::NONE:
51 12341
                return $fromClause . ' WITH (NOLOCK)';
52
53 12431
            case $lockMode === LockMode::PESSIMISTIC_READ:
54 12291
                return $fromClause . ' WITH (UPDLOCK)';
55
56 12423
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
57 12266
                return $fromClause . ' WITH (XLOCK)';
58
59
            default:
60 12415
                return $fromClause;
61
        }
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     *
67
     * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
68
     */
69 12216
    public function fixSchemaElementName($schemaElementName)
70
    {
71 12216
        $maxIdentifierLength = $this->getMaxIdentifierLength();
72
73 12216
        if (strlen($schemaElementName) > $maxIdentifierLength) {
74 12216
            return substr($schemaElementName, 0, $maxIdentifierLength);
75
        }
76
77 12216
        return $schemaElementName;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 12514
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
84
    {
85 12514
        $query = '';
86
87 12514
        if ($foreignKey->hasOption('match')) {
88 11991
            $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
89
        }
90
91 12514
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
92
93 12514
        if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
94 11991
            $query .= ' CHECK ON COMMIT';
95
        }
96
97 12514
        if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
98 11991
            $query .= ' CLUSTERED';
99
        }
100
101 12514
        if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
102 11991
            $query .= ' FOR OLAP WORKLOAD';
103
        }
104
105 12514
        return $query;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 12528
    public function getAlterTableSQL(TableDiff $diff)
112
    {
113 12528
        $sql          = [];
114 12528
        $columnSql    = [];
115 12528
        $commentsSQL  = [];
116 12528
        $tableSql     = [];
117 12528
        $alterClauses = [];
118
119 12528
        foreach ($diff->addedColumns as $column) {
120 10188
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
121
                continue;
122
            }
123
124 10188
            $alterClauses[] = $this->getAlterTableAddColumnClause($column);
125
126 10188
            $comment = $this->getColumnComment($column);
127
128 10188
            if ($comment === null || $comment === '') {
129 10180
                continue;
130
            }
131
132 10064
            $commentsSQL[] = $this->getCommentOnColumnSQL(
133 10064
                $diff->getName($this)->getQuotedName($this),
134 10064
                $column->getQuotedName($this),
135 416
                $comment
136
            );
137
        }
138
139 12528
        foreach ($diff->removedColumns as $column) {
140 10180
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
141
                continue;
142
            }
143
144 10180
            $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
145
        }
146
147 12528
        foreach ($diff->changedColumns as $columnDiff) {
148 12472
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
149
                continue;
150
            }
151
152 12472
            $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
153
154 12472
            if ($alterClause !== null) {
155 10196
                $alterClauses[] = $alterClause;
156
            }
157
158 12472
            if (! $columnDiff->hasChanged('comment')) {
159 10196
                continue;
160
            }
161
162 12432
            $column = $columnDiff->column;
163
164 12432
            $commentsSQL[] = $this->getCommentOnColumnSQL(
165 12432
                $diff->getName($this)->getQuotedName($this),
166 12432
                $column->getQuotedName($this),
167 12432
                $this->getColumnComment($column)
168
            );
169
        }
170
171 12528
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
172 10138
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
173
                continue;
174
            }
175
176 10138
            $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
177 10138
                $this->getAlterTableRenameColumnClause($oldColumnName, $column);
178
        }
179
180 12528
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
181 12528
            if (! empty($alterClauses)) {
182 10204
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
183
            }
184
185 12528
            $sql = array_merge($sql, $commentsSQL);
186
187 12528
            $newName = $diff->getNewName();
188
189 12528
            if ($newName !== false) {
190 10172
                $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
191 10172
                    $this->getAlterTableRenameTableClause($newName);
192
            }
193
194 12528
            $sql = array_merge(
195 12528
                $this->getPreAlterTableIndexForeignKeySQL($diff),
196 12528
                $sql,
197 12528
                $this->getPostAlterTableIndexForeignKeySQL($diff)
198
            );
199
        }
200
201 12528
        return array_merge($sql, $tableSql, $columnSql);
202
    }
203
204
    /**
205
     * Returns the SQL clause for creating a column in a table alteration.
206
     *
207
     * @param Column $column The column to add.
208
     *
209
     * @return string
210
     */
211 10188
    protected function getAlterTableAddColumnClause(Column $column)
212
    {
213 10188
        return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
214
    }
215
216
    /**
217
     * Returns the SQL clause for altering a table.
218
     *
219
     * @param Identifier $tableName The quoted name of the table to alter.
220
     *
221
     * @return string
222
     */
223 10220
    protected function getAlterTableClause(Identifier $tableName)
224
    {
225 10220
        return 'ALTER TABLE ' . $tableName->getQuotedName($this);
226
    }
227
228
    /**
229
     * Returns the SQL clause for dropping a column in a table alteration.
230
     *
231
     * @param Column $column The column to drop.
232
     *
233
     * @return string
234
     */
235 10180
    protected function getAlterTableRemoveColumnClause(Column $column)
236
    {
237 10180
        return 'DROP ' . $column->getQuotedName($this);
238
    }
239
240
    /**
241
     * Returns the SQL clause for renaming a column in a table alteration.
242
     *
243
     * @param string $oldColumnName The quoted name of the column to rename.
244
     * @param Column $column        The column to rename to.
245
     *
246
     * @return string
247
     */
248 10138
    protected function getAlterTableRenameColumnClause($oldColumnName, Column $column)
249
    {
250 10138
        $oldColumnName = new Identifier($oldColumnName);
251
252 10138
        return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
253
    }
254
255
    /**
256
     * Returns the SQL clause for renaming a table in a table alteration.
257
     *
258
     * @param Identifier $newTableName The quoted name of the table to rename to.
259
     *
260
     * @return string
261
     */
262 10172
    protected function getAlterTableRenameTableClause(Identifier $newTableName)
263
    {
264 10172
        return 'RENAME ' . $newTableName->getQuotedName($this);
265
    }
266
267
    /**
268
     * Returns the SQL clause for altering a column in a table alteration.
269
     *
270
     * This method returns null in case that only the column comment has changed.
271
     * Changes in column comments have to be handled differently.
272
     *
273
     * @param ColumnDiff $columnDiff The diff of the column to alter.
274
     *
275
     * @return string|null
276
     */
277 12472
    protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff)
278
    {
279 12472
        $column = $columnDiff->column;
280
281
        // Do not return alter clause if only comment has changed.
282 12472
        if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) {
283
            $columnAlterationClause = 'ALTER ' .
284 10196
                $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
285
286 10196
            if ($columnDiff->hasChanged('default') && $column->getDefault() === null) {
287
                $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT';
288
            }
289
290 10196
            return $columnAlterationClause;
291
        }
292
293 12432
        return null;
294
    }
295
296
    /**
297
     * {@inheritdoc}
298
     */
299 12191
    public function getBigIntTypeDeclarationSQL(array $columnDef)
300
    {
301 12191
        $columnDef['integer_type'] = 'BIGINT';
302
303 12191
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
304
    }
305
306
    /**
307
     * {@inheritdoc}
308
     */
309 11324
    public function getBinaryDefaultLength()
310
    {
311 11324
        return 1;
312
    }
313
314
    /**
315
     * {@inheritdoc}
316
     */
317 11332
    public function getBinaryMaxLength()
318
    {
319 11332
        return 32767;
320
    }
321
322
    /**
323
     * {@inheritdoc}
324
     */
325 12199
    public function getBlobTypeDeclarationSQL(array $field)
326
    {
327 12199
        return 'LONG BINARY';
328
    }
329
330
    /**
331
     * {@inheritdoc}
332
     *
333
     * BIT type columns require an explicit NULL declaration
334
     * in SQL Anywhere if they shall be nullable.
335
     * Otherwise by just omitting the NOT NULL clause,
336
     * SQL Anywhere will declare them NOT NULL nonetheless.
337
     */
338 12199
    public function getBooleanTypeDeclarationSQL(array $columnDef)
339
    {
340 12199
        $nullClause = isset($columnDef['notnull']) && (bool) $columnDef['notnull'] === false ? ' NULL' : '';
341
342 12199
        return 'BIT' . $nullClause;
343
    }
344
345
    /**
346
     * {@inheritdoc}
347
     */
348 12207
    public function getClobTypeDeclarationSQL(array $field)
349
    {
350 12207
        return 'TEXT';
351
    }
352
353
    /**
354
     * {@inheritdoc}
355
     */
356 12472
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
357
    {
358 12472
        $tableName  = new Identifier($tableName);
359 12472
        $columnName = new Identifier($columnName);
360 12472
        $comment    = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
361
362 12472
        return sprintf(
363 64
            'COMMENT ON COLUMN %s.%s IS %s',
364 12472
            $tableName->getQuotedName($this),
365 12472
            $columnName->getQuotedName($this),
366 12472
            $comment
367
        );
368
    }
369
370
    /**
371
     * {@inheritdoc}
372
     */
373 11666
    public function getConcatExpression()
374
    {
375 11666
        return 'STRING(' . implode(', ', (array) func_get_args()) . ')';
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     */
381 11857
    public function getCreateConstraintSQL(Constraint $constraint, $table)
382
    {
383 11857
        if ($constraint instanceof ForeignKeyConstraint) {
384 10214
            return $this->getCreateForeignKeySQL($constraint, $table);
385
        }
386
387 11857
        if ($table instanceof Table) {
388 11841
            $table = $table->getQuotedName($this);
389
        }
390
391 11857
        return 'ALTER TABLE ' . $table .
392 11857
               ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this));
393
    }
394
395
    /**
396
     * {@inheritdoc}
397
     */
398 12141
    public function getCreateDatabaseSQL($database)
399
    {
400 12141
        $database = new Identifier($database);
401
402 12141
        return "CREATE DATABASE '" . $database->getName() . "'";
403
    }
404
405
    /**
406
     * {@inheritdoc}
407
     *
408
     * Appends SQL Anywhere specific flags if given.
409
     */
410 12549
    public function getCreateIndexSQL(Index $index, $table)
411
    {
412 12549
        return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index);
413
    }
414
415
    /**
416
     * {@inheritdoc}
417
     */
418 12070
    public function getCreatePrimaryKeySQL(Index $index, $table)
419
    {
420 12070
        if ($table instanceof Table) {
421 12066
            $table = $table->getQuotedName($this);
422
        }
423
424 12070
        return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
425
    }
426
427
    /**
428
     * {@inheritdoc}
429
     */
430 12141
    public function getCreateTemporaryTableSnippetSQL()
431
    {
432 12141
        return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE';
433
    }
434
435
    /**
436
     * {@inheritdoc}
437
     */
438 12141
    public function getCreateViewSQL($name, $sql)
439
    {
440 12141
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
441
    }
442
443
    /**
444
     * {@inheritdoc}
445
     */
446 11674
    public function getCurrentDateSQL()
447
    {
448 11674
        return 'CURRENT DATE';
449
    }
450
451
    /**
452
     * {@inheritdoc}
453
     */
454 11666
    public function getCurrentTimeSQL()
455
    {
456 11666
        return 'CURRENT TIME';
457
    }
458
459
    /**
460
     * {@inheritdoc}
461
     */
462 11674
    public function getCurrentTimestampSQL()
463
    {
464 11674
        return 'CURRENT TIMESTAMP';
465
    }
466
467
    /**
468
     * {@inheritdoc}
469
     */
470 11666
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
471
    {
472 11666
        $factorClause = '';
473
474 11666
        if ($operator === '-') {
475 11666
            $factorClause = '-1 * ';
476
        }
477
478 11666
        return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
479
    }
480
481
    /**
482
     * {@inheritdoc}
483
     */
484 11666
    public function getDateDiffExpression($date1, $date2)
485
    {
486 11666
        return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')';
487
    }
488
489
    /**
490
     * {@inheritdoc}
491
     */
492 11670
    public function getDateTimeFormatString()
493
    {
494 11670
        return 'Y-m-d H:i:s.u';
495
    }
496
497
    /**
498
     * {@inheritdoc}
499
     */
500 12191
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
501
    {
502 12191
        return 'DATETIME';
503
    }
504
505
    /**
506
     * {@inheritdoc}
507
     */
508 11637
    public function getDateTimeTzFormatString()
509
    {
510 11637
        return $this->getDateTimeFormatString();
511
    }
512
513
    /**
514
     * {@inheritdoc}
515
     */
516 12191
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
517
    {
518 12191
        return 'DATE';
519
    }
520
521
    /**
522
     * {@inheritdoc}
523
     */
524 11616
    public function getDefaultTransactionIsolationLevel()
525
    {
526 11616
        return TransactionIsolationLevel::READ_UNCOMMITTED;
527
    }
528
529
    /**
530
     * {@inheritdoc}
531
     */
532 12141
    public function getDropDatabaseSQL($database)
533
    {
534 12141
        $database = new Identifier($database);
535
536 12141
        return "DROP DATABASE '" . $database->getName() . "'";
537
    }
538
539
    /**
540
     * {@inheritdoc}
541
     */
542 11757
    public function getDropIndexSQL($index, $table = null)
543
    {
544 11757
        if ($index instanceof Index) {
545 11741
            $index = $index->getQuotedName($this);
546
        }
547
548 11757
        if (! is_string($index)) {
549 11716
            throw new InvalidArgumentException(
550 11716
                'SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be string or ' . Index::class . '.'
551
            );
552
        }
553
554 11749
        if (! isset($table)) {
555 11741
            return 'DROP INDEX ' . $index;
556
        }
557
558 11749
        if ($table instanceof Table) {
559 11741
            $table = $table->getQuotedName($this);
560
        }
561
562 11749
        if (! is_string($table)) {
563 11691
            throw new InvalidArgumentException(
564 11691
                'SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be string or ' . Index::class . '.'
565
            );
566
        }
567
568 11741
        return 'DROP INDEX ' . $table . '.' . $index;
569
    }
570
571
    /**
572
     * {@inheritdoc}
573
     */
574 12141
    public function getDropViewSQL($name)
575
    {
576 12141
        return 'DROP VIEW ' . $name;
577
    }
578
579
    /**
580
     * {@inheritdoc}
581
     */
582 12538
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
583
    {
584 12538
        $sql              = '';
585 12538
        $foreignKeyName   = $foreignKey->getName();
586 12538
        $localColumns     = $foreignKey->getQuotedLocalColumns($this);
587 12538
        $foreignColumns   = $foreignKey->getQuotedForeignColumns($this);
588 12538
        $foreignTableName = $foreignKey->getQuotedForeignTableName($this);
589
590 12538
        if (! empty($foreignKeyName)) {
591 12506
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
592
        }
593
594 12538
        if (empty($localColumns)) {
595 11916
            throw new InvalidArgumentException("Incomplete definition. 'local' required.");
596
        }
597
598 12530
        if (empty($foreignColumns)) {
599 11891
            throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
600
        }
601
602 12522
        if (empty($foreignTableName)) {
603 11866
            throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
604
        }
605
606 12514
        if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) {
607 11991
            $sql .= 'NOT NULL ';
608
        }
609
610
        return $sql .
611 12514
            'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' .
612 12514
            'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) .
613 12514
            ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')';
614
    }
615
616
    /**
617
     * Returns foreign key MATCH clause for given type.
618
     *
619
     * @param int $type The foreign key match type
620
     *
621
     * @return string
622
     *
623
     * @throws InvalidArgumentException If unknown match type given.
624
     */
625 12007
    public function getForeignKeyMatchClauseSQL($type)
626
    {
627 12007
        switch ((int) $type) {
628 12007
            case self::FOREIGN_KEY_MATCH_SIMPLE:
629 11966
                return 'SIMPLE';
630
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
631 12007
            case self::FOREIGN_KEY_MATCH_FULL:
632 11966
                return 'FULL';
633
                break;
634 12007
            case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE:
635 11999
                return 'UNIQUE SIMPLE';
636
                break;
637 11974
            case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
638 11966
                return 'UNIQUE FULL';
639
            default:
640 11941
                throw new InvalidArgumentException('Invalid foreign key match type: ' . $type);
641
        }
642
    }
643
644
    /**
645
     * {@inheritdoc}
646
     */
647 12047
    public function getForeignKeyReferentialActionSQL($action)
648
    {
649
        // NO ACTION is not supported, therefore falling back to RESTRICT.
650 12047
        if (strtoupper($action) === 'NO ACTION') {
651 10965
            return 'RESTRICT';
652
        }
653
654 12039
        return parent::getForeignKeyReferentialActionSQL($action);
655
    }
656
657
    /**
658
     * {@inheritdoc}
659
     */
660 11666
    public function getForUpdateSQL()
661
    {
662 11666
        return '';
663
    }
664
665
    /**
666
     * {@inheritdoc}
667
     *
668
     * @deprecated Use application-generated UUIDs instead
669
     */
670 11666
    public function getGuidExpression()
671
    {
672 11666
        return 'NEWID()';
673
    }
674
675
    /**
676
     * {@inheritdoc}
677
     */
678 12199
    public function getGuidTypeDeclarationSQL(array $field)
679
    {
680 12199
        return 'UNIQUEIDENTIFIER';
681
    }
682
683
    /**
684
     * {@inheritdoc}
685
     */
686 11774
    public function getIndexDeclarationSQL($name, Index $index)
687
    {
688
        // Index declaration in statements like CREATE TABLE is not supported.
689 11774
        throw DBALException::notSupported(__METHOD__);
690
    }
691
692
    /**
693
     * {@inheritdoc}
694
     */
695 12571
    public function getIntegerTypeDeclarationSQL(array $columnDef)
696
    {
697 12571
        $columnDef['integer_type'] = 'INT';
698
699 12571
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
700
    }
701
702
    /**
703
     * {@inheritdoc}
704
     */
705
    public function getListDatabasesSQL()
706
    {
707
        return 'SELECT db_name(number) AS name FROM sa_db_list()';
708
    }
709
710
    /**
711
     * {@inheritdoc}
712
     */
713 11216
    public function getListTableColumnsSQL($table, $database = null)
714
    {
715 11216
        $user = 'USER_NAME()';
716
717 11216
        if (strpos($table, '.') !== false) {
718 11216
            [$user, $table] = explode('.', $table);
719 11216
            $user           = $this->quoteStringLiteral($user);
720
        }
721
722 11216
        return sprintf(
723
            <<<'SQL'
724 8
SELECT    col.column_name,
725
          COALESCE(def.user_type_name, def.domain_name) AS 'type',
726
          def.declared_width AS 'length',
727
          def.scale,
728
          CHARINDEX('unsigned', def.domain_name) AS 'unsigned',
729
          IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull',
730
          col."default",
731
          def.is_autoincrement AS 'autoincrement',
732
          rem.remarks AS 'comment'
733
FROM      sa_describe_query('SELECT * FROM "%s"') AS def
734
JOIN      SYS.SYSTABCOL AS col
735
ON        col.table_id = def.base_table_id AND col.column_id = def.base_column_id
736
LEFT JOIN SYS.SYSREMARK AS rem
737
ON        col.object_id = rem.object_id
738
WHERE     def.base_owner_name = %s
739
ORDER BY  def.base_column_id ASC
740
SQL
741
            ,
742 11216
            $table,
743 11216
            $user
744
        );
745
    }
746
747
    /**
748
     * {@inheritdoc}
749
     *
750
     * @todo Where is this used? Which information should be retrieved?
751
     */
752 11199
    public function getListTableConstraintsSQL($table)
753
    {
754 11199
        $user = '';
755
756 11199
        if (strpos($table, '.') !== false) {
757 11166
            [$user, $table] = explode('.', $table);
758 11166
            $user           = $this->quoteStringLiteral($user);
759 11166
            $table          = $this->quoteStringLiteral($table);
760
        } else {
761 11191
            $table = $this->quoteStringLiteral($table);
762
        }
763
764 11199
        return sprintf(
765
            <<<'SQL'
766 16
SELECT con.*
767
FROM   SYS.SYSCONSTRAINT AS con
768
JOIN   SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id
769
WHERE  tab.table_name = %s
770
AND    tab.creator = USER_ID(%s)
771
SQL
772
            ,
773 11199
            $table,
774 11199
            $user
775
        );
776
    }
777
778
    /**
779
     * {@inheritdoc}
780
     */
781 11149
    public function getListTableForeignKeysSQL($table)
782
    {
783 11149
        $user = '';
784
785 11149
        if (strpos($table, '.') !== false) {
786 11116
            [$user, $table] = explode('.', $table);
787 11116
            $user           = $this->quoteStringLiteral($user);
788 11116
            $table          = $this->quoteStringLiteral($table);
789
        } else {
790 11141
            $table = $this->quoteStringLiteral($table);
791
        }
792
793 11149
        return sprintf(
794
            <<<'SQL'
795 16
SELECT    fcol.column_name AS local_column,
796
          ptbl.table_name AS foreign_table,
797
          pcol.column_name AS foreign_column,
798
          idx.index_name,
799
          IF fk.nulls = 'N'
800
              THEN 1
801
              ELSE NULL
802
          ENDIF AS notnull,
803
          CASE ut.referential_action
804
              WHEN 'C' THEN 'CASCADE'
805
              WHEN 'D' THEN 'SET DEFAULT'
806
              WHEN 'N' THEN 'SET NULL'
807
              WHEN 'R' THEN 'RESTRICT'
808
              ELSE NULL
809
          END AS  on_update,
810
          CASE dt.referential_action
811
              WHEN 'C' THEN 'CASCADE'
812
              WHEN 'D' THEN 'SET DEFAULT'
813
              WHEN 'N' THEN 'SET NULL'
814
              WHEN 'R' THEN 'RESTRICT'
815
              ELSE NULL
816
          END AS on_delete,
817
          IF fk.check_on_commit = 'Y'
818
              THEN 1
819
              ELSE NULL
820
          ENDIF AS check_on_commit, -- check_on_commit flag
821
          IF ftbl.clustered_index_id = idx.index_id
822
              THEN 1
823
              ELSE NULL
824
          ENDIF AS 'clustered', -- clustered flag
825
          IF fk.match_type = 0
826
              THEN NULL
827
              ELSE fk.match_type
828
          ENDIF AS 'match', -- match option
829
          IF pidx.max_key_distance = 1
830
              THEN 1
831
              ELSE NULL
832
          ENDIF AS for_olap_workload -- for_olap_workload flag
833
FROM      SYS.SYSFKEY AS fk
834
JOIN      SYS.SYSIDX AS idx
835
ON        fk.foreign_table_id = idx.table_id
836
AND       fk.foreign_index_id = idx.index_id
837
JOIN      SYS.SYSPHYSIDX pidx
838
ON        idx.table_id = pidx.table_id
839
AND       idx.phys_index_id = pidx.phys_index_id
840
JOIN      SYS.SYSTAB AS ptbl
841
ON        fk.primary_table_id = ptbl.table_id
842
JOIN      SYS.SYSTAB AS ftbl
843
ON        fk.foreign_table_id = ftbl.table_id
844
JOIN      SYS.SYSIDXCOL AS idxcol
845
ON        idx.table_id = idxcol.table_id
846
AND       idx.index_id = idxcol.index_id
847
JOIN      SYS.SYSTABCOL AS pcol
848
ON        ptbl.table_id = pcol.table_id
849
AND       idxcol.primary_column_id = pcol.column_id
850
JOIN      SYS.SYSTABCOL AS fcol
851
ON        ftbl.table_id = fcol.table_id
852
AND       idxcol.column_id = fcol.column_id
853
LEFT JOIN SYS.SYSTRIGGER ut
854
ON        fk.foreign_table_id = ut.foreign_table_id
855
AND       fk.foreign_index_id = ut.foreign_key_id
856
AND       ut.event = 'C'
857
LEFT JOIN SYS.SYSTRIGGER dt
858
ON        fk.foreign_table_id = dt.foreign_table_id
859
AND       fk.foreign_index_id = dt.foreign_key_id
860
AND       dt.event = 'D'
861
WHERE     ftbl.table_name = %s
862
AND       ftbl.creator = USER_ID(%s)
863
ORDER BY  fk.foreign_index_id ASC, idxcol.sequence ASC
864
SQL
865
            ,
866 11149
            $table,
867 11149
            $user
868
        );
869
    }
870
871
    /**
872
     * {@inheritdoc}
873
     */
874 11099
    public function getListTableIndexesSQL($table, $currentDatabase = null)
875
    {
876 11099
        $user = '';
877
878 11099
        if (strpos($table, '.') !== false) {
879 11066
            [$user, $table] = explode('.', $table);
880 11066
            $user           = $this->quoteStringLiteral($user);
881 11066
            $table          = $this->quoteStringLiteral($table);
882
        } else {
883 11091
            $table = $this->quoteStringLiteral($table);
884
        }
885
886 11099
        return sprintf(
887
            <<<'SQL'
888 16
SELECT   idx.index_name AS key_name,
889
         IF idx.index_category = 1
890
             THEN 1
891
             ELSE 0
892
         ENDIF AS 'primary',
893
         col.column_name,
894
         IF idx."unique" IN(1, 2, 5)
895
             THEN 0
896
             ELSE 1
897
         ENDIF AS non_unique,
898
         IF tbl.clustered_index_id = idx.index_id
899
             THEN 1
900
             ELSE NULL
901
         ENDIF AS 'clustered', -- clustered flag
902
         IF idx."unique" = 5
903
             THEN 1
904
             ELSE NULL
905
         ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag
906
         IF pidx.max_key_distance = 1
907
              THEN 1
908
              ELSE NULL
909
          ENDIF AS for_olap_workload -- for_olap_workload flag
910
FROM     SYS.SYSIDX AS idx
911
JOIN     SYS.SYSPHYSIDX pidx
912
ON       idx.table_id = pidx.table_id
913
AND      idx.phys_index_id = pidx.phys_index_id
914
JOIN     SYS.SYSIDXCOL AS idxcol
915
ON       idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id
916
JOIN     SYS.SYSTABCOL AS col
917
ON       idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id
918
JOIN     SYS.SYSTAB AS tbl
919
ON       idx.table_id = tbl.table_id
920
WHERE    tbl.table_name = %s
921
AND      tbl.creator = USER_ID(%s)
922
AND      idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints
923
ORDER BY idx.index_id ASC, idxcol.sequence ASC
924
SQL
925
            ,
926 11099
            $table,
927 11099
            $user
928
        );
929
    }
930
931
    /**
932
     * {@inheritdoc}
933
     */
934
    public function getListTablesSQL()
935
    {
936
        return "SELECT   tbl.table_name
937
                FROM     SYS.SYSTAB AS tbl
938
                JOIN     SYS.SYSUSER AS usr ON tbl.creator = usr.user_id
939
                JOIN     dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id
940
                WHERE    tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP'
941
                AND      usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
942
                AND      obj.type = 'U' -- user created tables only
943
                ORDER BY tbl.table_name ASC";
944
    }
945
946
    /**
947
     * {@inheritdoc}
948
     *
949
     * @todo Where is this used? Which information should be retrieved?
950
     */
951
    public function getListUsersSQL()
952
    {
953
        return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC';
954
    }
955
956
    /**
957
     * {@inheritdoc}
958
     */
959
    public function getListViewsSQL($database)
960
    {
961
        return "SELECT   tbl.table_name, v.view_def
962
                FROM     SYS.SYSVIEW v
963
                JOIN     SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id
964
                JOIN     SYS.SYSUSER usr ON tbl.creator = usr.user_id
965
                JOIN     dbo.SYSOBJECTS obj ON tbl.object_id = obj.id
966
                WHERE    usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
967
                ORDER BY tbl.table_name ASC";
968
    }
969
970
    /**
971
     * {@inheritdoc}
972
     */
973 11666
    public function getLocateExpression($str, $substr, $startPos = false)
974
    {
975 11666
        if ($startPos === false) {
976 11666
            return 'LOCATE(' . $str . ', ' . $substr . ')';
977
        }
978
979 11666
        return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
980
    }
981
982
    /**
983
     * {@inheritdoc}
984
     */
985 12249
    public function getMaxIdentifierLength()
986
    {
987 12249
        return 128;
988
    }
989
990
    /**
991
     * {@inheritdoc}
992
     */
993 11666
    public function getMd5Expression($column)
994
    {
995 11666
        return 'HASH(' . $column . ", 'MD5')";
996
    }
997
998
    /**
999
     * {@inheritdoc}
1000
     */
1001 12507
    public function getName()
1002
    {
1003 12507
        return 'sqlanywhere';
1004
    }
1005
1006
    /**
1007
     * Obtain DBMS specific SQL code portion needed to set a primary key
1008
     * declaration to be used in statements like ALTER TABLE.
1009
     *
1010
     * @param Index  $index Index definition
1011
     * @param string $name  Name of the primary key
1012
     *
1013
     * @return string DBMS specific SQL code portion needed to set a primary key
1014
     *
1015
     * @throws InvalidArgumentException If the given index is not a primary key.
1016
     */
1017 12136
    public function getPrimaryKeyDeclarationSQL(Index $index, $name = null)
1018
    {
1019 12136
        if (! $index->isPrimary()) {
1020
            throw new InvalidArgumentException(
1021
                'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()'
1022
            );
1023
        }
1024
1025 12136
        return $this->getTableConstraintDeclarationSQL($index, $name);
1026
    }
1027
1028
    /**
1029
     * {@inheritdoc}
1030
     */
1031 11599
    public function getSetTransactionIsolationSQL($level)
1032
    {
1033 11599
        return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level);
1034
    }
1035
1036
    /**
1037
     * {@inheritdoc}
1038
     */
1039 12191
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
1040
    {
1041 12191
        $columnDef['integer_type'] = 'SMALLINT';
1042
1043 12191
        return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
1044
    }
1045
1046
    /**
1047
     * Returns the SQL statement for starting an existing database.
1048
     *
1049
     * In SQL Anywhere you can start and stop databases on a
1050
     * database server instance.
1051
     * This is a required statement after having created a new database
1052
     * as it has to be explicitly started to be usable.
1053
     * SQL Anywhere does not automatically start a database after creation!
1054
     *
1055
     * @param string $database Name of the database to start.
1056
     *
1057
     * @return string
1058
     */
1059 12141
    public function getStartDatabaseSQL($database)
1060
    {
1061 12141
        $database = new Identifier($database);
1062
1063 12141
        return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF";
1064
    }
1065
1066
    /**
1067
     * Returns the SQL statement for stopping a running database.
1068
     *
1069
     * In SQL Anywhere you can start and stop databases on a
1070
     * database server instance.
1071
     * This is a required statement before dropping an existing database
1072
     * as it has to be explicitly stopped before it can be dropped.
1073
     *
1074
     * @param string $database Name of the database to stop.
1075
     *
1076
     * @return string
1077
     */
1078 12141
    public function getStopDatabaseSQL($database)
1079
    {
1080 12141
        $database = new Identifier($database);
1081
1082 12141
        return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY';
1083
    }
1084
1085
    /**
1086
     * {@inheritdoc}
1087
     */
1088 11666
    public function getSubstringExpression($value, $from, $length = null)
1089
    {
1090 11666
        if ($length === null) {
1091 11666
            return 'SUBSTRING(' . $value . ', ' . $from . ')';
1092
        }
1093
1094 11666
        return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
1095
    }
1096
1097
    /**
1098
     * {@inheritdoc}
1099
     */
1100 12149
    public function getTemporaryTableSQL()
1101
    {
1102 12149
        return 'GLOBAL TEMPORARY';
1103
    }
1104
1105
    /**
1106
     * {@inheritdoc}
1107
     */
1108 11666
    public function getTimeFormatString()
1109
    {
1110 11666
        return 'H:i:s.u';
1111
    }
1112
1113
    /**
1114
     * {@inheritdoc}
1115
     */
1116 12191
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
1117
    {
1118 12191
        return 'TIME';
1119
    }
1120
1121
    /**
1122
     * {@inheritdoc}
1123
     */
1124 11666
    public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
1125
    {
1126 11666
        if (! $char) {
1127 11666
            switch ($pos) {
1128
                case TrimMode::LEADING:
1129 11666
                    return $this->getLtrimExpression($str);
1130
                case TrimMode::TRAILING:
1131 11666
                    return $this->getRtrimExpression($str);
1132
                default:
1133 11666
                    return 'TRIM(' . $str . ')';
1134
            }
1135
        }
1136
1137 11666
        $pattern = "'%[^' + " . $char . " + ']%'";
1138
1139 11666
        switch ($pos) {
1140
            case TrimMode::LEADING:
1141 11666
                return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
1142
            case TrimMode::TRAILING:
1143 11666
                return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
1144
            default:
1145 11666
                return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
1146 11666
                    'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
1147
        }
1148
    }
1149
1150
    /**
1151
     * {@inheritdoc}
1152
     */
1153 12149
    public function getTruncateTableSQL($tableName, $cascade = false)
1154
    {
1155 12149
        $tableIdentifier = new Identifier($tableName);
1156
1157 12149
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1158
    }
1159
1160
    /**
1161
     * {@inheritdoc}
1162
     */
1163 12065
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
1164
    {
1165 12065
        if ($index->isPrimary()) {
1166
            throw new InvalidArgumentException(
1167
                'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().'
1168
            );
1169
        }
1170
1171 12065
        if (! $index->isUnique()) {
1172
            throw new InvalidArgumentException(
1173
                'Can only create unique constraint declarations, no common index declarations with ' .
1174
                'getUniqueConstraintDeclarationSQL().'
1175
            );
1176
        }
1177
1178 12065
        return $this->getTableConstraintDeclarationSQL($index, $name);
1179
    }
1180
1181
    /**
1182
     * {@inheritdoc}
1183
     */
1184 12215
    public function getVarcharDefaultLength()
1185
    {
1186 12215
        return 1;
1187
    }
1188
1189
    /**
1190
     * {@inheritdoc}
1191
     */
1192 12587
    public function getVarcharMaxLength()
1193
    {
1194 12587
        return 32767;
1195
    }
1196
1197
    /**
1198
     * {@inheritdoc}
1199
     */
1200 12843
    public function hasNativeGuidType()
1201
    {
1202 12843
        return true;
1203
    }
1204
1205
    /**
1206
     * {@inheritdoc}
1207
     */
1208 11416
    public function prefersIdentityColumns()
1209
    {
1210 11416
        return true;
1211
    }
1212
1213
    /**
1214
     * {@inheritdoc}
1215
     */
1216 12587
    public function supportsCommentOnStatement()
1217
    {
1218 12587
        return true;
1219
    }
1220
1221
    /**
1222
     * {@inheritdoc}
1223
     */
1224 11391
    public function supportsIdentityColumns()
1225
    {
1226 11391
        return true;
1227
    }
1228
1229
    /**
1230
     * {@inheritdoc}
1231
     */
1232 12571
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
1233
    {
1234 12571
        $unsigned      = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : '';
1235 12571
        $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : '';
1236
1237 12571
        return $unsigned . $columnDef['integer_type'] . $autoincrement;
1238
    }
1239
1240
    /**
1241
     * {@inheritdoc}
1242
     */
1243 12579
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
1244
    {
1245 12579
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1246 12579
        $indexSql      = [];
1247
1248 12579
        if (! empty($options['uniqueConstraints'])) {
1249
            foreach ((array) $options['uniqueConstraints'] as $name => $definition) {
1250
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1251
            }
1252
        }
1253
1254 12579
        if (! empty($options['indexes'])) {
1255
            /** @var Index $index */
1256 12515
            foreach ((array) $options['indexes'] as $index) {
1257 12515
                $indexSql[] = $this->getCreateIndexSQL($index, $tableName);
1258
            }
1259
        }
1260
1261 12579
        if (! empty($options['primary'])) {
1262 12531
            $flags = '';
1263
1264 12531
            if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) {
1265
                $flags = ' CLUSTERED ';
1266
            }
1267
1268 12531
            $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')';
1269
        }
1270
1271 12579
        if (! empty($options['foreignKeys'])) {
1272 12474
            foreach ((array) $options['foreignKeys'] as $definition) {
1273 12474
                $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition);
1274
            }
1275
        }
1276
1277 12579
        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1278 12579
        $check = $this->getCheckDeclarationSQL($columns);
1279
1280 12579
        if (! empty($check)) {
1281 12441
            $query .= ', ' . $check;
1282
        }
1283
1284 12579
        $query .= ')';
1285
1286 12579
        return array_merge([$query], $indexSql);
1287
    }
1288
1289
    /**
1290
     * {@inheritdoc}
1291
     */
1292 11599
    protected function _getTransactionIsolationLevelSQL($level)
1293
    {
1294 16
        switch ($level) {
1295 11583
            case TransactionIsolationLevel::READ_UNCOMMITTED:
1296 11591
                return 0;
1297 11583
            case TransactionIsolationLevel::READ_COMMITTED:
1298 11591
                return 1;
1299 11583
            case TransactionIsolationLevel::REPEATABLE_READ:
1300 11591
                return 2;
1301 11583
            case TransactionIsolationLevel::SERIALIZABLE:
1302 11591
                return 3;
1303
            default:
1304 11566
                throw new InvalidArgumentException('Invalid isolation level:' . $level);
1305
        }
1306
    }
1307
1308
    /**
1309
     * {@inheritdoc}
1310
     */
1311 11581
    protected function doModifyLimitQuery($query, $limit, $offset)
1312
    {
1313 11581
        $limitOffsetClause = $this->getTopClauseSQL($limit, $offset);
1314
1315 11581
        if ($limitOffsetClause === '') {
1316 9439
            return $query;
1317
        }
1318
1319 11573
        if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) {
1320
            return $query;
1321
        }
1322
1323 11573
        return $matches[1] . $limitOffsetClause . ' ' . $matches[3];
1324
    }
1325
1326 11581
    private function getTopClauseSQL(?int $limit, ?int $offset) : string
1327
    {
1328 11581
        if ($offset > 0) {
1329 11499
            return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1);
1330
        }
1331
1332 11565
        return $limit === null ? '' : 'TOP ' . $limit;
1333
    }
1334
1335
    /**
1336
     * Return the INDEX query section dealing with non-standard
1337
     * SQL Anywhere options.
1338
     *
1339
     * @param Index $index Index definition
1340
     *
1341
     * @return string
1342
     */
1343 12547
    protected function getAdvancedIndexOptionsSQL(Index $index)
1344
    {
1345 12547
        $sql = '';
1346
1347 12547
        if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) {
1348 11791
            $sql .= ' FOR OLAP WORKLOAD';
1349
        }
1350
1351 12547
        return $sql;
1352
    }
1353
1354
    /**
1355
     * {@inheritdoc}
1356
     */
1357 11316
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
1358
    {
1359 11316
        return $fixed
1360 11316
            ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'
1361 11316
            : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')';
1362
    }
1363
1364
    /**
1365
     * Returns the SQL snippet for creating a table constraint.
1366
     *
1367
     * @param Constraint  $constraint The table constraint to create the SQL snippet for.
1368
     * @param string|null $name       The table constraint name to use if any.
1369
     *
1370
     * @return string
1371
     *
1372
     * @throws InvalidArgumentException If the given table constraint type is not supported by this method.
1373
     */
1374 12192
    protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null)
1375
    {
1376 12192
        if ($constraint instanceof ForeignKeyConstraint) {
1377
            return $this->getForeignKeyDeclarationSQL($constraint);
1378
        }
1379
1380 12192
        if (! $constraint instanceof Index) {
1381 11816
            throw new InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint));
1382
        }
1383
1384 12184
        if (! $constraint->isPrimary() && ! $constraint->isUnique()) {
1385 11841
            throw new InvalidArgumentException(
1386
                'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' .
1387 11841
                'with getTableConstraintDeclarationSQL().'
1388
            );
1389
        }
1390
1391 12176
        $constraintColumns = $constraint->getQuotedColumns($this);
1392
1393 12176
        if (empty($constraintColumns)) {
1394 12099
            throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
1395
        }
1396
1397 12160
        $sql   = '';
1398 12160
        $flags = '';
1399
1400 12160
        if (! empty($name)) {
1401 12148
            $name = new Identifier($name);
1402 12148
            $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' ';
1403
        }
1404
1405 12160
        if ($constraint->hasFlag('clustered')) {
1406 12132
            $flags = 'CLUSTERED ';
1407
        }
1408
1409 12160
        if ($constraint->isPrimary()) {
1410 12136
            return $sql . 'PRIMARY KEY ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1411
        }
1412
1413 12065
        return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
1414
    }
1415
1416
    /**
1417
     * {@inheritdoc}
1418
     */
1419 12549
    protected function getCreateIndexSQLFlags(Index $index)
1420
    {
1421 12549
        $type = '';
1422 12549
        if ($index->hasFlag('virtual')) {
1423 11791
            $type .= 'VIRTUAL ';
1424
        }
1425
1426 12549
        if ($index->isUnique()) {
1427 11807
            $type .= 'UNIQUE ';
1428
        }
1429
1430 12549
        if ($index->hasFlag('clustered')) {
1431 11791
            $type .= 'CLUSTERED ';
1432
        }
1433
1434 12549
        return $type;
1435
    }
1436
1437
    /**
1438
     * {@inheritdoc}
1439
     */
1440 9746
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
1441
    {
1442 9746
        return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)];
1443
    }
1444
1445
    /**
1446
     * {@inheritdoc}
1447
     */
1448 3256
    protected function getReservedKeywordsClass()
1449
    {
1450 3256
        return Keywords\SQLAnywhereKeywords::class;
1451
    }
1452
1453
    /**
1454
     * {@inheritdoc}
1455
     */
1456 12579
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
1457
    {
1458 12579
        return $fixed
1459 9489
            ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')')
1460 12579
            : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')');
1461
    }
1462
1463
    /**
1464
     * {@inheritdoc}
1465
     */
1466 11377
    protected function initializeDoctrineTypeMappings()
1467
    {
1468 11377
        $this->doctrineTypeMapping = [
1469
            'char' => 'string',
1470
            'long nvarchar' => 'text',
1471
            'long varchar' => 'text',
1472
            'nchar' => 'string',
1473
            'ntext' => 'text',
1474
            'nvarchar' => 'string',
1475
            'text' => 'text',
1476
            'uniqueidentifierstr' => 'guid',
1477
            'varchar' => 'string',
1478
            'xml' => 'text',
1479
            'bigint' => 'bigint',
1480
            'unsigned bigint' => 'bigint',
1481
            'bit' => 'boolean',
1482
            'decimal' => 'decimal',
1483
            'double' => 'float',
1484
            'float' => 'float',
1485
            'int' => 'integer',
1486
            'integer' => 'integer',
1487
            'unsigned int' => 'integer',
1488
            'numeric' => 'decimal',
1489
            'smallint' => 'smallint',
1490
            'unsigned smallint' => 'smallint',
1491
            'tinyint' => 'smallint',
1492
            'unsigned tinyint' => 'smallint',
1493
            'money' => 'decimal',
1494
            'smallmoney' => 'decimal',
1495
            'long varbit' => 'text',
1496
            'varbit' => 'string',
1497
            'date' => 'date',
1498
            'datetime' => 'datetime',
1499
            'smalldatetime' => 'datetime',
1500
            'time' => 'time',
1501
            'timestamp' => 'datetime',
1502
            'binary' => 'binary',
1503
            'image' => 'blob',
1504
            'long binary' => 'blob',
1505
            'uniqueidentifier' => 'guid',
1506
            'varbinary' => 'binary',
1507
        ];
1508 11377
    }
1509
}
1510