SQLServerPlatform.php 24.6 KB
Newer Older
1
<?php
2

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */
20

21
namespace Doctrine\DBAL\Platforms;
22

23 24
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\DBALException;
25
use Doctrine\DBAL\Schema\Index,
26
    Doctrine\DBAL\Schema\Table;
27 28

/**
29 30
 * The SQLServerPlatform provides the behavior, features and SQL dialect of the
 * Microsoft SQL Server database platform.
31 32 33 34
 *
 * @since 2.0
 * @author Roman Borschel <roman@code-factory.org>
 * @author Jonathan H. Wage <jonwage@gmail.com>
35
 * @author Benjamin Eberlei <kontakt@beberlei.de>
36
 */
37
class SQLServerPlatform extends AbstractPlatform
38
{
39 40 41
    /**
     * {@inheritDoc}
     */
42
    public function getDateDiffExpression($date1, $date2)
43 44 45
    {
        return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')';
    }
46

47 48 49 50
    public function getDateAddDaysExpression($date, $days)
    {
        return 'DATEADD(day, ' . $days . ', ' . $date . ')';
    }
51

52 53 54 55
    public function getDateSubDaysExpression($date, $days)
    {
        return 'DATEADD(day, -1 * ' . $days . ', ' . $date . ')';
    }
56

57 58 59 60
    public function getDateAddMonthExpression($date, $months)
    {
        return 'DATEADD(month, ' . $months . ', ' . $date . ')';
    }
61

62 63 64 65
    public function getDateSubMonthExpression($date, $months)
    {
        return 'DATEADD(month, -1 * ' . $months . ', ' . $date . ')';
    }
66

67
    /**
68 69 70
     * Whether the platform prefers identity columns for ID generation.
     * MsSql prefers "autoincrement" identity columns since sequences can only
     * be emulated with a table.
71
     *
72
     * @return boolean
73 74
     * @override
     */
75
    public function prefersIdentityColumns()
76
    {
77 78
        return true;
    }
79

80 81 82 83 84 85 86 87 88 89 90 91 92
    /**
     * Whether the platform supports identity columns.
     * MsSql supports this through AUTO_INCREMENT columns.
     *
     * @return boolean
     * @override
     */
    public function supportsIdentityColumns()
    {
        return true;
    }

    /**
93
     * Whether the platform supports releasing savepoints.
94 95 96
     *
     * @return boolean
     */
97
    public function supportsReleaseSavepoints()
98 99 100
    {
        return false;
    }
101

102 103 104 105 106 107 108 109 110 111 112
    /**
     * create a new database
     *
     * @param string $name name of the database that should be created
     * @return string
     * @override
     */
    public function getCreateDatabaseSQL($name)
    {
        return 'CREATE DATABASE ' . $name;
    }
113

114 115 116 117 118 119 120 121 122
    /**
     * drop an existing database
     *
     * @param string $name name of the database that should be dropped
     * @return string
     * @override
     */
    public function getDropDatabaseSQL($name)
    {
123 124 125 126 127 128
        return 'DROP DATABASE ' . $name;
    }

    /**
     * @override
     */
129
    public function supportsCreateDropDatabase()
130 131
    {
        return false;
132 133 134 135 136 137 138 139
    }

    /**
     * @override
     */
    public function getDropForeignKeySQL($foreignKey, $table)
    {
        if ($foreignKey instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) {
140
            $foreignKey = $foreignKey->getQuotedName($this);
141 142
        }

143
        if ($table instanceof \Doctrine\DBAL\Schema\Table) {
144
            $table = $table->getQuotedName($this);
145 146 147
        }

        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
148
    }
149 150

    /**
151 152
     * @override
     */
153
    public function getDropIndexSQL($index, $table=null)
154
    {
155
        if ($index instanceof \Doctrine\DBAL\Schema\Index) {
156
            $index = $index->getQuotedName($this);
157
        } else if (!is_string($index)) {
158 159 160
            throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
        }

161 162 163 164
        if (!isset($table)) {
            return 'DROP INDEX ' . $index;
        } else {
            if ($table instanceof \Doctrine\DBAL\Schema\Table) {
165
                $table = $table->getQuotedName($this);
166 167 168
            }

            return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index')
169
						ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . "
Benjamin Eberlei's avatar
Benjamin Eberlei committed
170
					ELSE
171
						DROP INDEX " . $index . " ON " . $table;
172
        }
173
    }
174 175 176 177 178

    /**
     * @override
     */
    protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
179
    {
180 181 182 183 184 185 186 187
        // @todo does other code breaks because of this?
        // foce primary keys to be not null
        foreach ($columns as &$column) {
            if (isset($column['primary']) && $column['primary']) {
                $column['notnull'] = true;
            }
        }

188
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
189 190

        if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) {
191 192 193 194
            foreach ($options['uniqueConstraints'] as $name => $definition) {
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
            }
        }
195 196

        if (isset($options['primary']) && !empty($options['primary'])) {
197 198 199 200 201
            $flags = '';
            if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) {
                $flags = ' NONCLUSTERED';
            }
            $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')';
202 203 204 205 206
        }

        $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;

        $check = $this->getCheckDeclarationSQL($columns);
