AbstractPlatform.php 100 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php
/*
 * 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
Benjamin Eberlei's avatar
Benjamin Eberlei committed
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18 19
 */

20 21
namespace Doctrine\DBAL\Platforms;

Benjamin Morel's avatar
Benjamin Morel committed
22 23
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Connection;
24
use Doctrine\DBAL\Schema\Identifier;
Benjamin Morel's avatar
Benjamin Morel committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
use Doctrine\DBAL\Types;
use Doctrine\DBAL\Schema\Constraint;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Events;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs;
use Doctrine\DBAL\Event\SchemaDropTableEventArgs;
use Doctrine\DBAL\Event\SchemaAlterTableEventArgs;
use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs;
use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs;
use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs;
use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs;
45 46 47 48 49 50

/**
 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
 * point of abstraction of platform-specific behaviors, features and SQL dialects.
 * They are a passive source of information.
 *
Benjamin Morel's avatar
Benjamin Morel committed
51 52 53 54 55 56 57 58
 * @link   www.doctrine-project.org
 * @since  2.0
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
 * @author Benjamin Eberlei <kontakt@beberlei.de>
 * @todo   Remove any unnecessary methods.
59
 */
60
abstract class AbstractPlatform
61
{
62
    /**
63
     * @var integer
64 65 66 67
     */
    const CREATE_INDEXES = 1;

    /**
68
     * @var integer
69 70 71
     */
    const CREATE_FOREIGNKEYS = 2;

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_SECOND = 'SECOND';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_MINUTE = 'MINUTE';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_HOUR = 'HOUR';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_DAY = 'DAY';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_WEEK = 'WEEK';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_MONTH = 'MONTH';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_QUARTER = 'QUARTER';

    /**
     * @var string
     */
    const DATE_INTERVAL_UNIT_YEAR = 'YEAR';

112
    /**
113
     * @var integer
114 115 116 117
     */
    const TRIM_UNSPECIFIED = 0;

    /**
118
     * @var integer
119 120 121 122
     */
    const TRIM_LEADING = 1;

    /**
123
     * @var integer
124 125 126 127
     */
    const TRIM_TRAILING = 2;

    /**
128
     * @var integer
129 130 131
     */
    const TRIM_BOTH = 3;

132
    /**
Benjamin Morel's avatar
Benjamin Morel committed
133
     * @var array|null
134 135 136
     */
    protected $doctrineTypeMapping = null;

137 138 139 140
    /**
     * Contains a list of all columns that should generate parseable column comments for type-detection
     * in reverse engineering scenarios.
     *
Benjamin Morel's avatar
Benjamin Morel committed
141
     * @var array|null
142 143 144
     */
    protected $doctrineTypeComments = null;

145
    /**
Konstantin Kuklin's avatar
Konstantin Kuklin committed
146
     * @var \Doctrine\Common\EventManager
147 148 149
     */
    protected $_eventManager;

150 151 152
    /**
     * Holds the KeywordList instance for the current platform.
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
153
     * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList
154 155 156
     */
    protected $_keywords;

157 158 159
    /**
     * Constructor.
     */
Benjamin Morel's avatar
Benjamin Morel committed
160 161 162
    public function __construct()
    {
    }
163

164 165 166
    /**
     * Sets the EventManager used by the Platform.
     *
167
     * @param \Doctrine\Common\EventManager $eventManager
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
     */
    public function setEventManager(EventManager $eventManager)
    {
        $this->_eventManager = $eventManager;
    }

    /**
     * Gets the EventManager used by the Platform.
     *
     * @return \Doctrine\Common\EventManager
     */
    public function getEventManager()
    {
        return $this->_eventManager;
    }

184
    /**
Benjamin Morel's avatar
Benjamin Morel committed
185
     * Returns the SQL snippet that declares a boolean column.
186 187
     *
     * @param array $columnDef
188
     *
189 190 191 192 193
     * @return string
     */
    abstract public function getBooleanTypeDeclarationSQL(array $columnDef);

    /**
Benjamin Morel's avatar
Benjamin Morel committed
194
     * Returns the SQL snippet that declares a 4 byte integer column.
195 196
     *
     * @param array $columnDef
197
     *
198 199 200 201 202
     * @return string
     */
    abstract public function getIntegerTypeDeclarationSQL(array $columnDef);

    /**
Benjamin Morel's avatar
Benjamin Morel committed
203
     * Returns the SQL snippet that declares an 8 byte integer column.
204 205
     *
     * @param array $columnDef
206
     *
207 208 209 210 211
     * @return string
     */
    abstract public function getBigIntTypeDeclarationSQL(array $columnDef);

    /**
Benjamin Morel's avatar
Benjamin Morel committed
212
     * Returns the SQL snippet that declares a 2 byte integer column.
213 214
     *
     * @param array $columnDef
215
     *
216 217 218 219 220
     * @return string
     */
    abstract public function getSmallIntTypeDeclarationSQL(array $columnDef);

    /**
Benjamin Morel's avatar
Benjamin Morel committed
221
     * Returns the SQL snippet that declares common properties of an integer column.
222 223
     *
     * @param array $columnDef
Benjamin Morel's avatar
Benjamin Morel committed
224
     *
225 226 227 228 229
     * @return string
     */
    abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef);

    /**
Benjamin Morel's avatar
Benjamin Morel committed
230
     * Lazy load Doctrine Type Mappings.
231 232 233 234 235
     *
     * @return void
     */
    abstract protected function initializeDoctrineTypeMappings();

236
    /**
Benjamin Morel's avatar
Benjamin Morel committed
237
     * Initializes Doctrine Type Mappings with the platform defaults
238
     * and with all additional type mappings.
Benjamin Morel's avatar
Benjamin Morel committed
239 240
     *
     * @return void
241 242 243 244 245 246 247 248 249 250 251 252
     */
    private function initializeAllDoctrineTypeMappings()
    {
        $this->initializeDoctrineTypeMappings();

        foreach (Type::getTypesMap() as $typeName => $className) {
            foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
                $this->doctrineTypeMapping[$dbType] = $typeName;
            }
        }
    }

253
    /**
Benjamin Morel's avatar
Benjamin Morel committed
254
     * Returns the SQL snippet used to declare a VARCHAR column type.
255 256
     *
     * @param array $field
257
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
258
     * @return string
259
     */
260 261 262 263 264 265 266 267 268 269 270
    public function getVarcharTypeDeclarationSQL(array $field)
    {
        if ( !isset($field['length'])) {
            $field['length'] = $this->getVarcharDefaultLength();
        }

        $fixed = (isset($field['fixed'])) ? $field['fixed'] : false;

        if ($field['length'] > $this->getVarcharMaxLength()) {
            return $this->getClobTypeDeclarationSQL($field);
        }
271 272

        return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed);
273 274
    }

Steve Müller's avatar
Steve Müller committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    /**
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
     *
     * @param array $field The column definition.
     *
     * @return string
     */
    public function getBinaryTypeDeclarationSQL(array $field)
    {
        if ( ! isset($field['length'])) {
            $field['length'] = $this->getBinaryDefaultLength();
        }

        $fixed = isset($field['fixed']) ? $field['fixed'] : false;

        if ($field['length'] > $this->getBinaryMaxLength()) {
            return $this->getBlobTypeDeclarationSQL($field);
        }

        return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed);
    }

297
    /**
Benjamin Morel's avatar
Benjamin Morel committed
298
     * Returns the SQL snippet to declare a GUID/UUID field.
299
     *
300
     * By default this maps directly to a CHAR(36) and only maps to more
301 302 303
     * special datatypes when the underlying databases support this datatype.
     *
     * @param array $field
304
     *
305 306
     * @return string
     */
307
    public function getGuidTypeDeclarationSQL(array $field)
308
    {
309 310 311
        $field['length'] = 36;
        $field['fixed']  = true;

312 313 314
        return $this->getVarcharTypeDeclarationSQL($field);
    }

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
    /**
     * Returns the SQL snippet to declare a JSON field.
     *
     * By default this maps directly to a CLOB and only maps to more
     * special datatypes when the underlying databases support this datatype.
     *
     * @param array $field
     *
     * @return string
     */
    public function getJsonTypeDeclarationSQL(array $field)
    {
        return $this->getClobTypeDeclarationSQL($field);
    }

Christophe Coevoet's avatar
Christophe Coevoet committed
330
    /**
331
     * @param integer $length
Christophe Coevoet's avatar
Christophe Coevoet committed
332
     * @param boolean $fixed
333
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
334
     * @return string
335
     *
Benjamin Morel's avatar
Benjamin Morel committed
336
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
Christophe Coevoet's avatar
Christophe Coevoet committed
337
     */
338 339 340 341
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
    {
        throw DBALException::notSupported('VARCHARs not supported by Platform.');
    }
342

Steve Müller's avatar
Steve Müller committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
    /**
     * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
     *
     * @param integer $length The length of the column.
     * @param boolean $fixed  Whether the column length is fixed.
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
    {
        throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
    }

358
    /**
Benjamin Morel's avatar
Benjamin Morel committed
359
     * Returns the SQL snippet used to declare a CLOB column type.
360 361
     *
     * @param array $field
362 363
     *
     * @return string
364 365 366
     */
    abstract public function getClobTypeDeclarationSQL(array $field);

367
    /**
Benjamin Morel's avatar
Benjamin Morel committed
368
     * Returns the SQL Snippet used to declare a BLOB column type.
369 370
     *
     * @param array $field
371 372
     *
     * @return string
373 374 375
     */
    abstract public function getBlobTypeDeclarationSQL(array $field);

376 377 378 379 380 381 382
    /**
     * Gets the name of the platform.
     *
     * @return string
     */
    abstract public function getName();

