SQLAnywhere16Platform::getDateTimeFormatString()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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