207
        if (!empty($check)) {
208 209 210 211 212
            $query .= ', ' . $check;
        }
        $query .= ')';

        $sql[] = $query;
213 214

        if (isset($options['indexes']) && !empty($options['indexes'])) {
215
            foreach ($options['indexes'] as $index) {
216 217 218 219 220
                $sql[] = $this->getCreateIndexSQL($index, $tableName);
            }
        }

        if (isset($options['foreignKeys'])) {
221
            foreach ((array) $options['foreignKeys'] as $definition) {
222 223 224 225 226 227
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
            }
        }

        return $sql;
    }
228

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    /**
     * Get SQL to create an unnamed primary key constraint.
     *
     * @param Index $index
     * @param string|Table $table
     * @return string
     */
    public function getCreatePrimaryKeySQL(Index $index, $table)
    {
        $flags = '';
        if ($index->hasFlag('nonclustered')) {
            $flags = ' NONCLUSTERED';
        }
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')';
    }

245
    /**
246 247
     * @override
     */
248
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
249 250
    {
        $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index);
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

        $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index);

        return $constraint;
    }

    /**
     * @override
     */
    public function getCreateIndexSQL(Index $index, $table)
    {
        $constraint = parent::getCreateIndexSQL($index, $table);

        if ($index->isUnique()) {
            $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index);
        }

        return $constraint;
    }

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    /**
     * @override
     */
    protected function getCreateIndexSQLFlags(Index $index)
    {
        $type = '';
        if ($index->isUnique()) {
            $type .= 'UNIQUE ';
        }

        if ($index->hasFlag('clustered')) {
            $type .= 'CLUSTERED ';
        } else if ($index->hasFlag('nonclustered')) {
            $type .= 'NONCLUSTERED ';
        }

        return $type;
    }

290
    /**
291
     * Extend unique key constraint with required filters
292 293 294 295 296 297 298 299
     *
     * @param string $sql
     * @param Index $index
     * @return string
     */
    private function _appendUniqueConstraintDefinition($sql, Index $index)
    {
        $fields = array();
300 301 302 303
        foreach ($index->getColumns() as $field => $definition) {
            if (!is_array($definition)) {
                $field = $definition;
            }
304 305

            $fields[] = $field . ' IS NOT NULL';
306
        }
307 308 309

        return $sql . ' WHERE ' . implode(' AND ', $fields);
    }
310

311
    /**
312
     * @override
313
     */
314
    public function getAlterTableSQL(TableDiff $diff)
315
    {
316
        $queryParts = array();
317
        $sql = array();
318
        $columnSql = array();
319

320
        if ($diff->newName !== false) {
321
            $queryParts[] = 'RENAME TO ' . $diff->newName;
322 323
        }

324
        foreach ($diff->addedColumns as $column) {
325 326
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
                continue;
327 328
            }

329
            $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
330 331
        }

332
        foreach ($diff->removedColumns as $column) {
333 334
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
                continue;
335 336
            }

337
            $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this);
338 339
        }

340
        foreach ($diff->changedColumns as $columnDiff) {
341 342
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
                continue;
343 344
            }

345 346
            /* @var $columnDiff Doctrine\DBAL\Schema\ColumnDiff */
            $column = $columnDiff->column;
347 348
            $queryParts[] = 'ALTER COLUMN ' .
                    $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
349 350
        }

351
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
352 353
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
                continue;
354 355
            }

356 357 358
            $sql[] = "sp_RENAME '". $diff->name. ".". $oldColumnName . "' , '".$column->getQuotedName($this)."', 'COLUMN'";
            $queryParts[] = 'ALTER COLUMN ' .
                    $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
359
        }
360

361 362 363
        $tableSql = array();

        if ($this->onSchemaAlterTable($diff, $tableSql)) {
364
            return array_merge($tableSql, $columnSql);
365
        }
366

367 368 369
        foreach ($queryParts as $query) {
            $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
        }