383
    /**
Benjamin Morel's avatar
Benjamin Morel committed
384
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
385 386 387
     *
     * @param string $dbType
     * @param string $doctrineType
388
     *
Benjamin Morel's avatar
Benjamin Morel committed
389
     * @throws \Doctrine\DBAL\DBALException If the type is not found.
390 391 392 393
     */
    public function registerDoctrineTypeMapping($dbType, $doctrineType)
    {
        if ($this->doctrineTypeMapping === null) {
394
            $this->initializeAllDoctrineTypeMappings();
395 396 397 398 399 400 401 402 403 404 405
        }

        if (!Types\Type::hasType($doctrineType)) {
            throw DBALException::typeNotFound($doctrineType);
        }

        $dbType = strtolower($dbType);
        $this->doctrineTypeMapping[$dbType] = $doctrineType;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
406
     * Gets the Doctrine type that is mapped for the given database column type.
407
     *
Benjamin Morel's avatar
Benjamin Morel committed
408
     * @param string $dbType
409
     *
410
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
411 412
     *
     * @throws \Doctrine\DBAL\DBALException
413 414 415 416
     */
    public function getDoctrineTypeMapping($dbType)
    {
        if ($this->doctrineTypeMapping === null) {
417
            $this->initializeAllDoctrineTypeMappings();
418
        }
419

420
        $dbType = strtolower($dbType);
421

422
        if (!isset($this->doctrineTypeMapping[$dbType])) {
423 424
            throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it.");
        }
425 426

        return $this->doctrineTypeMapping[$dbType];
427 428
    }

429
    /**
Benjamin Morel's avatar
Benjamin Morel committed
430
     * Checks if a database type is currently supported by this platform.
431 432
     *
     * @param string $dbType
433 434
     *
     * @return boolean
435 436 437 438
     */
    public function hasDoctrineTypeMappingFor($dbType)
    {
        if ($this->doctrineTypeMapping === null) {
439
            $this->initializeAllDoctrineTypeMappings();
440 441 442
        }

        $dbType = strtolower($dbType);
Benjamin Morel's avatar
Benjamin Morel committed
443

444 445 446
        return isset($this->doctrineTypeMapping[$dbType]);
    }

447
    /**
Benjamin Morel's avatar
Benjamin Morel committed
448
     * Initializes the Doctrine Type comments instance variable for in_array() checks.
449 450 451 452 453
     *
     * @return void
     */
    protected function initializeCommentedDoctrineTypes()
    {
454 455 456 457
        $this->doctrineTypeComments = array();

        foreach (Type::getTypesMap() as $typeName => $className) {
            $type = Type::getType($typeName);
Benjamin Eberlei's avatar
Benjamin Eberlei committed
458

459 460 461 462
            if ($type->requiresSQLCommentHint($this)) {
                $this->doctrineTypeComments[] = $typeName;
            }
        }
463 464 465 466 467
    }

    /**
     * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
     *
Benjamin Morel's avatar
Benjamin Morel committed
468
     * @param \Doctrine\DBAL\Types\Type $doctrineType
469
     *
470
     * @return boolean
471 472 473 474 475 476 477 478 479 480 481
     */
    public function isCommentedDoctrineType(Type $doctrineType)
    {
        if ($this->doctrineTypeComments === null) {
            $this->initializeCommentedDoctrineTypes();
        }

        return in_array($doctrineType->getName(), $this->doctrineTypeComments);
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
482
     * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
483
     *
Benjamin Morel's avatar
Benjamin Morel committed
484
     * @param string|\Doctrine\DBAL\Types\Type $doctrineType
485
     *
486 487
     * @return void
     */
488
    public function markDoctrineTypeCommented($doctrineType)
489 490 491 492
    {
        if ($this->doctrineTypeComments === null) {
            $this->initializeCommentedDoctrineTypes();
        }
493

494
        $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType;
495 496 497
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
498 499 500
     * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
     *
     * @param \Doctrine\DBAL\Types\Type $doctrineType
501
     *
502 503 504 505 506 507 508
     * @return string
     */
    public function getDoctrineTypeComment(Type $doctrineType)
    {
        return '(DC2Type:' . $doctrineType->getName() . ')';
    }

509
    /**
Benjamin Morel's avatar
Benjamin Morel committed
510 511 512
     * Gets the comment of a passed column modified by potential doctrine type comment hints.
     *
     * @param \Doctrine\DBAL\Schema\Column $column
513
     *
514 515 516 517 518
     * @return string
     */
    protected function getColumnComment(Column $column)
    {
        $comment = $column->getComment();
519

520 521 522
        if ($this->isCommentedDoctrineType($column->getType())) {
            $comment .= $this->getDoctrineTypeComment($column->getType());
        }
523

524 525 526
        return $comment;
    }

527 528 529 530 531 532 533 534 535
    /**
     * Gets the character used for identifier quoting.
     *
     * @return string
     */
    public function getIdentifierQuoteCharacter()
    {
        return '"';
    }
536

537 538 539 540 541 542 543 544 545
    /**
     * Gets the string portion that starts an SQL comment.
     *
     * @return string
     */
    public function getSqlCommentStartString()
    {
        return "--";
    }
546

547
    /**
548
     * Gets the string portion that ends an SQL comment.
549 550 551 552 553 554 555
     *
     * @return string
     */
    public function getSqlCommentEndString()
    {
        return "\n";
    }
556

557 558 559 560 561 562
    /**
     * Gets the maximum length of a varchar field.
     *
     * @return integer
     */
    public function getVarcharMaxLength()
563 564 565 566 567 568 569 570 571 572
    {
        return 4000;
    }

    /**
     * Gets the default length of a varchar field.
     *
     * @return integer
     */
    public function getVarcharDefaultLength()
573 574 575
    {
        return 255;
    }
576

Steve Müller's avatar
Steve Müller committed
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
    /**
     * Gets the maximum length of a binary field.
     *
     * @return integer
     */
    public function getBinaryMaxLength()
    {
        return 4000;
    }

    /**
     * Gets the default length of a binary field.
     *
     * @return integer
     */
    public function getBinaryDefaultLength()
    {
        return 255;
    }

597 598 599 600 601 602 603 604 605
    /**
     * Gets all SQL wildcard characters of the platform.
     *
     * @return array
     */
    public function getWildcards()
    {
        return array('%', '_');
    }
606

607 608 609 610
    /**
     * Returns the regular expression operator.
     *
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
611 612
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
613 614 615
     */
    public function getRegexpExpression()
    {
616
        throw DBALException::notSupported(__METHOD__);
617
    }
618

619
    /**
Benjamin Morel's avatar
Benjamin Morel committed
620 621 622
     * Returns the global unique identifier expression.
     *
     * @return string
623
     *
Benjamin Morel's avatar
Benjamin Morel committed
624
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
625 626 627 628
     */
    public function getGuidExpression()
    {
        throw DBALException::notSupported(__METHOD__);
629 630 631
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
632
     * Returns the SQL snippet to get the average value of a column.
633
     *
Benjamin Morel's avatar
Benjamin Morel committed
634
     * @param string $column The column to use.
635
     *
Benjamin Morel's avatar
Benjamin Morel committed
636
     * @return string Generated SQL including an AVG aggregate function.
637 638 639
     */
    public function getAvgExpression($column)
    {
Benjamin Morel's avatar
Benjamin Morel committed
640
        return 'AVG(' . $column . ')';
641 642 643
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
644
     * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
645
     *
Benjamin Morel's avatar
Benjamin Morel committed
646
     * If a '*' is used instead of a column the number of selected rows is returned.
647
     *
Benjamin Morel's avatar
Benjamin Morel committed
648
     * @param string|integer $column The column to use.
649
     *
Benjamin Morel's avatar
Benjamin Morel committed
650
     * @return string Generated SQL including a COUNT aggregate function.
651 652 653 654 655 656 657
     */
    public function getCountExpression($column)
    {
        return 'COUNT(' . $column . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
658 659 660
     * Returns the SQL snippet to get the highest value of a column.
     *
     * @param string $column The column to use.
661
     *
Benjamin Morel's avatar
Benjamin Morel committed
662
     * @return string Generated SQL including a MAX aggregate function.
663 664 665 666 667 668 669
     */
    public function getMaxExpression($column)
    {
        return 'MAX(' . $column . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
670
     * Returns the SQL snippet to get the lowest value of a column.
671
     *
Benjamin Morel's avatar
Benjamin Morel committed
672 673 674
     * @param string $column The column to use.
     *
     * @return string Generated SQL including a MIN aggregate function.
675 676 677 678 679 680 681
     */
    public function getMinExpression($column)
    {
        return 'MIN(' . $column . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
682
     * Returns the SQL snippet to get the total sum of a column.
683
     *
Benjamin Morel's avatar
Benjamin Morel committed
684 685 686
     * @param string $column The column to use.
     *
     * @return string Generated SQL including a SUM aggregate function.
687 688 689 690 691 692 693 694 695
     */
    public function getSumExpression($column)
    {
        return 'SUM(' . $column . ')';
    }

    // scalar functions

    /**
Benjamin Morel's avatar
Benjamin Morel committed
696
     * Returns the SQL snippet to get the md5 sum of a field.
697
     *
Benjamin Morel's avatar
Benjamin Morel committed
698
     * Note: Not SQL92, but common functionality.
699
     *
700
     * @param string $column
Benjamin Morel's avatar
Benjamin Morel committed
701
     *
702 703 704 705 706 707 708 709
     * @return string
     */
    public function getMd5Expression($column)
    {
        return 'MD5(' . $column . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
710
     * Returns the SQL snippet to get the length of a text field.
711
     *
712
     * @param string $column
713
     *
714 715 716 717 718 719 720
     * @return string
     */
    public function getLengthExpression($column)
    {
        return 'LENGTH(' . $column . ')';
    }

721
    /**
Benjamin Morel's avatar
Benjamin Morel committed
722
     * Returns the SQL snippet to get the squared value of a column.
723
     *
Benjamin Morel's avatar
Benjamin Morel committed
724
     * @param string $column The column to use.
725
     *
Benjamin Morel's avatar
Benjamin Morel committed
726
     * @return string Generated SQL including an SQRT aggregate function.
727 728 729 730 731 732
     */
    public function getSqrtExpression($column)
    {
        return 'SQRT(' . $column . ')';
    }

733
    /**
Benjamin Morel's avatar
Benjamin Morel committed
734
     * Returns the SQL snippet to round a numeric field to the number of decimals specified.
735
     *
Benjamin Morel's avatar
Benjamin Morel committed
736
     * @param string  $column
737
     * @param integer $decimals
738
     *
739 740 741 742 743 744 745 746
     * @return string
     */
    public function getRoundExpression($column, $decimals = 0)
    {
        return 'ROUND(' . $column . ', ' . $decimals . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
747
     * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2.
748 749 750
     *
     * @param string $expression1
     * @param string $expression2
751
     *
752 753 754 755 756 757 758 759
     * @return string
     */
    public function getModExpression($expression1, $expression2)
    {
        return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
760
     * Returns the SQL snippet to trim a string.
761
     *
Benjamin Morel's avatar
Benjamin Morel committed
762 763 764
     * @param string         $str  The expression to apply the trim to.
     * @param integer        $pos  The position of the trim (leading/trailing/both).
     * @param string|boolean $char The char to trim, has to be quoted already. Defaults to space.
765
     *
766 767
     * @return string
     */
768
    public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false)
769
    {
Steve Müller's avatar
Steve Müller committed
770
        $expression = '';
771

772 773
        switch ($pos) {
            case self::TRIM_LEADING:
Steve Müller's avatar
Steve Müller committed
774
                $expression = 'LEADING ';
775 776 777
                break;

            case self::TRIM_TRAILING:
Steve Müller's avatar
Steve Müller committed
778
                $expression = 'TRAILING ';
779 780 781
                break;

            case self::TRIM_BOTH:
Steve Müller's avatar
Steve Müller committed
782
                $expression = 'BOTH ';
783
                break;
784 785
        }

Steve Müller's avatar
Steve Müller committed
786 787 788 789 790 791 792 793 794
        if (false !== $char) {
            $expression .= $char . ' ';
        }

        if ($pos || false !== $char) {
            $expression .= 'FROM ';
        }

        return 'TRIM(' . $expression . $str . ')';
795 796 797
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
798
     * Returns the SQL snippet to trim trailing space characters from the expression.
799
     *
Benjamin Morel's avatar
Benjamin Morel committed
800
     * @param string $str Literal string or column name.
801
     *
802 803 804 805 806 807 808 809
     * @return string
     */
    public function getRtrimExpression($str)
    {
        return 'RTRIM(' . $str . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
810
     * Returns the SQL snippet to trim leading space characters from the expression.
811
     *
Benjamin Morel's avatar
Benjamin Morel committed
812
     * @param string $str Literal string or column name.
813
     *
814 815 816 817 818 819 820 821
     * @return string
     */
    public function getLtrimExpression($str)
    {
        return 'LTRIM(' . $str . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
822 823
     * Returns the SQL snippet to change all characters from the expression to uppercase,
     * according to the current character set mapping.
824
     *
Benjamin Morel's avatar
Benjamin Morel committed
825
     * @param string $str Literal string or column name.
826
     *
827 828 829 830 831 832 833 834
     * @return string
     */
    public function getUpperExpression($str)
    {
        return 'UPPER(' . $str . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
835 836
     * Returns the SQL snippet to change all characters from the expression to lowercase,
     * according to the current character set mapping.
837
     *
Benjamin Morel's avatar
Benjamin Morel committed
838
     * @param string $str Literal string or column name.
839
     *
840 841 842 843 844 845 846 847
     * @return string
     */
    public function getLowerExpression($str)
    {
        return 'LOWER(' . $str . ')';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
848
     * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
849
     *
Benjamin Morel's avatar
Benjamin Morel committed
850 851 852
     * @param string          $str      Literal string.
     * @param string          $substr   Literal string to find.
     * @param integer|boolean $startPos Position to start at, beginning of string by default.
853
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
854
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
855 856
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
857
     */
858
    public function getLocateExpression($str, $substr, $startPos = false)
859
    {
860
        throw DBALException::notSupported(__METHOD__);
861 862 863
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
864
     * Returns the SQL snippet to get the current system date.
865 866 867 868 869 870 871 872 873
     *
     * @return string
     */
    public function getNowExpression()
    {
        return 'NOW()';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
874
     * Returns a SQL snippet to get a substring inside an SQL statement.
875 876 877
     *
     * Note: Not SQL92, but common functionality.
     *
Benjamin Morel's avatar
Benjamin Morel committed
878
     * SQLite only supports the 2 parameter variant of this function.
879
     *
Benjamin Morel's avatar
Benjamin Morel committed
880 881 882
     * @param string       $value  An sql string literal or column name/alias.
     * @param integer      $from   Where to start the substring portion.
     * @param integer|null $length The substring portion length.
883
     *
884
     * @return string
885
     */
886
    public function getSubstringExpression($value, $from, $length = null)
887
    {
888
        if ($length === null) {
889 890
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
        }
891 892

        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
893 894 895
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
896
     * Returns a SQL snippet to concatenate the given expressions.
897
     *
Benjamin Morel's avatar
Benjamin Morel committed
898
     * Accepts an arbitrary number of string parameters. Each parameter must contain an expression.
899
     *
900 901 902 903
     * @return string
     */
    public function getConcatExpression()
    {
904
        return join(' || ', func_get_args());
905 906 907 908 909 910 911 912 913 914 915 916 917
    }

    /**
     * Returns the SQL for a logical not.
     *
     * Example:
     * <code>
     * $q = new Doctrine_Query();
     * $e = $q->expr;
     * $q->select('*')->from('table')
     *   ->where($e->eq('id', $e->not('null'));
     * </code>
     *
918
     * @param string $expression
919
     *
Benjamin Morel's avatar
Benjamin Morel committed
920
     * @return string The logical expression.
921 922 923
     */
    public function getNotExpression($expression)
    {
924
        return 'NOT(' . $expression . ')';
925 926 927
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
928
     * Returns the SQL that checks if an expression is null.
929
     *
Benjamin Morel's avatar
Benjamin Morel committed
930
     * @param string $expression The expression that should be compared to null.
931
     *
Benjamin Morel's avatar
Benjamin Morel committed
932
     * @return string The logical expression.
933 934 935 936 937 938 939
     */
    public function getIsNullExpression($expression)
    {
        return $expression . ' IS NULL';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
940
     * Returns the SQL that checks if an expression is not null.
941
     *
Benjamin Morel's avatar
Benjamin Morel committed
942
     * @param string $expression The expression that should be compared to null.
943
     *
Benjamin Morel's avatar
Benjamin Morel committed
944
     * @return string The logical expression.
945 946 947 948 949 950 951
     */
    public function getIsNotNullExpression($expression)
    {
        return $expression . ' IS NOT NULL';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
952
     * Returns the SQL that checks if an expression evaluates to a value between two values.
953 954 955 956 957 958 959
     *
     * The parameter $expression is checked if it is between $value1 and $value2.
     *
     * Note: There is a slight difference in the way BETWEEN works on some databases.
     * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
     * independence you should avoid using between().
     *
Benjamin Morel's avatar
Benjamin Morel committed
960 961 962
     * @param string $expression The value to compare to.
     * @param string $value1     The lower value to compare with.
     * @param string $value2     The higher value to compare with.
963
     *
Benjamin Morel's avatar
Benjamin Morel committed
964
     * @return string The logical expression.
965 966 967 968 969 970
     */
    public function getBetweenExpression($expression, $value1, $value2)
    {
        return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2;
    }

Benjamin Morel's avatar
Benjamin Morel committed
971 972 973 974 975 976 977
    /**
     * Returns the SQL to get the arccosine of a value.
     *
     * @param string $value
     *
     * @return string
     */
978 979 980 981 982
    public function getAcosExpression($value)
    {
        return 'ACOS(' . $value . ')';
    }

Benjamin Morel's avatar
Benjamin Morel committed
983 984 985 986 987 988 989
    /**
     * Returns the SQL to get the sine of a value.
     *
     * @param string $value
     *
     * @return string
     */
990 991 992 993 994
    public function getSinExpression($value)
    {
        return 'SIN(' . $value . ')';
    }

Benjamin Morel's avatar
Benjamin Morel committed
995 996 997 998 999
    /**
     * Returns the SQL to get the PI value.
     *
     * @return string
     */
1000 1001 1002 1003 1004
    public function getPiExpression()
    {
        return 'PI()';
    }

Benjamin Morel's avatar
Benjamin Morel committed
1005 1006 1007 1008 1009 1010 1011
    /**
     * Returns the SQL to get the cosine of a value.
     *
     * @param string $value
     *
     * @return string
     */
1012 1013 1014 1015
    public function getCosExpression($value)
    {
        return 'COS(' . $value . ')';
    }
1016

1017
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1018
     * Returns the SQL to calculate the difference in days between the two passed dates.
1019
     *
Benjamin Morel's avatar
Benjamin Morel committed
1020
     * Computes diff = date1 - date2.
1021 1022 1023
     *
     * @param string $date1
     * @param string $date2
1024
     *
1025
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1026 1027
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1028 1029 1030 1031
     */
    public function getDateDiffExpression($date1, $date2)
    {
        throw DBALException::notSupported(__METHOD__);
1032 1033
    }

1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    /**
     * Returns the SQL to add the number of given seconds to a date.
     *
     * @param string  $date
     * @param integer $seconds
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateAddSecondsExpression($date, $seconds)
    {
        return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, self::DATE_INTERVAL_UNIT_SECOND);
    }

    /**
     * Returns the SQL to subtract the number of given seconds from a date.
     *
     * @param string  $date
     * @param integer $seconds
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateSubSecondsExpression($date, $seconds)
    {
        return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, self::DATE_INTERVAL_UNIT_SECOND);
    }

    /**
     * Returns the SQL to add the number of given minutes to a date.
     *
     * @param string  $date
     * @param integer $minutes
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateAddMinutesExpression($date, $minutes)
    {
        return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, self::DATE_INTERVAL_UNIT_MINUTE);
    }

    /**
     * Returns the SQL to subtract the number of given minutes from a date.
     *
     * @param string  $date
     * @param integer $minutes
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateSubMinutesExpression($date, $minutes)
    {
        return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, self::DATE_INTERVAL_UNIT_MINUTE);
    }

1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
    /**
     * Returns the SQL to add the number of given hours to a date.
     *
     * @param string  $date
     * @param integer $hours
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateAddHourExpression($date, $hours)
    {
1106
        return $this->getDateArithmeticIntervalExpression($date, '+', $hours, self::DATE_INTERVAL_UNIT_HOUR);
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
    }

    /**
     * Returns the SQL to subtract the number of given hours to a date.
     *
     * @param string  $date
     * @param integer $hours
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateSubHourExpression($date, $hours)
    {
1121
        return $this->getDateArithmeticIntervalExpression($date, '-', $hours, self::DATE_INTERVAL_UNIT_HOUR);
1122 1123 1124
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1125
     * Returns the SQL to add the number of given days to a date.
1126
     *
Benjamin Morel's avatar
Benjamin Morel committed
1127
     * @param string  $date
1128 1129
     * @param integer $days
     *
1130
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1131 1132
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1133 1134 1135
     */
    public function getDateAddDaysExpression($date, $days)
    {
1136
        return $this->getDateArithmeticIntervalExpression($date, '+', $days, self::DATE_INTERVAL_UNIT_DAY);
1137 1138 1139
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1140
     * Returns the SQL to subtract the number of given days to a date.
1141
     *
Benjamin Morel's avatar
Benjamin Morel committed
1142
     * @param string  $date
1143 1144
     * @param integer $days
     *
1145
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1146 1147
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1148 1149 1150
     */
    public function getDateSubDaysExpression($date, $days)
    {
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
        return $this->getDateArithmeticIntervalExpression($date, '-', $days, self::DATE_INTERVAL_UNIT_DAY);
    }

    /**
     * Returns the SQL to add the number of given weeks to a date.
     *
     * @param string  $date
     * @param integer $weeks
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateAddWeeksExpression($date, $weeks)
    {
        return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, self::DATE_INTERVAL_UNIT_WEEK);
    }

    /**
     * Returns the SQL to subtract the number of given weeks from a date.
     *
     * @param string  $date
     * @param integer $weeks
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateSubWeeksExpression($date, $weeks)
    {
        return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, self::DATE_INTERVAL_UNIT_WEEK);
1182 1183 1184
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1185
     * Returns the SQL to add the number of given months to a date.
1186
     *
Benjamin Morel's avatar
Benjamin Morel committed
1187
     * @param string  $date
1188 1189
     * @param integer $months
     *
1190
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1191 1192
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1193 1194 1195
     */
    public function getDateAddMonthExpression($date, $months)
    {
1196
        return $this->getDateArithmeticIntervalExpression($date, '+', $months, self::DATE_INTERVAL_UNIT_MONTH);
1197 1198 1199
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1200
     * Returns the SQL to subtract the number of given months to a date.
1201
     *
Benjamin Morel's avatar
Benjamin Morel committed
1202
     * @param string  $date
1203 1204
     * @param integer $months
     *
1205
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1206 1207
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1208 1209
     */
    public function getDateSubMonthExpression($date, $months)
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
    {
        return $this->getDateArithmeticIntervalExpression($date, '-', $months, self::DATE_INTERVAL_UNIT_MONTH);
    }

    /**
     * Returns the SQL to add the number of given quarters to a date.
     *
     * @param string  $date
     * @param integer $quarters
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateAddQuartersExpression($date, $quarters)
    {
        return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, self::DATE_INTERVAL_UNIT_QUARTER);
    }

    /**
     * Returns the SQL to subtract the number of given quarters from a date.
     *
     * @param string  $date
     * @param integer $quarters
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateSubQuartersExpression($date, $quarters)
    {
        return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, self::DATE_INTERVAL_UNIT_QUARTER);
    }

    /**
     * Returns the SQL to add the number of given years to a date.
     *
     * @param string  $date
     * @param integer $years
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateAddYearsExpression($date, $years)
    {
        return $this->getDateArithmeticIntervalExpression($date, '+', $years, self::DATE_INTERVAL_UNIT_YEAR);
    }

    /**
     * Returns the SQL to subtract the number of given years from a date.
     *
     * @param string  $date
     * @param integer $years
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getDateSubYearsExpression($date, $years)
    {
        return $this->getDateArithmeticIntervalExpression($date, '-', $years, self::DATE_INTERVAL_UNIT_YEAR);
    }

    /**
     * Returns the SQL for a date arithmetic expression.
     *
     * @param string  $date     The column or literal representing a date to perform the arithmetic operation on.
     * @param string  $operator The arithmetic operator (+ or -).
     * @param integer $interval The interval that shall be calculated into the date.
     * @param string  $unit     The unit of the interval that shall be calculated into the date.
     *                          One of the DATE_INTERVAL_UNIT_* constants.
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
1288 1289 1290 1291
    {
        throw DBALException::notSupported(__METHOD__);
    }

1292
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1293
     * Returns the SQL bit AND comparison expression.
1294
     *
Benjamin Morel's avatar
Benjamin Morel committed
1295 1296
     * @param string $value1
     * @param string $value2
1297
     *
Benjamin Morel's avatar
Benjamin Morel committed
1298
     * @return string
1299 1300 1301 1302 1303
     */
    public function getBitAndComparisonExpression($value1, $value2)
    {
        return '(' . $value1 . ' & ' . $value2 . ')';
    }
Fabio B. Silva's avatar
Fabio B. Silva committed
1304

1305
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1306
     * Returns the SQL bit OR comparison expression.
1307
     *
Benjamin Morel's avatar
Benjamin Morel committed
1308 1309
     * @param string $value1
     * @param string $value2
1310
     *
Benjamin Morel's avatar
Benjamin Morel committed
1311
     * @return string
1312 1313 1314 1315 1316 1317
     */
    public function getBitOrComparisonExpression($value1, $value2)
    {
        return '(' . $value1 . ' | ' . $value2 . ')';
    }

Benjamin Morel's avatar
Benjamin Morel committed
1318 1319
    /**
     * Returns the FOR UPDATE expression.
1320
     *
Benjamin Morel's avatar
Benjamin Morel committed
1321 1322
     * @return string
     */
1323
    public function getForUpdateSQL()
1324 1325 1326
    {
        return 'FOR UPDATE';
    }
1327

1328 1329 1330
    /**
     * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification.
     *
1331 1332 1333
     * @param string       $fromClause The FROM clause to append the hint for the given lock mode to.
     * @param integer|null $lockMode   One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
     *                                 be appended to the FROM clause.
1334
     *
1335 1336 1337 1338 1339 1340 1341 1342
     * @return string
     */
    public function appendLockHint($fromClause, $lockMode)
    {
        return $fromClause;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1343
     * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
1344
     *
1345
     * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
     * vendors allow to lighten this constraint up to be a real read lock.
     *
     * @return string
     */
    public function getReadLockSQL()
    {
        return $this->getForUpdateSQL();
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1356
     * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
1357
     *
1358
     * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
1359 1360 1361 1362 1363 1364 1365 1366
     *
     * @return string
     */
    public function getWriteLockSQL()
    {
        return $this->getForUpdateSQL();
    }

1367
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1368
     * Returns the SQL snippet to drop an existing database.
1369
     *
Benjamin Morel's avatar
Benjamin Morel committed
1370
     * @param string $database The name of the database that should be dropped.
1371 1372 1373
     *
     * @return string
     */
1374
    public function getDropDatabaseSQL($database)
1375 1376 1377
    {
        return 'DROP DATABASE ' . $database;
    }
1378

1379
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1380
     * Returns the SQL snippet to drop an existing table.
1381
     *
Benjamin Morel's avatar
Benjamin Morel committed
1382
     * @param \Doctrine\DBAL\Schema\Table|string $table
1383
     *
1384
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1385 1386
     *
     * @throws \InvalidArgumentException
1387
     */
1388
    public function getDropTableSQL($table)
1389
    {
1390 1391
        $tableArg = $table;

1392
        if ($table instanceof Table) {
1393
            $table = $table->getQuotedName($this);
Steve Müller's avatar
Steve Müller committed
1394
        } elseif (!is_string($table)) {
1395
            throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
1396 1397
        }

1398
        if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
1399
            $eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
1400 1401 1402 1403 1404 1405
            $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);

            if ($eventArgs->isDefaultPrevented()) {
                return $eventArgs->getSql();
            }
        }
1406

1407 1408
        return 'DROP TABLE ' . $table;
    }
1409

1410
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1411
     * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
1412
     *
Benjamin Morel's avatar
Benjamin Morel committed
1413
     * @param \Doctrine\DBAL\Schema\Table|string $table
1414
     *
1415 1416 1417 1418 1419 1420 1421
     * @return string
     */
    public function getDropTemporaryTableSQL($table)
    {
        return $this->getDropTableSQL($table);
    }

1422
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1423
     * Returns the SQL to drop an index from a table.
1424
     *
Benjamin Morel's avatar
Benjamin Morel committed
1425 1426
     * @param \Doctrine\DBAL\Schema\Index|string $index
     * @param \Doctrine\DBAL\Schema\Table|string $table
1427
     *
1428
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1429 1430
     *
     * @throws \InvalidArgumentException
1431
     */
1432
    public function getDropIndexSQL($index, $table = null)
1433
    {
1434
        if ($index instanceof Index) {
1435
            $index = $index->getQuotedName($this);
Steve Müller's avatar
Steve Müller committed
1436
        } elseif (!is_string($index)) {
1437
            throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
1438 1439 1440
        }

        return 'DROP INDEX ' . $index;
1441
    }
1442

1443
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1444
     * Returns the SQL to drop a constraint.
1445
     *
Benjamin Morel's avatar
Benjamin Morel committed
1446 1447
     * @param \Doctrine\DBAL\Schema\Constraint|string $constraint
     * @param \Doctrine\DBAL\Schema\Table|string      $table
1448
     *
1449 1450
     * @return string
     */
1451
    public function getDropConstraintSQL($constraint, $table)
1452
    {
1453
        if ($constraint instanceof Constraint) {
1454
            $constraint = $constraint->getQuotedName($this);
1455 1456
        }

1457
        if ($table instanceof Table) {
1458
            $table = $table->getQuotedName($this);
1459 1460 1461
        }

        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
1462
    }
1463

1464
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1465
     * Returns the SQL to drop a foreign key.
1466
     *
Benjamin Morel's avatar
Benjamin Morel committed
1467 1468
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey
     * @param \Doctrine\DBAL\Schema\Table|string                $table
1469
     *
1470 1471
     * @return string
     */
1472
    public function getDropForeignKeySQL($foreignKey, $table)
1473
    {
1474
        if ($foreignKey instanceof ForeignKeyConstraint) {
1475
            $foreignKey = $foreignKey->getQuotedName($this);
1476 1477
        }

1478
        if ($table instanceof Table) {
1479
            $table = $table->getQuotedName($this);
1480 1481 1482
        }

        return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
1483
    }
1484

1485
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1486
     * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
1487
     * on this platform.
1488
     *
Benjamin Morel's avatar
Benjamin Morel committed
1489 1490
     * @param \Doctrine\DBAL\Schema\Table   $table
     * @param integer                       $createFlags
1491
     *
1492
     * @return array The sequence of SQL statements.
Benjamin Morel's avatar
Benjamin Morel committed
1493 1494 1495
     *
     * @throws \Doctrine\DBAL\DBALException
     * @throws \InvalidArgumentException
1496
     */
1497
    public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
1498
    {
1499
        if ( ! is_int($createFlags)) {
1500
            throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.");
1501 1502
        }

1503
        if (count($table->getColumns()) === 0) {
1504 1505 1506
            throw DBALException::noColumnsSpecifiedForTable($table->getName());
        }

1507
        $tableName = $table->getQuotedName($this);
1508 1509 1510 1511 1512
        $options = $table->getOptions();
        $options['uniqueConstraints'] = array();
        $options['indexes'] = array();
        $options['primary'] = array();

1513
        if (($createFlags&self::CREATE_INDEXES) > 0) {
1514
            foreach ($table->getIndexes() as $index) {
1515 1516
                /* @var $index Index */
                if ($index->isPrimary()) {
Steve Müller's avatar
Steve Müller committed
1517
                    $options['primary']       = $index->getQuotedColumns($this);
1518
                    $options['primary_index'] = $index;
1519 1520 1521
                } else {
                    $options['indexes'][$index->getName()] = $index;
                }
1522 1523
            }
        }
1524

1525
        $columnSql = array();
1526
        $columns = array();
1527

1528
        foreach ($table->getColumns() as $column) {
1529
            /* @var \Doctrine\DBAL\Schema\Column $column */
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541

            if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
                $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
                $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);

                $columnSql = array_merge($columnSql, $eventArgs->getSql());

                if ($eventArgs->isDefaultPrevented()) {
                    continue;
                }
            }

1542
            $columnData = $column->toArray();
1543
            $columnData['name'] = $column->getQuotedName($this);
1544
            $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false;
1545
            $columnData['comment'] = $this->getColumnComment($column);
1546 1547

            if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) {
1548 1549
                $columnData['length'] = 255;
            }
1550 1551

            if (in_array($column->getName(), $options['primary'])) {
1552 1553 1554 1555 1556 1557
                $columnData['primary'] = true;
            }

            $columns[$columnData['name']] = $columnData;
        }

1558 1559
        if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) {
            $options['foreignKeys'] = array();
1560
            foreach ($table->getForeignKeys() as $fkConstraint) {
1561 1562 1563 1564
                $options['foreignKeys'][] = $fkConstraint;
            }
        }

1565 1566 1567
        if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
            $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
            $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1568

Jan Sorgalla's avatar
Jan Sorgalla committed
1569 1570 1571
            if ($eventArgs->isDefaultPrevented()) {
                return array_merge($eventArgs->getSql(), $columnSql);
            }
1572
        }
1573

1574 1575
        $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
        if ($this->supportsCommentOnStatement()) {
1576
            foreach ($table->getColumns() as $column) {
1577 1578 1579 1580
                $comment = $this->getColumnComment($column);

                if (null !== $comment && '' !== $comment) {
                    $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
1581 1582 1583
                }
            }
        }
1584

Jan Sorgalla's avatar
Jan Sorgalla committed
1585
        return array_merge($sql, $columnSql);
1586 1587
    }

Benjamin Morel's avatar
Benjamin Morel committed
1588 1589 1590 1591 1592 1593 1594
    /**
     * @param string $tableName
     * @param string $columnName
     * @param string $comment
     *
     * @return string
     */
1595 1596
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
    {
1597 1598
        $tableName = new Identifier($tableName);
        $columnName = new Identifier($columnName);
1599 1600
        $comment = $this->quoteStringLiteral($comment);

1601 1602
        return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) .
            " IS " . $comment;
1603 1604 1605
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1606
     * Returns the SQL used to create a table.
1607
     *
1608
     * @param string $tableName
Benjamin Morel's avatar
Benjamin Morel committed
1609 1610
     * @param array  $columns
     * @param array  $options
1611
     *
1612 1613
     * @return array
     */
1614
    protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
1615
    {
1616
        $columnListSql = $this->getColumnDeclarationListSQL($columns);
1617

1618
        if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1619
            foreach ($options['uniqueConstraints'] as $name => $definition) {
1620
                $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1621 1622
            }
        }
1623

1624
        if (isset($options['primary']) && ! empty($options['primary'])) {
1625
            $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1626 1627 1628
        }

        if (isset($options['indexes']) && ! empty($options['indexes'])) {
Steve Müller's avatar
Steve Müller committed
1629
            foreach ($options['indexes'] as $index => $definition) {
1630
                $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1631 1632 1633
            }
        }

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

1636
        $check = $this->getCheckDeclarationSQL($columns);
1637 1638
        if ( ! empty($check)) {
            $query .= ', ' . $check;
1639
        }
1640 1641 1642 1643 1644
        $query .= ')';

        $sql[] = $query;

        if (isset($options['foreignKeys'])) {
1645
            foreach ((array) $options['foreignKeys'] as $definition) {
1646
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1647 1648
            }
        }
1649

1650 1651
        return $sql;
    }
1652

Benjamin Morel's avatar
Benjamin Morel committed
1653 1654 1655
    /**
     * @return string
     */
1656
    public function getCreateTemporaryTableSnippetSQL()
1657 1658 1659
    {
        return "CREATE TEMPORARY TABLE";
    }
1660

1661
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1662
     * Returns the SQL to create a sequence on this platform.
1663
     *
1664
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
1665 1666 1667
     *
     * @return string
     *
Benjamin Morel's avatar
Benjamin Morel committed
1668
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1669
     */
1670
    public function getCreateSequenceSQL(Sequence $sequence)
1671
    {
1672
        throw DBALException::notSupported(__METHOD__);
1673
    }
1674

1675
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1676
     * Returns the SQL to change a sequence on this platform.
1677 1678
     *
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
1679
     *
1680
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1681 1682
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1683
     */
1684
    public function getAlterSequenceSQL(Sequence $sequence)
1685 1686 1687
    {
        throw DBALException::notSupported(__METHOD__);
    }
1688

1689
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1690
     * Returns the SQL to create a constraint on a table on this platform.
1691
     *
Benjamin Morel's avatar
Benjamin Morel committed
1692 1693
     * @param \Doctrine\DBAL\Schema\Constraint   $constraint
     * @param \Doctrine\DBAL\Schema\Table|string $table
1694
     *
1695
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1696 1697
     *
     * @throws \InvalidArgumentException
1698
     */
1699
    public function getCreateConstraintSQL(Constraint $constraint, $table)
1700
    {
1701
        if ($table instanceof Table) {
1702
            $table = $table->getQuotedName($this);
1703 1704
        }

1705
        $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1706

1707
        $columnList = '('. implode(', ', $constraint->getQuotedColumns($this)) . ')';
1708 1709

        $referencesClause = '';
1710
        if ($constraint instanceof Index) {
Steve Müller's avatar
Steve Müller committed
1711
            if ($constraint->isPrimary()) {
1712 1713 1714 1715 1716
                $query .= ' PRIMARY KEY';
            } elseif ($constraint->isUnique()) {
                $query .= ' UNIQUE';
            } else {
                throw new \InvalidArgumentException(
1717
                    'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1718 1719
                );
            }
Steve Müller's avatar
Steve Müller committed
1720
        } elseif ($constraint instanceof ForeignKeyConstraint) {
1721 1722
            $query .= ' FOREIGN KEY';

1723
            $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
1724
                ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
1725 1726
        }
        $query .= ' '.$columnList.$referencesClause;
1727 1728 1729

        return $query;
    }
1730

1731
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1732
     * Returns the SQL to create an index on a table on this platform.
1733
     *
Benjamin Morel's avatar
Benjamin Morel committed
1734 1735
     * @param \Doctrine\DBAL\Schema\Index        $index
     * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created.
1736
     *
1737
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
1738 1739
     *
     * @throws \InvalidArgumentException
1740
     */
1741
    public function getCreateIndexSQL(Index $index, $table)
1742
    {
1743
        if ($table instanceof Table) {
1744
            $table = $table->getQuotedName($this);
1745
        }
1746
        $name = $index->getQuotedName($this);
1747
        $columns = $index->getQuotedColumns($this);
1748 1749 1750

        if (count($columns) == 0) {
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
1751
        }
1752

1753 1754
        if ($index->isPrimary()) {
            return $this->getCreatePrimaryKeySQL($index, $table);
1755 1756
        }

1757
        $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1758
        $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')' . $this->getPartialIndexSQL($index);
1759

1760 1761
        return $query;
    }
1762

1763 1764 1765 1766 1767 1768 1769 1770 1771
    /**
     * Adds condition for partial index.
     *
     * @param \Doctrine\DBAL\Schema\Index $index
     *
     * @return string
     */
    protected function getPartialIndexSQL(Index $index)
    {
1772 1773 1774
        if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
            return  ' WHERE ' . $index->getOption('where');
        }
1775 1776

        return '';
1777 1778
    }

1779
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1780
     * Adds additional flags for index generation.
1781
     *
Benjamin Morel's avatar
Benjamin Morel committed
1782
     * @param \Doctrine\DBAL\Schema\Index $index
1783
     *
1784 1785 1786 1787
     * @return string
     */
    protected function getCreateIndexSQLFlags(Index $index)
    {
1788
        return $index->isUnique() ? 'UNIQUE ' : '';
1789 1790
    }

1791
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1792
     * Returns the SQL to create an unnamed primary key constraint.
1793
     *
Benjamin Morel's avatar
Benjamin Morel committed
1794 1795
     * @param \Doctrine\DBAL\Schema\Index        $index
     * @param \Doctrine\DBAL\Schema\Table|string $table
1796
     *
1797 1798 1799 1800
     * @return string
     */
    public function getCreatePrimaryKeySQL(Index $index, $table)
    {
1801
        return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')';
1802
    }
1803

1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816
    /**
     * Returns the SQL to create a named schema.
     *
     * @param string $schemaName
     *
     * @return string
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getCreateSchemaSQL($schemaName)
    {
        throw DBALException::notSupported(__METHOD__);
    }

1817
    /**
1818
     * Quotes a string so that it can be safely used as a table or column name,
1819
     * even if it is a reserved word of the platform. This also detects identifier
1820
     * chains separated by dot and quotes them independently.
1821
     *
1822
     * NOTE: Just because you CAN use quoted identifiers doesn't mean
Benjamin Morel's avatar
Benjamin Morel committed
1823
     * you SHOULD use them. In general, they end up causing way more
1824 1825
     * problems than they solve.
     *
Benjamin Morel's avatar
Benjamin Morel committed
1826
     * @param string $str The identifier name to be quoted.
1827
     *
Benjamin Morel's avatar
Benjamin Morel committed
1828
     * @return string The quoted identifier string.
1829 1830
     */
    public function quoteIdentifier($str)
1831 1832
    {
        if (strpos($str, ".") !== false) {
1833
            $parts = array_map(array($this, "quoteSingleIdentifier"), explode(".", $str));
1834

1835 1836 1837 1838 1839 1840 1841
            return implode(".", $parts);
        }

        return $this->quoteSingleIdentifier($str);
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1842
     * Quotes a single identifier (no dot chain separation).
1843
     *
Benjamin Morel's avatar
Benjamin Morel committed
1844
     * @param string $str The identifier name to be quoted.
1845
     *
Benjamin Morel's avatar
Benjamin Morel committed
1846
     * @return string The quoted identifier string.
1847 1848
     */
    public function quoteSingleIdentifier($str)
1849 1850 1851
    {
        $c = $this->getIdentifierQuoteCharacter();

1852
        return $c . str_replace($c, $c.$c, $str) . $c;
1853
    }
1854

1855
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1856
     * Returns the SQL to create a new foreign key.
1857
     *
Benjamin Morel's avatar
Benjamin Morel committed
1858 1859
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key constraint.
     * @param \Doctrine\DBAL\Schema\Table|string         $table      The name of the table on which the foreign key is to be created.
1860
     *
1861 1862
     * @return string
     */
1863
    public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1864
    {
1865
        if ($table instanceof Table) {
1866
            $table = $table->getQuotedName($this);
1867 1868
        }

1869
        $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1870 1871 1872

        return $query;
    }
1873

1874
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1875
     * Gets the SQL statements for altering an existing table.
1876
     *
Benjamin Morel's avatar
Benjamin Morel committed
1877
     * This method returns an array of SQL statements, since some platforms need several statements.
1878
     *
Benjamin Morel's avatar
Benjamin Morel committed
1879
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
1880
     *
1881
     * @return array
Benjamin Morel's avatar
Benjamin Morel committed
1882 1883
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
1884
     */
1885
    public function getAlterTableSQL(TableDiff $diff)
1886
    {
1887
        throw DBALException::notSupported(__METHOD__);
1888
    }
1889

1890
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1891 1892 1893
     * @param \Doctrine\DBAL\Schema\Column    $column
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
     * @param array                           $columnSql
1894
     *
1895
     * @return boolean
1896 1897 1898 1899 1900 1901 1902
     */
    protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
    {
        if (null === $this->_eventManager) {
            return false;
        }

1903
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915
            return false;
        }

        $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);

        $columnSql = array_merge($columnSql, $eventArgs->getSql());

        return $eventArgs->isDefaultPrevented();
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1916 1917 1918
     * @param \Doctrine\DBAL\Schema\Column    $column
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
     * @param array                           $columnSql
1919
     *
1920
     * @return boolean
1921 1922 1923 1924 1925 1926 1927
     */
    protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
    {
        if (null === $this->_eventManager) {
            return false;
        }

1928
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940
            return false;
        }

        $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);

        $columnSql = array_merge($columnSql, $eventArgs->getSql());

        return $eventArgs->isDefaultPrevented();
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1941 1942 1943
     * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff
     * @param \Doctrine\DBAL\Schema\TableDiff  $diff
     * @param array                            $columnSql
1944
     *
1945
     * @return boolean
1946 1947 1948 1949 1950 1951 1952
     */
    protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
    {
        if (null === $this->_eventManager) {
            return false;
        }

1953
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965
            return false;
        }

        $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);

        $columnSql = array_merge($columnSql, $eventArgs->getSql());

        return $eventArgs->isDefaultPrevented();
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
1966 1967 1968 1969
     * @param string                          $oldColumnName
     * @param \Doctrine\DBAL\Schema\Column    $column
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
     * @param array                           $columnSql
1970
     *
1971
     * @return boolean
1972 1973 1974 1975 1976 1977 1978
     */
    protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
    {
        if (null === $this->_eventManager) {
            return false;
        }

1979
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
            return false;
        }

        $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);

        $columnSql = array_merge($columnSql, $eventArgs->getSql());

        return $eventArgs->isDefaultPrevented();
    }
Christophe Coevoet's avatar
Christophe Coevoet committed
1990

1991
    /**
Benjamin Morel's avatar
Benjamin Morel committed
1992 1993
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
     * @param array                           $sql
1994
     *
1995
     * @return boolean
1996 1997 1998 1999 2000 2001 2002
     */
    protected function onSchemaAlterTable(TableDiff $diff, &$sql)
    {
        if (null === $this->_eventManager) {
            return false;
        }

2003
        if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
            return false;
        }

        $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
        $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);

        $sql = array_merge($sql, $eventArgs->getSql());

        return $eventArgs->isDefaultPrevented();
    }
2014

Benjamin Morel's avatar
Benjamin Morel committed
2015 2016 2017 2018 2019
    /**
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
     *
     * @return array
     */
2020 2021
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
    {
2022
        $tableName = $diff->getName($this)->getQuotedName($this);
2023

2024 2025
        $sql = array();
        if ($this->supportsForeignKeyConstraints()) {
2026
            foreach ($diff->removedForeignKeys as $foreignKey) {
2027 2028
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
            }
2029
            foreach ($diff->changedForeignKeys as $foreignKey) {
2030 2031 2032 2033
                $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
            }
        }

2034
        foreach ($diff->removedIndexes as $index) {
2035 2036
            $sql[] = $this->getDropIndexSQL($index, $tableName);
        }
2037
        foreach ($diff->changedIndexes as $index) {
2038 2039 2040 2041 2042
            $sql[] = $this->getDropIndexSQL($index, $tableName);
        }

        return $sql;
    }
2043

Benjamin Morel's avatar
Benjamin Morel committed
2044 2045 2046 2047 2048
    /**
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
     *
     * @return array
     */
2049
    protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
2050
    {
2051 2052
        $tableName = (false !== $diff->newName)
            ? $diff->getNewName()->getQuotedName($this)
2053
            : $diff->getName($this)->getQuotedName($this);
2054

2055
        $sql = array();
2056

2057
        if ($this->supportsForeignKeyConstraints()) {
2058
            foreach ($diff->addedForeignKeys as $foreignKey) {
2059
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2060
            }
2061

2062
            foreach ($diff->changedForeignKeys as $foreignKey) {
2063
                $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
2064 2065 2066
            }
        }

2067
        foreach ($diff->addedIndexes as $index) {
2068
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2069
        }
2070

2071
        foreach ($diff->changedIndexes as $index) {
2072
            $sql[] = $this->getCreateIndexSQL($index, $tableName);
2073 2074
        }

2075 2076 2077 2078 2079 2080 2081 2082
        foreach ($diff->renamedIndexes as $oldIndexName => $index) {
            $oldIndexName = new Identifier($oldIndexName);
            $sql          = array_merge(
                $sql,
                $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
            );
        }

2083 2084
        return $sql;
    }
2085

2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102
    /**
     * Returns the SQL for renaming an index on a table.
     *
     * @param string                      $oldIndexName The name of the index to rename from.
     * @param \Doctrine\DBAL\Schema\Index $index        The definition of the index to rename to.
     * @param string                      $tableName    The table to rename the given index on.
     *
     * @return array The sequence of SQL statements for renaming the given index.
     */
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
    {
        return array(
            $this->getDropIndexSQL($oldIndexName, $tableName),
            $this->getCreateIndexSQL($index, $tableName)
        );
    }

2103 2104 2105
    /**
     * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2106
     * @param \Doctrine\DBAL\Schema\TableDiff $diff
2107
     *
2108 2109 2110 2111 2112 2113
     * @return array
     */
    protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
    {
        return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
    }
2114

2115
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2116
     * Gets declaration of a number of fields in bulk.
2117
     *
Benjamin Morel's avatar
Benjamin Morel committed
2118 2119 2120 2121 2122
     * @param array $fields A multidimensional associative array.
     *                      The first dimension determines the field name, while the second
     *                      dimension is keyed with the name of the properties
     *                      of the field being declared as array indexes. Currently, the types
     *                      of supported field properties are as follows:
2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143
     *
     *      length
     *          Integer value that determines the maximum length of the text
     *          field. If this argument is missing the field should be
     *          declared to have the longest length allowed by the DBMS.
     *
     *      default
     *          Text value to be used as default for this field.
     *
     *      notnull
     *          Boolean flag that indicates whether this field is constrained
     *          to not be set to null.
     *      charset
     *          Text value with the default CHARACTER SET for this field.
     *      collation
     *          Text value with the default COLLATION for this field.
     *      unique
     *          unique constraint
     *
     * @return string
     */
2144
    public function getColumnDeclarationListSQL(array $fields)
2145
    {
2146
        $queryFields = array();
2147

2148
        foreach ($fields as $fieldName => $field) {
2149
            $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
2150
        }
2151

2152 2153 2154 2155
        return implode(', ', $queryFields);
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2156
     * Obtains DBMS specific SQL code portion needed to declare a generic type
2157 2158
     * field to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2159 2160 2161 2162
     * @param string $name  The name the field to be declared.
     * @param array  $field An associative array with the name of the properties
     *                      of the field being declared as array indexes. Currently, the types
     *                      of supported field properties are as follows:
2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182
     *
     *      length
     *          Integer value that determines the maximum length of the text
     *          field. If this argument is missing the field should be
     *          declared to have the longest length allowed by the DBMS.
     *
     *      default
     *          Text value to be used as default for this field.
     *
     *      notnull
     *          Boolean flag that indicates whether this field is constrained
     *          to not be set to null.
     *      charset
     *          Text value with the default CHARACTER SET for this field.
     *      collation
     *          Text value with the default COLLATION for this field.
     *      unique
     *          unique constraint
     *      check
     *          column check constraint
2183 2184
     *      columnDefinition
     *          a string that defines the complete column
2185
     *
Benjamin Morel's avatar
Benjamin Morel committed
2186
     * @return string DBMS specific SQL code portion that should be used to declare the column.
2187
     */
2188
    public function getColumnDeclarationSQL($name, array $field)
2189
    {
2190
        if (isset($field['columnDefinition'])) {
2191
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
2192
        } else {
2193
            $default = $this->getDefaultValueDeclarationSQL($field);
2194

2195
            $charset = (isset($field['charset']) && $field['charset']) ?
2196
                    ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
2197

2198
            $collation = (isset($field['collation']) && $field['collation']) ?
2199
                    ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
2200

2201
            $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
2202

2203
            $unique = (isset($field['unique']) && $field['unique']) ?
2204
                    ' ' . $this->getUniqueFieldDeclarationSQL() : '';
2205

2206 2207
            $check = (isset($field['check']) && $field['check']) ?
                    ' ' . $field['check'] : '';
2208

2209 2210 2211
            $typeDecl = $field['type']->getSqlDeclaration($field, $this);
            $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
        }
2212

2213
        if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') {
2214
            $columnDef .= " COMMENT " . $this->quoteStringLiteral($field['comment']);
2215 2216
        }

2217
        return $name . ' ' . $columnDef;
2218
    }
2219

2220
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2221
     * Returns the SQL snippet that declares a floating point column of arbitrary precision.
2222 2223
     *
     * @param array $columnDef
2224
     *
2225 2226
     * @return string
     */
2227
    public function getDecimalTypeDeclarationSQL(array $columnDef)
2228 2229
    {
        $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision']))
2230
            ? 10 : $columnDef['precision'];
2231 2232
        $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale']))
            ? 0 : $columnDef['scale'];
2233

2234 2235
        return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
    }
2236 2237

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2238
     * Obtains DBMS specific SQL code portion needed to set a default value
2239 2240
     * declaration to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2241
     * @param array $field The field definition array.
2242
     *
Benjamin Morel's avatar
Benjamin Morel committed
2243
     * @return string DBMS specific SQL code portion needed to set a default value.
2244
     */
2245
    public function getDefaultValueDeclarationSQL($field)
2246
    {
2247
        $default = empty($field['notnull']) ? ' DEFAULT NULL' : '';
2248

2249
        if (isset($field['default'])) {
2250 2251
            $default = " DEFAULT '".$field['default']."'";
            if (isset($field['type'])) {
2252
                if (in_array((string) $field['type'], array("Integer", "BigInt", "SmallInt"))) {
2253
                    $default = " DEFAULT ".$field['default'];
2254
                } elseif (in_array((string) $field['type'], array('DateTime', 'DateTimeTz')) && $field['default'] == $this->getCurrentTimestampSQL()) {
2255
                    $default = " DEFAULT ".$this->getCurrentTimestampSQL();
2256
                } elseif ((string) $field['type'] == 'Time' && $field['default'] == $this->getCurrentTimeSQL()) {
2257
                    $default = " DEFAULT ".$this->getCurrentTimeSQL();
2258
                } elseif ((string) $field['type'] == 'Date' && $field['default'] == $this->getCurrentDateSQL()) {
2259
                    $default = " DEFAULT ".$this->getCurrentDateSQL();
Steve Müller's avatar
Steve Müller committed
2260
                } elseif ((string) $field['type'] == 'Boolean') {
2261
                    $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'";
2262 2263
                }
            }
2264
        }
Benjamin Morel's avatar
Benjamin Morel committed
2265

2266 2267 2268 2269
        return $default;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2270
     * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
2271 2272
     * declaration to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2273
     * @param array $definition The check definition.
2274
     *
Benjamin Morel's avatar
Benjamin Morel committed
2275
     * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
2276
     */
2277
    public function getCheckDeclarationSQL(array $definition)
2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
    {
        $constraints = array();
        foreach ($definition as $field => $def) {
            if (is_string($def)) {
                $constraints[] = 'CHECK (' . $def . ')';
            } else {
                if (isset($def['min'])) {
                    $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
                }

                if (isset($def['max'])) {
                    $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
                }
            }
        }

        return implode(', ', $constraints);
    }
2296

2297
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2298
     * Obtains DBMS specific SQL code portion needed to set a unique
2299 2300
     * constraint declaration to be used in statements like CREATE TABLE.
     *
2301 2302
     * @param string                      $name  The name of the unique constraint.
     * @param \Doctrine\DBAL\Schema\Index $index The index definition.
2303
     *
Benjamin Morel's avatar
Benjamin Morel committed
2304 2305 2306
     * @return string DBMS specific SQL code portion needed to set a constraint.
     *
     * @throws \InvalidArgumentException
2307
     */
2308
    public function getUniqueConstraintDeclarationSQL($name, Index $index)
2309
    {
2310
        $columns = $index->getQuotedColumns($this);
2311
        $name = new Identifier($name);
2312 2313

        if (count($columns) === 0) {
2314
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
2315
        }
2316

2317
        return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE ('
2318
             . $this->getIndexFieldDeclarationListSQL($columns)
2319
             . ')' . $this->getPartialIndexSQL($index);
2320
    }
2321 2322

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2323
     * Obtains DBMS specific SQL code portion needed to set an index
2324 2325
     * declaration to be used in statements like CREATE TABLE.
     *
2326 2327
     * @param string                      $name  The name of the index.
     * @param \Doctrine\DBAL\Schema\Index $index The index definition.
Benjamin Morel's avatar
Benjamin Morel committed
2328 2329
     *
     * @return string DBMS specific SQL code portion needed to set an index.
2330
     *
Benjamin Morel's avatar
Benjamin Morel committed
2331
     * @throws \InvalidArgumentException
2332
     */
2333
    public function getIndexDeclarationSQL($name, Index $index)
2334
    {
2335
        $columns = $index->getQuotedColumns($this);
2336
        $name = new Identifier($name);
2337 2338

        if (count($columns) === 0) {
2339
            throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
2340 2341
        }

2342
        return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
2343 2344
            . $this->getIndexFieldDeclarationListSQL($columns)
            . ')' . $this->getPartialIndexSQL($index);
2345 2346
    }

2347
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2348
     * Obtains SQL code portion needed to create a custom column,
2349 2350 2351
     * e.g. when a field has the "columnDefinition" keyword.
     * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
     *
2352
     * @param array $columnDef
2353
     *
2354 2355
     * @return string
     */
2356
    public function getCustomTypeDeclarationSQL(array $columnDef)
2357 2358 2359 2360
    {
        return $columnDef['columnDefinition'];
    }

2361
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2362
     * Obtains DBMS specific SQL code portion needed to set an index
2363 2364
     * declaration to be used in statements like CREATE TABLE.
     *
2365
     * @param array $fields
2366
     *
2367 2368
     * @return string
     */
2369
    public function getIndexFieldDeclarationListSQL(array $fields)
2370 2371
    {
        $ret = array();
2372

2373 2374
        foreach ($fields as $field => $definition) {
            if (is_array($definition)) {
2375
                $ret[] = $field;
2376
            } else {
2377
                $ret[] = $definition;
2378 2379
            }
        }
2380

2381 2382 2383 2384
        return implode(', ', $ret);
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2385
     * Returns the required SQL string that fits between CREATE ... TABLE
2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397
     * to create the table as a temporary table.
     *
     * Should be overridden in driver classes to return the correct string for the
     * specific database type.
     *
     * The default is to return the string "TEMPORARY" - this will result in a
     * SQL error for any database that does not support temporary tables, or that
     * requires a different SQL command from "CREATE TEMPORARY TABLE".
     *
     * @return string The string required to be placed between "CREATE" and "TABLE"
     *                to generate a temporary table, if possible.
     */
2398
    public function getTemporaryTableSQL()
2399 2400 2401
    {
        return 'TEMPORARY';
    }
2402

2403 2404 2405
    /**
     * Some vendors require temporary table names to be qualified specially.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2406
     * @param string $tableName
2407
     *
2408 2409 2410 2411 2412 2413 2414
     * @return string
     */
    public function getTemporaryTableName($tableName)
    {
        return $tableName;
    }

2415 2416 2417 2418
    /**
     * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
     * of a field declaration to be used in statements like CREATE TABLE.
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
2419
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2420
     *
Benjamin Morel's avatar
Benjamin Morel committed
2421 2422
     * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
     *                of a field declaration.
2423
     */
2424
    public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2425
    {
2426 2427
        $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
        $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2428 2429 2430 2431 2432

        return $sql;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2433
     * Returns the FOREIGN KEY query section dealing with non-standard options
2434 2435
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
     *
Benjamin Morel's avatar
Benjamin Morel committed
2436
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key definition.
2437
     *
2438 2439
     * @return string
     */
2440
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2441 2442
    {
        $query = '';
2443
        if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2444
            $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2445
        }
2446
        if ($foreignKey->hasOption('onDelete')) {
2447
            $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2448
        }
Benjamin Morel's avatar
Benjamin Morel committed
2449

2450 2451 2452 2453
        return $query;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2454
     * Returns the given referential action in uppercase if valid, otherwise throws an exception.
2455
     *
Benjamin Morel's avatar
Benjamin Morel committed
2456
     * @param string $action The foreign key referential action.
2457
     *
2458
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2459 2460
     *
     * @throws \InvalidArgumentException if unknown referential action given
2461
     */
2462
    public function getForeignKeyReferentialActionSQL($action)
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472
    {
        $upper = strtoupper($action);
        switch ($upper) {
            case 'CASCADE':
            case 'SET NULL':
            case 'NO ACTION':
            case 'RESTRICT':
            case 'SET DEFAULT':
                return $upper;
            default:
2473
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper);
2474 2475 2476 2477
        }
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2478
     * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2479 2480
     * of a field declaration to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2481
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2482
     *
2483
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2484 2485
     *
     * @throws \InvalidArgumentException
2486
     */
2487
    public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2488 2489
    {
        $sql = '';
2490
        if (strlen($foreignKey->getName())) {
2491
            $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2492 2493 2494
        }
        $sql .= 'FOREIGN KEY (';

2495
        if (count($foreignKey->getLocalColumns()) === 0) {
2496
            throw new \InvalidArgumentException("Incomplete definition. 'local' required.");
2497
        }
2498
        if (count($foreignKey->getForeignColumns()) === 0) {
2499
            throw new \InvalidArgumentException("Incomplete definition. 'foreign' required.");
2500
        }
2501
        if (strlen($foreignKey->getForeignTableName()) === 0) {
2502
            throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2503 2504
        }

2505
        $sql .= implode(', ', $foreignKey->getQuotedLocalColumns($this))
2506
              . ') REFERENCES '
2507
              . $foreignKey->getQuotedForeignTableName($this) . ' ('
2508
              . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
2509 2510 2511 2512 2513

        return $sql;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2514
     * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
2515 2516
     * of a field declaration to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2517 2518
     * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
     *                of a field declaration.
2519
     */
2520
    public function getUniqueFieldDeclarationSQL()
2521 2522 2523 2524 2525
    {
        return 'UNIQUE';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2526
     * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
2527 2528
     * of a field declaration to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2529
     * @param string $charset The name of the charset.
2530
     *
Benjamin Morel's avatar
Benjamin Morel committed
2531 2532
     * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
     *                of a field declaration.
2533
     */
2534
    public function getColumnCharsetDeclarationSQL($charset)
2535 2536 2537 2538 2539
    {
        return '';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2540
     * Obtains DBMS specific SQL code portion needed to set the COLLATION
2541 2542
     * of a field declaration to be used in statements like CREATE TABLE.
     *
Benjamin Morel's avatar
Benjamin Morel committed
2543
     * @param string $collation The name of the collation.
2544
     *
Benjamin Morel's avatar
Benjamin Morel committed
2545 2546
     * @return string DBMS specific SQL code portion needed to set the COLLATION
     *                of a field declaration.
2547
     */
2548
    public function getColumnCollationDeclarationSQL($collation)
2549
    {
2550
        return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
2551
    }
2552

2553 2554 2555 2556 2557 2558 2559 2560 2561 2562
    /**
     * Whether the platform prefers sequences for ID generation.
     * Subclasses should override this method to return TRUE if they prefer sequences.
     *
     * @return boolean
     */
    public function prefersSequences()
    {
        return false;
    }
2563

2564 2565 2566 2567 2568 2569 2570 2571 2572 2573
    /**
     * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
     * Subclasses should override this method to return TRUE if they prefer identity columns.
     *
     * @return boolean
     */
    public function prefersIdentityColumns()
    {
        return false;
    }
2574

2575 2576
    /**
     * Some platforms need the boolean values to be converted.
2577
     *
romanb's avatar
romanb committed
2578
     * The default conversion in this implementation converts to integers (false => 0, true => 1).
2579
     *
2580 2581
     * Note: if the input is not a boolean the original input might be returned.
     *
2582 2583
     * There are two contexts when converting booleans: Literals and Prepared Statements.
     * This method should handle the literal case
2584
     *
2585
     * @param mixed $item A boolean or an array of them.
2586
     *
2587
     * @return mixed A boolean database value or an array of them.
2588
     */
2589
    public function convertBooleans($item)
2590 2591 2592 2593 2594 2595 2596
    {
        if (is_array($item)) {
            foreach ($item as $k => $value) {
                if (is_bool($value)) {
                    $item[$k] = (int) $value;
                }
            }
Steve Müller's avatar
Steve Müller committed
2597
        } elseif (is_bool($item)) {
romanb's avatar
romanb committed
2598
            $item = (int) $item;
2599
        }
2600

2601 2602
        return $item;
    }
2603

2604
    /**
2605
     * Some platforms have boolean literals that needs to be correctly converted
2606 2607 2608 2609 2610
     *
     * The default conversion tries to convert value into bool "(bool)$item"
     *
     * @param mixed $item
     *
2611
     * @return bool|null
2612 2613 2614
     */
    public function convertFromBoolean($item)
    {
2615
        return null === $item ? null: (bool) $item ;
2616
    }
2617

2618 2619 2620 2621
    /**
     * This method should handle the prepared statements case. When there is no
     * distinction, it's OK to use the same method.
     *
2622 2623 2624
     * Note: if the input is not a boolean the original input might be returned.
     *
     * @param mixed $item A boolean or an array of them.
2625
     *
2626
     * @return mixed A boolean database value or an array of them.
2627
     */
2628
    public function convertBooleansToDatabaseValue($item)
2629
    {
2630
        return $this->convertBooleans($item);
2631 2632
    }

2633
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2634
     * Returns the SQL specific for the platform to get the current date.
2635 2636 2637
     *
     * @return string
     */
2638
    public function getCurrentDateSQL()
2639 2640 2641 2642 2643
    {
        return 'CURRENT_DATE';
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2644
     * Returns the SQL specific for the platform to get the current time.
2645 2646 2647
     *
     * @return string
     */
2648
    public function getCurrentTimeSQL()
2649 2650 2651 2652
    {
        return 'CURRENT_TIME';
    }

2653
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2654
     * Returns the SQL specific for the platform to get the current timestamp
2655 2656 2657
     *
     * @return string
     */
2658
    public function getCurrentTimestampSQL()
2659 2660 2661
    {
        return 'CURRENT_TIMESTAMP';
    }
2662

romanb's avatar
romanb committed
2663
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2664
     * Returns the SQL for a given transaction isolation level Connection constant.
romanb's avatar
romanb committed
2665
     *
2666
     * @param integer $level
2667
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
2668
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2669 2670
     *
     * @throws \InvalidArgumentException
romanb's avatar
romanb committed
2671
     */
2672
    protected function _getTransactionIsolationLevelSQL($level)
romanb's avatar
romanb committed
2673 2674
    {
        switch ($level) {
2675
            case Connection::TRANSACTION_READ_UNCOMMITTED:
romanb's avatar
romanb committed
2676
                return 'READ UNCOMMITTED';
2677
            case Connection::TRANSACTION_READ_COMMITTED:
romanb's avatar
romanb committed
2678
                return 'READ COMMITTED';
2679
            case Connection::TRANSACTION_REPEATABLE_READ:
romanb's avatar
romanb committed
2680
                return 'REPEATABLE READ';
2681
            case Connection::TRANSACTION_SERIALIZABLE:
romanb's avatar
romanb committed
2682 2683
                return 'SERIALIZABLE';
            default:
2684
                throw new \InvalidArgumentException('Invalid isolation level:' . $level);
2685 2686 2687
        }
    }

Benjamin Morel's avatar
Benjamin Morel committed
2688 2689 2690 2691 2692
    /**
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2693
    public function getListDatabasesSQL()
2694
    {
2695
        throw DBALException::notSupported(__METHOD__);
2696 2697
    }

2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709
    /**
     * Returns the SQL statement for retrieving the namespaces defined in the database.
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
    public function getListNamespacesSQL()
    {
        throw DBALException::notSupported(__METHOD__);
    }

Benjamin Morel's avatar
Benjamin Morel committed
2710 2711 2712 2713 2714 2715 2716
    /**
     * @param string $database
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2717
    public function getListSequencesSQL($database)
2718
    {
2719
        throw DBALException::notSupported(__METHOD__);
2720 2721
    }

Benjamin Morel's avatar
Benjamin Morel committed
2722 2723 2724 2725 2726 2727 2728
    /**
     * @param string $table
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2729
    public function getListTableConstraintsSQL($table)
2730
    {
2731
        throw DBALException::notSupported(__METHOD__);
2732 2733
    }

Benjamin Morel's avatar
Benjamin Morel committed
2734 2735 2736 2737 2738 2739 2740 2741
    /**
     * @param string      $table
     * @param string|null $database
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2742
    public function getListTableColumnsSQL($table, $database = null)
2743
    {
2744
        throw DBALException::notSupported(__METHOD__);
2745 2746
    }

Benjamin Morel's avatar
Benjamin Morel committed
2747 2748 2749 2750 2751
    /**
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2752
    public function getListTablesSQL()
2753
    {
2754
        throw DBALException::notSupported(__METHOD__);
2755 2756
    }

Benjamin Morel's avatar
Benjamin Morel committed
2757 2758 2759 2760 2761
    /**
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2762
    public function getListUsersSQL()
2763
    {
2764
        throw DBALException::notSupported(__METHOD__);
2765 2766
    }

2767
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2768
     * Returns the SQL to list all views of a database or user.
2769 2770
     *
     * @param string $database
2771
     *
2772
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2773 2774
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2775
     */
2776
    public function getListViewsSQL($database)
2777
    {
2778
        throw DBALException::notSupported(__METHOD__);
2779 2780
    }

2781
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2782
     * Returns the list of indexes for the current database.
2783
     *
2784 2785
     * The current database parameter is optional but will always be passed
     * when using the SchemaManager API and is the database the given table is in.
2786
     *
2787 2788 2789
     * Attention: Some platforms only support currentDatabase when they
     * are connected with that database. Cross-database information schema
     * requests may be impossible.
2790
     *
2791
     * @param string $table
2792
     * @param string $currentDatabase
2793
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
2794
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2795 2796
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2797 2798
     */
    public function getListTableIndexesSQL($table, $currentDatabase = null)
2799
    {
2800
        throw DBALException::notSupported(__METHOD__);
2801 2802
    }

Benjamin Morel's avatar
Benjamin Morel committed
2803 2804 2805 2806 2807 2808 2809
    /**
     * @param string $table
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2810
    public function getListTableForeignKeysSQL($table)
2811
    {
2812
        throw DBALException::notSupported(__METHOD__);
2813 2814
    }

Benjamin Morel's avatar
Benjamin Morel committed
2815 2816 2817 2818 2819 2820 2821 2822
    /**
     * @param string $name
     * @param string $sql
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2823
    public function getCreateViewSQL($name, $sql)
2824
    {
2825
        throw DBALException::notSupported(__METHOD__);
2826 2827
    }

Benjamin Morel's avatar
Benjamin Morel committed
2828 2829 2830 2831 2832 2833 2834
    /**
     * @param string $name
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2835
    public function getDropViewSQL($name)
2836
    {
2837
        throw DBALException::notSupported(__METHOD__);
2838 2839
    }

2840
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2841
     * Returns the SQL snippet to drop an existing sequence.
2842
     *
jeroendedauw's avatar
jeroendedauw committed
2843
     * @param Sequence|string $sequence
2844 2845
     *
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2846 2847
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2848
     */
2849
    public function getDropSequenceSQL($sequence)
2850
    {
2851
        throw DBALException::notSupported(__METHOD__);
2852 2853
    }

Benjamin Morel's avatar
Benjamin Morel committed
2854 2855 2856 2857 2858 2859 2860
    /**
     * @param string $sequenceName
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
2861
    public function getSequenceNextValSQL($sequenceName)
2862
    {
2863
        throw DBALException::notSupported(__METHOD__);
romanb's avatar
romanb committed
2864
    }
2865

2866
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2867
     * Returns the SQL to create a new database.
2868
     *
Benjamin Morel's avatar
Benjamin Morel committed
2869
     * @param string $database The name of the database that should be created.
2870 2871
     *
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2872 2873
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2874
     */
2875
    public function getCreateDatabaseSQL($database)
2876
    {
2877
        throw DBALException::notSupported(__METHOD__);
2878 2879
    }

romanb's avatar
romanb committed
2880
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2881
     * Returns the SQL to set the transaction isolation level.
romanb's avatar
romanb committed
2882
     *
2883
     * @param integer $level
2884
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
2885
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2886 2887
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
romanb's avatar
romanb committed
2888
     */
2889
    public function getSetTransactionIsolationSQL($level)
romanb's avatar
romanb committed
2890
    {
2891
        throw DBALException::notSupported(__METHOD__);
romanb's avatar
romanb committed
2892
    }
2893

2894
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2895 2896
     * Obtains DBMS specific SQL to be used to create datetime fields in
     * statements like CREATE TABLE.
2897
     *
2898
     * @param array $fieldDeclaration
2899
     *
2900
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2901 2902
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2903
     */
2904
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2905
    {
2906
        throw DBALException::notSupported(__METHOD__);
2907
    }
2908 2909

    /**
Benjamin Morel's avatar
Benjamin Morel committed
2910
     * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields.
2911
     *
2912
     * @param array $fieldDeclaration
2913
     *
Christophe Coevoet's avatar
Christophe Coevoet committed
2914
     * @return string
2915 2916 2917
     */
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
    {
2918
        return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2919
    }
2920 2921


2922
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2923
     * Obtains DBMS specific SQL to be used to create date fields in statements
2924
     * like CREATE TABLE.
2925
     *
2926
     * @param array $fieldDeclaration
2927
     *
2928
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2929 2930
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2931
     */
2932
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
2933
    {
2934
        throw DBALException::notSupported(__METHOD__);
2935
    }
2936

2937
    /**
Benjamin Morel's avatar
Benjamin Morel committed
2938
     * Obtains DBMS specific SQL to be used to create time fields in statements
2939 2940 2941
     * like CREATE TABLE.
     *
     * @param array $fieldDeclaration
2942
     *
2943
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
2944 2945
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
2946
     */
2947
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
2948
    {
2949
        throw DBALException::notSupported(__METHOD__);
2950 2951
    }

Benjamin Morel's avatar
Benjamin Morel committed
2952 2953 2954 2955 2956
    /**
     * @param array $fieldDeclaration
     *
     * @return string
     */
2957 2958 2959 2960 2961
    public function getFloatDeclarationSQL(array $fieldDeclaration)
    {
        return 'DOUBLE PRECISION';
    }

romanb's avatar
romanb committed
2962 2963 2964 2965
    /**
     * Gets the default transaction isolation level of the platform.
     *
     * @return integer The default isolation level.
2966
     *
2967
     * @see Doctrine\DBAL\Connection\TRANSACTION_* constants.
romanb's avatar
romanb committed
2968 2969 2970
     */
    public function getDefaultTransactionIsolationLevel()
    {
2971
        return Connection::TRANSACTION_READ_COMMITTED;
romanb's avatar
romanb committed
2972
    }
2973

2974
    /* supports*() methods */
2975 2976 2977 2978 2979 2980

    /**
     * Whether the platform supports sequences.
     *
     * @return boolean
     */
2981 2982 2983 2984
    public function supportsSequences()
    {
        return false;
    }
2985 2986 2987

    /**
     * Whether the platform supports identity columns.
Benjamin Morel's avatar
Benjamin Morel committed
2988
     *
Pascal Borreli's avatar
Pascal Borreli committed
2989
     * Identity columns are columns that receive an auto-generated value from the
2990 2991 2992 2993
     * database on insert of a row.
     *
     * @return boolean
     */
2994 2995 2996 2997
    public function supportsIdentityColumns()
    {
        return false;
    }
2998

2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029
    /**
     * Whether the platform emulates identity columns through sequences.
     *
     * Some platforms that do not support identity columns natively
     * but support sequences can emulate identity columns by using
     * sequences.
     *
     * @return boolean
     */
    public function usesSequenceEmulatedIdentityColumns()
    {
        return false;
    }

    /**
     * Returns the name of the sequence for a particular identity column in a particular table.
     *
     * @param string $tableName  The name of the table to return the sequence name for.
     * @param string $columnName The name of the identity column in the table to return the sequence name for.
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     *
     * @see    usesSequenceEmulatedIdentityColumns
     */
    public function getIdentitySequenceName($tableName, $columnName)
    {
        throw DBALException::notSupported(__METHOD__);
    }

3030 3031 3032 3033 3034
    /**
     * Whether the platform supports indexes.
     *
     * @return boolean
     */
3035 3036 3037 3038
    public function supportsIndexes()
    {
        return true;
    }
3039

3040 3041 3042 3043 3044 3045 3046 3047 3048 3049
    /**
     * Whether the platform supports partial indexes.
     *
     * @return boolean
     */
    public function supportsPartialIndexes()
    {
        return false;
    }

3050 3051 3052 3053 3054
    /**
     * Whether the platform supports altering tables.
     *
     * @return boolean
     */
3055 3056 3057 3058 3059
    public function supportsAlterTable()
    {
        return true;
    }

3060 3061 3062 3063 3064
    /**
     * Whether the platform supports transactions.
     *
     * @return boolean
     */
3065 3066 3067 3068
    public function supportsTransactions()
    {
        return true;
    }
3069 3070 3071 3072 3073 3074

    /**
     * Whether the platform supports savepoints.
     *
     * @return boolean
     */
3075 3076 3077 3078
    public function supportsSavepoints()
    {
        return true;
    }
3079

3080 3081 3082 3083 3084 3085 3086 3087 3088 3089
    /**
     * Whether the platform supports releasing savepoints.
     *
     * @return boolean
     */
    public function supportsReleaseSavepoints()
    {
        return $this->supportsSavepoints();
    }

3090 3091 3092 3093 3094
    /**
     * Whether the platform supports primary key constraints.
     *
     * @return boolean
     */
3095 3096 3097 3098
    public function supportsPrimaryConstraints()
    {
        return true;
    }
3099 3100

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3101
     * Whether the platform supports foreign key constraints.
3102 3103 3104
     *
     * @return boolean
     */
3105 3106 3107 3108
    public function supportsForeignKeyConstraints()
    {
        return true;
    }
3109 3110

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3111
     * Whether this platform supports onUpdate in foreign key constraints.
3112
     *
3113
     * @return boolean
3114 3115 3116 3117 3118
     */
    public function supportsForeignKeyOnUpdate()
    {
        return ($this->supportsForeignKeyConstraints() && true);
    }
3119

3120 3121
    /**
     * Whether the platform supports database schemas.
3122
     *
3123 3124 3125 3126 3127 3128
     * @return boolean
     */
    public function supportsSchemas()
    {
        return false;
    }
3129

3130
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3131
     * Whether this platform can emulate schemas.
3132 3133 3134 3135 3136
     *
     * Platforms that either support or emulate schemas don't automatically
     * filter a schema for the namespaced elements in {@link
     * AbstractManager#createSchema}.
     *
3137
     * @return boolean
3138 3139 3140 3141 3142 3143
     */
    public function canEmulateSchemas()
    {
        return false;
    }

3144 3145 3146 3147 3148 3149 3150
    /**
     * Returns the default schema name.
     *
     * @return string
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
     */
3151
    public function getDefaultSchemaName()
3152 3153 3154 3155
    {
        throw DBALException::notSupported(__METHOD__);
    }

3156
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3157 3158
     * Whether this platform supports create database.
     *
3159 3160
     * Some databases don't allow to create and drop databases at all or only with certain tools.
     *
3161
     * @return boolean
3162 3163 3164 3165 3166 3167
     */
    public function supportsCreateDropDatabase()
    {
        return true;
    }

3168
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3169
     * Whether the platform supports getting the affected rows of a recent update/delete type query.
3170 3171 3172
     *
     * @return boolean
     */
3173 3174 3175 3176
    public function supportsGettingAffectedRows()
    {
        return true;
    }
3177

3178
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3179
     * Whether this platform support to add inline column comments as postfix.
3180
     *
3181
     * @return boolean
3182 3183 3184 3185 3186 3187 3188
     */
    public function supportsInlineColumnComments()
    {
        return false;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3189
     * Whether this platform support the proprietary syntax "COMMENT ON asset".
3190
     *
3191
     * @return boolean
3192 3193 3194 3195 3196 3197
     */
    public function supportsCommentOnStatement()
    {
        return false;
    }

3198 3199 3200 3201 3202 3203 3204 3205 3206 3207
    /**
     * Does this platform have native guid type.
     *
     * @return boolean
     */
    public function hasNativeGuidType()
    {
        return false;
    }

3208 3209 3210 3211 3212 3213 3214 3215 3216 3217
    /**
     * Does this platform have native JSON type.
     *
     * @return boolean
     */
    public function hasNativeJsonType()
    {
        return false;
    }

3218 3219 3220 3221
    /**
     * @deprecated
     * @todo Remove in 3.0
     */
3222
    public function getIdentityColumnNullInsertSQL()
3223 3224 3225 3226
    {
        return "";
    }

3227
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3228
     * Whether this platform supports views.
3229 3230
     *
     * @return boolean
3231 3232 3233 3234 3235 3236
     */
    public function supportsViews()
    {
        return true;
    }

3237 3238 3239 3240 3241 3242 3243 3244 3245 3246
    /**
     * Does this platform support column collation?
     *
     * @return boolean
     */
    public function supportsColumnCollation()
    {
        return false;
    }

3247
    /**
3248 3249
     * Gets the format string, as accepted by the date() function, that describes
     * the format of a stored datetime value of this platform.
3250
     *
3251
     * @return string The format string.
3252 3253 3254 3255 3256 3257
     */
    public function getDateTimeFormatString()
    {
        return 'Y-m-d H:i:s';
    }

3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268
    /**
     * Gets the format string, as accepted by the date() function, that describes
     * the format of a stored datetime with timezone value of this platform.
     *
     * @return string The format string.
     */
    public function getDateTimeTzFormatString()
    {
        return 'Y-m-d H:i:s';
    }

3269 3270 3271
    /**
     * Gets the format string, as accepted by the date() function, that describes
     * the format of a stored date value of this platform.
3272
     *
3273 3274
     * @return string The format string.
     */
3275 3276
    public function getDateFormatString()
    {
3277
        return 'Y-m-d';
3278
    }
3279

3280 3281 3282
    /**
     * Gets the format string, as accepted by the date() function, that describes
     * the format of a stored time value of this platform.
3283
     *
3284 3285
     * @return string The format string.
     */
3286 3287 3288 3289 3290
    public function getTimeFormatString()
    {
        return 'H:i:s';
    }

3291
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3292
     * Adds an driver-specific LIMIT clause to the query.
3293
     *
Benjamin Morel's avatar
Benjamin Morel committed
3294 3295 3296
     * @param string       $query
     * @param integer|null $limit
     * @param integer|null $offset
3297
     *
3298
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
3299 3300
     *
     * @throws DBALException
3301 3302 3303
     */
    final public function modifyLimitQuery($query, $limit, $offset = null)
    {
3304
        if ($limit !== null) {
3305
            $limit = (int) $limit;
3306 3307
        }

3308
        if ($offset !== null) {
3309
            $offset = (int) $offset;
3310 3311 3312 3313

            if ($offset < 0) {
                throw new DBALException("LIMIT argument offset=$offset is not valid");
            }
3314
            if ($offset > 0 && ! $this->supportsLimitOffset()) {
3315 3316
                throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName()));
            }
3317 3318 3319 3320 3321 3322
        }

        return $this->doModifyLimitQuery($query, $limit, $offset);
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3323
     * Adds an driver-specific LIMIT clause to the query.
3324
     *
3325
     * @param string       $query
Benjamin Morel's avatar
Benjamin Morel committed
3326 3327
     * @param integer|null $limit
     * @param integer|null $offset
3328
     *
3329 3330 3331
     * @return string
     */
    protected function doModifyLimitQuery($query, $limit, $offset)
3332
    {
3333
        if ($limit !== null) {
3334
            $query .= ' LIMIT ' . $limit;
3335 3336
        }

3337
        if ($offset !== null) {
3338 3339 3340
            $query .= ' OFFSET ' . $offset;
        }

3341 3342
        return $query;
    }
3343

3344
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3345
     * Whether the database platform support offsets in modify limit clauses.
3346
     *
3347
     * @return boolean
3348 3349 3350 3351 3352 3353
     */
    public function supportsLimitOffset()
    {
        return true;
    }

3354 3355
    /**
     * Gets the character casing of a column in an SQL result set of this platform.
3356
     *
3357
     * @param string $column The column name for which to get the correct character casing.
3358
     *
3359 3360
     * @return string The column name in the character casing used in SQL result sets.
     */
3361
    public function getSQLResultCasing($column)
3362 3363 3364
    {
        return $column;
    }
3365

3366 3367 3368
    /**
     * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
     * by restrictions of the platform, like a maximum length.
3369
     *
3370
     * @param string $schemaElementName
3371
     *
3372 3373 3374 3375 3376 3377
     * @return string
     */
    public function fixSchemaElementName($schemaElementName)
    {
        return $schemaElementName;
    }
3378

3379
    /**
Pascal Borreli's avatar
Pascal Borreli committed
3380
     * Maximum length of any given database identifier, like tables or column names.
3381
     *
3382
     * @return integer
3383 3384 3385 3386 3387 3388
     */
    public function getMaxIdentifierLength()
    {
        return 63;
    }

3389
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3390
     * Returns the insert SQL for an empty insert statement.
3391
     *
3392 3393
     * @param string $tableName
     * @param string $identifierColumnName
3394
     *
Benjamin Morel's avatar
Benjamin Morel committed
3395
     * @return string
3396
     */
3397
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
3398 3399 3400
    {
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
    }
3401 3402

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3403
     * Generates a Truncate Table SQL statement for a given table.
3404 3405 3406 3407
     *
     * Cascade is not supported on many platforms but would optionally cascade the truncate by
     * following the foreign keys.
     *
Benjamin Morel's avatar
Benjamin Morel committed
3408 3409
     * @param string  $tableName
     * @param boolean $cascade
3410
     *
3411 3412
     * @return string
     */
3413
    public function getTruncateTableSQL($tableName, $cascade = false)
3414 3415 3416
    {
        return 'TRUNCATE '.$tableName;
    }
3417 3418 3419

    /**
     * This is for test reasons, many vendors have special requirements for dummy statements.
3420
     *
3421 3422 3423 3424 3425 3426
     * @return string
     */
    public function getDummySelectSQL()
    {
        return 'SELECT 1';
    }
3427 3428

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3429
     * Returns the SQL to create a new savepoint.
3430 3431
     *
     * @param string $savepoint
3432
     *
3433 3434 3435 3436 3437 3438 3439 3440
     * @return string
     */
    public function createSavePoint($savepoint)
    {
        return 'SAVEPOINT ' . $savepoint;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3441
     * Returns the SQL to release a savepoint.
3442 3443
     *
     * @param string $savepoint
3444
     *
3445 3446 3447 3448 3449 3450 3451 3452
     * @return string
     */
    public function releaseSavePoint($savepoint)
    {
        return 'RELEASE SAVEPOINT ' . $savepoint;
    }

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3453
     * Returns the SQL to rollback a savepoint.
3454 3455
     *
     * @param string $savepoint
3456
     *
3457 3458 3459 3460 3461 3462
     * @return string
     */
    public function rollbackSavePoint($savepoint)
    {
        return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
    }
3463 3464

    /**
Benjamin Morel's avatar
Benjamin Morel committed
3465
     * Returns the keyword list instance of this platform.
3466
     *
3467
     * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList
Benjamin Morel's avatar
Benjamin Morel committed
3468 3469
     *
     * @throws \Doctrine\DBAL\DBALException If no keyword list is specified.
3470 3471 3472
     */
    final public function getReservedKeywordsList()
    {
3473
        // Check for an existing instantiation of the keywords class.
3474 3475
        if ($this->_keywords) {
            return $this->_keywords;
3476 3477
        }

3478 3479
        $class = $this->getReservedKeywordsClass();
        $keywords = new $class;
3480
        if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) {
3481 3482
            throw DBALException::notSupported(__METHOD__);
        }
3483 3484

        // Store the instance so it doesn't need to be generated on every request.
3485
        $this->_keywords = $keywords;
3486

3487 3488
        return $keywords;
    }
3489

3490
    /**
Benjamin Morel's avatar
Benjamin Morel committed
3491
     * Returns the class name of the reserved keywords list.
3492
     *
3493
     * @return string
Benjamin Morel's avatar
Benjamin Morel committed
3494 3495
     *
     * @throws \Doctrine\DBAL\DBALException If not supported on this platform.
3496 3497 3498 3499 3500
     */
    protected function getReservedKeywordsClass()
    {
        throw DBALException::notSupported(__METHOD__);
    }
3501 3502

    /**
3503 3504 3505 3506
     * Quotes a literal string.
     * This method is NOT meant to fix SQL injections!
     * It is only meant to escape this platform's string literal
     * quote character inside the given literal string.
3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527
     *
     * @param string $str The literal string to be quoted.
     *
     * @return string The quoted literal string.
     */
    public function quoteStringLiteral($str)
    {
        $c = $this->getStringLiteralQuoteCharacter();

        return $c . str_replace($c, $c . $c, $str) . $c;
    }

    /**
     * Gets the character used for string literal quoting.
     *
     * @return string
     */
    public function getStringLiteralQuoteCharacter()
    {
        return "'";
    }
3528
}