370

371
        $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff));
Juozas Kaziukenas's avatar
Juozas Kaziukenas committed
372

373
        return array_merge($sql, $tableSql, $columnSql);
374
    }
375

376 377 378
    /**
     * @override
     */
379
    public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
380
    {
381
        return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES';
382 383
    }

384 385 386
    /**
     * @override
     */
387
    public function getShowDatabasesSQL()
388
    {
389
        return 'SHOW DATABASES';
390 391 392 393 394
    }

    /**
     * @override
     */
395
    public function getListTablesSQL()
396
    {
397 398
        // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams
        return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' ORDER BY name";
399 400 401 402 403
    }

    /**
     * @override
     */
404
    public function getListTableColumnsSQL($table, $database = null)
405
    {
406
        return "exec sp_columns @table_name = '" . $table . "'";
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
    }

    /**
     * @override
     */
    public function getListTableForeignKeysSQL($table, $database = null)
    {
        return "SELECT f.name AS ForeignKey,
                SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName,
                OBJECT_NAME (f.parent_object_id) AS TableName,
                COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName,
                SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName,
                OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
                COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName,
                f.delete_referential_action_desc,
                f.update_referential_action_desc
                FROM sys.foreign_keys AS f
                INNER JOIN sys.foreign_key_columns AS fc
                INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id
                ON f.OBJECT_ID = fc.constraint_object_id
                WHERE OBJECT_NAME (f.parent_object_id) = '" . $table . "'";
    }

    /**
     * @override
     */
433
    public function getListTableIndexesSQL($table, $currentDatabase = null)
434 435
    {
        return "exec sp_helpindex '" . $table . "'";
436
    }
437 438

    /**
439 440
     * @override
     */
441
    public function getCreateViewSQL($name, $sql)
442 443 444
    {
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
    }
445 446

    /**
447 448
     * @override
     */
449
    public function getListViewsSQL($database)
450 451 452 453
    {
        return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name";
    }

454
    /**
455 456 457 458
     * @override
     */
    public function getDropViewSQL($name)
    {
459
        return 'DROP VIEW ' . $name;
460
    }
461 462

    /**
463
     * Returns the regular expression operator.
464
     *
465
     * @return string
466 467
     * @override
     */
468
    public function getRegexpExpression()
469
    {
470
        return 'RLIKE';
471 472 473 474 475 476 477 478 479 480
    }

    /**
     * Returns global unique identifier
     *
     * @return string to get global unique identifier
     * @override
     */
    public function getGuidExpression()
    {
481
        return 'UUID()';
482
    }
483 484 485 486

    /**
     * @override
     */
487
    public function getLocateExpression($str, $substr, $startPos = false)
488
    {
489 490 491
        if ($startPos == false) {
            return 'CHARINDEX(' . $substr . ', ' . $str . ')';
        } else {
492
            return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')';
493
        }
494
    }
495

496 497 498
    /**
     * @override
     */
499
    public function getModExpression($expression1, $expression2)
500
    {
501
        return $expression1 . ' % ' . $expression2;
502
    }
503

504 505 506
    /**
     * @override
     */
507
    public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false)
508
    {
509
        $trimFn = '';
510

511
        if ( ! $char) {
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
            if ($pos == self::TRIM_LEADING) {
                $trimFn = 'LTRIM';
            } else if ($pos == self::TRIM_TRAILING) {
                $trimFn = 'RTRIM';
            } else {
                return 'LTRIM(RTRIM(' . $str . '))';
            }

            return $trimFn . '(' . $str . ')';
        } else {
            /** Original query used to get those expressions
              declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x';
              declare @pat varchar(10) = '%[^' + @trim_char + ']%';
              select @c as string
              , @trim_char as trim_char
              , stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading
              , reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing
              , reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both;
             */
            $pattern = "'%[^' + $char + ']%'";

            if ($pos == self::TRIM_LEADING) {
                return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)';
            } else if ($pos == self::TRIM_TRAILING) {
                return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))';
            } else {
                return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))';
            }
        }
541
    }
542

543 544 545 546
    /**
     * @override
     */
    public function getConcatExpression()
547
    {
548 549
        $args = func_get_args();
        return '(' . implode(' + ', $args) . ')';
550
    }
551 552

    public function getListDatabasesSQL()
553 554 555
    {
        return 'SELECT * FROM SYS.DATABASES';
    }
556

557
    /**
558 559
     * @override
     */
560
    public function getSubstringExpression($value, $from, $len = null)
561
    {
562
        if (!is_null($len)) {
563 564 565
            return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $len . ')';
        }
        return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)';
566
    }
567

568 569 570
    /**
     * @override
     */
571
    public function getLengthExpression($column)
572
    {
573
        return 'LEN(' . $column . ')';
574 575
    }

576 577 578
    /**
     * @override
     */
579
    public function getSetTransactionIsolationSQL($level)
romanb's avatar
romanb committed
580
    {
581
        return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
romanb's avatar
romanb committed
582
    }
583

584 585
    /**
     * @override
586
     */
587
    public function getIntegerTypeDeclarationSQL(array $field)
588
    {
589
        return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
590 591
    }

592
    /**
593
     * @override
594
     */
595
    public function getBigIntTypeDeclarationSQL(array $field)
596
    {
597
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
598 599
    }

600 601
    /**
     * @override
602
     */
603
    public function getSmallIntTypeDeclarationSQL(array $field)
604
    {
605
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
606 607
    }

608 609 610 611 612 613 614 615 616 617 618
    /**
     * Decleration for a UNIQUEIDENTIFIER (GUID) field in SQL Server
     *
     * @param array $field
     * @return string
     */
    public function getGuidTypeDeclartionSQL(array $field)
    {
        return 'UNIQUEIDENTIFIER';
    }

619
    /** @override */
620
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
621
    {
622
        return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)');
623
    }
624

625
    /** @override */
626
    public function getClobTypeDeclarationSQL(array $field)
627 628 629
    {
        return 'TEXT';
    }
630

631 632
    /**
     * @override
633
     */
634
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
635 636
    {
        $autoinc = '';
637
        if (!empty($columnDef['autoincrement'])) {
638
            $autoinc = ' IDENTITY';
639 640 641 642 643
        }
        $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : '';

        return $unsigned . $autoinc;
    }
644

645 646 647
    /**
     * @override
     */
648
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
649
    {
650
        return 'DATETIME';
651 652
    }

653 654 655
    /**
     * @override
     */
656
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
657
    {
658
        return 'DATETIME';
659
    }
660 661 662 663

    /**
     * @override
     */
664
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
665
    {
666
        return 'DATETIME';
667
    }
668

669 670 671
    /**
     * @override
     */
672
    public function getBooleanTypeDeclarationSQL(array $field)
673 674 675 676
    {
        return 'BIT';
    }

677 678 679 680
    /**
     * Adds an adapter-specific LIMIT clause to the SELECT statement.
     *
     * @param string $query
681 682
     * @param integer $limit
     * @param integer $offset
683 684 685
     * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html
     * @return string
     */
686
    protected function doModifyLimitQuery($query, $limit, $offset = null)
687 688
    {
        if ($limit > 0) {
689
            if ($offset == 0) {
690
                $query = preg_replace('/^(SELECT\s(DISTINCT\s)?)/i', '\1TOP ' . $limit . ' ', $query);
691 692
            } else {
                $orderby = stristr($query, 'ORDER BY');
693

694
                if ( ! $orderby) {
695 696 697
                    $over = 'ORDER BY (SELECT 0)';
                } else {
                    $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby);
698 699
                }

700 701
                // Remove ORDER BY clause from $query
                $query = preg_replace('/\s+ORDER BY(.*)/', '', $query);
AaronDDM's avatar
AaronDDM committed
702
                $query = preg_replace('/^SELECT\s/', '', $query);
703

704
                $start = $offset + 1;
705
                $end = $offset + $limit;
706

AaronDDM's avatar
AaronDDM committed
707
                $query = "SELECT * FROM (SELECT ROW_NUMBER() OVER ($over) AS \"doctrine_rownum\", $query) AS doctrine_tbl WHERE \"doctrine_rownum\" BETWEEN $start AND $end";
708 709 710 711 712
            }
        }

        return $query;
    }
713

714 715 716 717 718 719 720 721
    /**
     * @override
     */
    public function supportsLimitOffset()
    {
        return false;
    }

722
    /**
723
     * @override
724
     */
725
    public function convertBooleans($item)
726
    {
727 728 729
        if (is_array($item)) {
            foreach ($item as $key => $value) {
                if (is_bool($value) || is_numeric($item)) {
730
                    $item[$key] = ($value) ? 1 : 0;
731 732 733
                }
            }
        } else {
734
            if (is_bool($item) || is_numeric($item)) {
735
                $item = ($item) ? 1 : 0;
736
            }
737 738
        }
        return $item;
739
    }
740 741

    /**
742
     * @override
743
     */
744
    public function getCreateTemporaryTableSnippetSQL()
745
    {
746
        return "CREATE TABLE";
747
    }
748

749 750 751 752 753 754 755 756
    /**
     * @override
     */
    public function getTemporaryTableName($tableName)
    {
        return '#' . $tableName;
    }

757
    /**
758 759 760 761
     * @override
     */
    public function getDateTimeFormatString()
    {
762 763
        return 'Y-m-d H:i:s.000';
    }
764

765 766
    /**
     * @override
767
     */
768 769 770 771 772 773 774
    public function getDateFormatString()
    {
        return 'Y-m-d H:i:s.000';
    }

    /**
     * @override
775
     */
776 777 778
    public function getTimeFormatString()
    {
        return 'Y-m-d H:i:s.000';
779
    }
780

781 782 783 784 785 786 787
    /**
     * @override
     */
    public function getDateTimeTzFormatString()
    {
        return $this->getDateTimeFormatString();
    }
788

789
    /**
790 791
     * Get the platform name for this instance
     *
792 793
     * @return string
     */
794
    public function getName()
795
    {
796
        return 'mssql';
797
    }
798

Juozas Kaziukenas's avatar
Juozas Kaziukenas committed
799 800 801
    /**
     * @override
     */
802 803
    protected function initializeDoctrineTypeMappings()
    {
804
        $this->doctrineTypeMapping = array(
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
            'bigint' => 'bigint',
            'numeric' => 'decimal',
            'bit' => 'boolean',
            'smallint' => 'smallint',
            'decimal' => 'decimal',
            'smallmoney' => 'integer',
            'int' => 'integer',
            'tinyint' => 'smallint',
            'money' => 'integer',
            'float' => 'float',
            'real' => 'float',
            'double' => 'float',
            'double precision' => 'float',
            'datetimeoffset' => 'datetimetz',
            'smalldatetime' => 'datetime',
            'datetime' => 'datetime',
            'char' => 'string',
            'varchar' => 'string',
            'text' => 'text',
            'nchar' => 'string',
            'nvarchar' => 'string',
            'ntext' => 'text',
            'binary' => 'text',
828
            'varbinary' => 'blob',
829
            'image' => 'text',
830
            'uniqueidentifier' => 'guid',
831
        );
832
    }
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864

    /**
     * Generate SQL to create a new savepoint
     *
     * @param string $savepoint
     * @return string
     */
    public function createSavePoint($savepoint)
    {
        return 'SAVE TRANSACTION ' . $savepoint;
    }

    /**
     * Generate SQL to release a savepoint
     *
     * @param string $savepoint
     * @return string
     */
    public function releaseSavePoint($savepoint)
    {
        return '';
    }

    /**
     * Generate SQL to rollback a savepoint
     *
     * @param string $savepoint
     * @return string
     */
    public function rollbackSavePoint($savepoint)
    {
        return 'ROLLBACK TRANSACTION ' . $savepoint;
865
    }
866 867

    /**
Juozas Kaziukenas's avatar
Juozas Kaziukenas committed
868 869
     * @override
     */
870
    public function appendLockHint($fromClause, $lockMode)
Juozas Kaziukenas's avatar
Juozas Kaziukenas committed
871
    {
872 873
        // @todo coorect
        if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ) {
Juozas Kaziukenas's avatar
Juozas Kaziukenas committed
874 875 876
            return $fromClause . ' WITH (tablockx)';
        } else if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) {
            return $fromClause . ' WITH (tablockx)';
877 878
        } else {
            return $fromClause;
Juozas Kaziukenas's avatar
Juozas Kaziukenas committed
879 880 881 882 883 884 885 886 887 888
        }
    }

    /**
     * @override
     */
    public function getForUpdateSQL()
    {
        return ' ';
    }
889

890 891 892 893
    protected function getReservedKeywordsClass()
    {
        return 'Doctrine\DBAL\Platforms\Keywords\MsSQLKeywords';
    }
894 895

    /**
896
     * {@inheritDoc}
897
     */
898
    public function quoteSingleIdentifier($str)
899
    {
900
        return "[" . str_replace("]", "][", $str) . "]";
901
    }
902

903 904 905 906
    public function getTruncateTableSQL($tableName, $cascade = false)
    {
        return 'TRUNCATE TABLE '.$tableName;
    }
907 908 909 910 911 912 913 914

    /**
     * Gets the SQL Snippet used to declare a BLOB column type.
     */
    public function getBlobTypeDeclarationSQL(array $field)
    {
        return 'VARBINARY(MAX)';
    }
915
}