1<?php 2/* 3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 * 15 * This software consists of voluntary contributions made by many individuals 16 * and is licensed under the MIT license. For more information, see 17 * <http://www.doctrine-project.org>. 18 */ 19 20namespace Doctrine\DBAL\Platforms; 21 22use Doctrine\DBAL\DBALException, 23 Doctrine\DBAL\Connection, 24 Doctrine\DBAL\Types, 25 Doctrine\DBAL\Schema\Constraint, 26 Doctrine\DBAL\Schema\Sequence, 27 Doctrine\DBAL\Schema\Table, 28 Doctrine\DBAL\Schema\Index, 29 Doctrine\DBAL\Schema\ForeignKeyConstraint, 30 Doctrine\DBAL\Schema\TableDiff, 31 Doctrine\DBAL\Schema\Column, 32 Doctrine\DBAL\Schema\ColumnDiff, 33 Doctrine\DBAL\Types\Type, 34 Doctrine\DBAL\Events, 35 Doctrine\Common\EventManager, 36 Doctrine\DBAL\Event\SchemaCreateTableEventArgs, 37 Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs, 38 Doctrine\DBAL\Event\SchemaDropTableEventArgs, 39 Doctrine\DBAL\Event\SchemaAlterTableEventArgs, 40 Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs, 41 Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs, 42 Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs, 43 Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs; 44 45/** 46 * Base class for all DatabasePlatforms. The DatabasePlatforms are the central 47 * point of abstraction of platform-specific behaviors, features and SQL dialects. 48 * They are a passive source of information. 49 * 50 * 51 * @link www.doctrine-project.org 52 * @since 2.0 53 * @author Guilherme Blanco <guilhermeblanco@hotmail.com> 54 * @author Jonathan Wage <jonwage@gmail.com> 55 * @author Roman Borschel <roman@code-factory.org> 56 * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library) 57 * @author Benjamin Eberlei <kontakt@beberlei.de> 58 * @todo Remove any unnecessary methods. 59 */ 60abstract class AbstractPlatform 61{ 62 /** 63 * @var integer 64 */ 65 const CREATE_INDEXES = 1; 66 67 /** 68 * @var integer 69 */ 70 const CREATE_FOREIGNKEYS = 2; 71 72 /** 73 * @var integer 74 */ 75 const TRIM_UNSPECIFIED = 0; 76 77 /** 78 * @var integer 79 */ 80 const TRIM_LEADING = 1; 81 82 /** 83 * @var integer 84 */ 85 const TRIM_TRAILING = 2; 86 87 /** 88 * @var integer 89 */ 90 const TRIM_BOTH = 3; 91 92 /** 93 * @var array 94 */ 95 protected $doctrineTypeMapping = null; 96 97 /** 98 * Contains a list of all columns that should generate parseable column comments for type-detection 99 * in reverse engineering scenarios. 100 * 101 * @var array 102 */ 103 protected $doctrineTypeComments = null; 104 105 /** 106 * @var Doctrine\Common\EventManager 107 */ 108 protected $_eventManager; 109 110 /** 111 * Holds the KeywordList instance for the current platform. 112 * 113 * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList 114 */ 115 protected $_keywords; 116 117 /** 118 * Constructor. 119 */ 120 public function __construct() {} 121 122 /** 123 * Sets the EventManager used by the Platform. 124 * 125 * @param \Doctrine\Common\EventManager 126 */ 127 public function setEventManager(EventManager $eventManager) 128 { 129 $this->_eventManager = $eventManager; 130 } 131 132 /** 133 * Gets the EventManager used by the Platform. 134 * 135 * @return \Doctrine\Common\EventManager 136 */ 137 public function getEventManager() 138 { 139 return $this->_eventManager; 140 } 141 142 /** 143 * Gets the SQL snippet that declares a boolean column. 144 * 145 * @param array $columnDef 146 * 147 * @return string 148 */ 149 abstract public function getBooleanTypeDeclarationSQL(array $columnDef); 150 151 /** 152 * Gets the SQL snippet that declares a 4 byte integer column. 153 * 154 * @param array $columnDef 155 * 156 * @return string 157 */ 158 abstract public function getIntegerTypeDeclarationSQL(array $columnDef); 159 160 /** 161 * Gets the SQL snippet that declares an 8 byte integer column. 162 * 163 * @param array $columnDef 164 * 165 * @return string 166 */ 167 abstract public function getBigIntTypeDeclarationSQL(array $columnDef); 168 169 /** 170 * Gets the SQL snippet that declares a 2 byte integer column. 171 * 172 * @param array $columnDef 173 * 174 * @return string 175 */ 176 abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); 177 178 /** 179 * Gets the SQL snippet that declares common properties of an integer column. 180 * 181 * @param array $columnDef 182 * @return string 183 */ 184 abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef); 185 186 /** 187 * Lazy load Doctrine Type Mappings 188 * 189 * @return void 190 */ 191 abstract protected function initializeDoctrineTypeMappings(); 192 193 /** 194 * Initialize Doctrine Type Mappings with the platform defaults 195 * and with all additional type mappings. 196 */ 197 private function initializeAllDoctrineTypeMappings() 198 { 199 $this->initializeDoctrineTypeMappings(); 200 201 foreach (Type::getTypesMap() as $typeName => $className) { 202 foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { 203 $this->doctrineTypeMapping[$dbType] = $typeName; 204 } 205 } 206 } 207 208 /** 209 * Gets the SQL snippet used to declare a VARCHAR column type. 210 * 211 * @param array $field 212 * 213 * @return string 214 */ 215 public function getVarcharTypeDeclarationSQL(array $field) 216 { 217 if ( !isset($field['length'])) { 218 $field['length'] = $this->getVarcharDefaultLength(); 219 } 220 221 $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; 222 223 if ($field['length'] > $this->getVarcharMaxLength()) { 224 return $this->getClobTypeDeclarationSQL($field); 225 } 226 227 return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed); 228 } 229 230 /** 231 * Get the SQL Snippet to create a GUID/UUID field. 232 * 233 * By default this maps directly to a VARCHAR and only maps to more 234 * special datatypes when the underlying databases support this datatype. 235 * 236 * @param array $field 237 * 238 * @return string 239 */ 240 public function getGuidTypeDeclarationSQL(array $field) 241 { 242 return $this->getVarcharTypeDeclarationSQL($field); 243 } 244 245 /** 246 * @param integer $length 247 * @param boolean $fixed 248 * 249 * @return string 250 * 251 * @throws \Doctrine\DBAL\DBALException 252 */ 253 protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) 254 { 255 throw DBALException::notSupported('VARCHARs not supported by Platform.'); 256 } 257 258 /** 259 * Gets the SQL snippet used to declare a CLOB column type. 260 * 261 * @param array $field 262 * 263 * @return string 264 */ 265 abstract public function getClobTypeDeclarationSQL(array $field); 266 267 /** 268 * Gets the SQL Snippet used to declare a BLOB column type. 269 * 270 * @param array $field 271 * 272 * @return string 273 */ 274 abstract public function getBlobTypeDeclarationSQL(array $field); 275 276 /** 277 * Gets the name of the platform. 278 * 279 * @return string 280 */ 281 abstract public function getName(); 282 283 /** 284 * Register a doctrine type to be used in conjunction with a column type of this platform. 285 * 286 * @param string $dbType 287 * @param string $doctrineType 288 * 289 * @throws \Doctrine\DBAL\DBALException if the type is not found 290 */ 291 public function registerDoctrineTypeMapping($dbType, $doctrineType) 292 { 293 if ($this->doctrineTypeMapping === null) { 294 $this->initializeAllDoctrineTypeMappings(); 295 } 296 297 if (!Types\Type::hasType($doctrineType)) { 298 throw DBALException::typeNotFound($doctrineType); 299 } 300 301 $dbType = strtolower($dbType); 302 $this->doctrineTypeMapping[$dbType] = $doctrineType; 303 } 304 305 /** 306 * Get the Doctrine type that is mapped for the given database column type. 307 * 308 * @param string $dbType 309 * 310 * @return string 311 */ 312 public function getDoctrineTypeMapping($dbType) 313 { 314 if ($this->doctrineTypeMapping === null) { 315 $this->initializeAllDoctrineTypeMappings(); 316 } 317 318 $dbType = strtolower($dbType); 319 320 if (!isset($this->doctrineTypeMapping[$dbType])) { 321 throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it."); 322 } 323 324 return $this->doctrineTypeMapping[$dbType]; 325 } 326 327 /** 328 * Check if a database type is currently supported by this platform. 329 * 330 * @param string $dbType 331 * 332 * @return boolean 333 */ 334 public function hasDoctrineTypeMappingFor($dbType) 335 { 336 if ($this->doctrineTypeMapping === null) { 337 $this->initializeAllDoctrineTypeMappings(); 338 } 339 340 $dbType = strtolower($dbType); 341 return isset($this->doctrineTypeMapping[$dbType]); 342 } 343 344 /** 345 * Initialize the Doctrine Type comments instance variable for in_array() checks. 346 * 347 * @return void 348 */ 349 protected function initializeCommentedDoctrineTypes() 350 { 351 $this->doctrineTypeComments = array(); 352 353 foreach (Type::getTypesMap() as $typeName => $className) { 354 $type = Type::getType($typeName); 355 356 if ($type->requiresSQLCommentHint($this)) { 357 $this->doctrineTypeComments[] = $typeName; 358 } 359 } 360 } 361 362 /** 363 * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? 364 * 365 * @param Type $doctrineType 366 * 367 * @return boolean 368 */ 369 public function isCommentedDoctrineType(Type $doctrineType) 370 { 371 if ($this->doctrineTypeComments === null) { 372 $this->initializeCommentedDoctrineTypes(); 373 } 374 375 return in_array($doctrineType->getName(), $this->doctrineTypeComments); 376 } 377 378 /** 379 * Mark this type as to be commented in ALTER TABLE and CREATE TABLE statements. 380 * 381 * @param string|Type $doctrineType 382 * 383 * @return void 384 */ 385 public function markDoctrineTypeCommented($doctrineType) 386 { 387 if ($this->doctrineTypeComments === null) { 388 $this->initializeCommentedDoctrineTypes(); 389 } 390 391 $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; 392 } 393 394 /** 395 * Get the comment to append to a column comment that helps parsing this type in reverse engineering. 396 * 397 * @param Type $doctrineType 398 * @return string 399 */ 400 public function getDoctrineTypeComment(Type $doctrineType) 401 { 402 return '(DC2Type:' . $doctrineType->getName() . ')'; 403 } 404 405 /** 406 * Return the comment of a passed column modified by potential doctrine type comment hints. 407 * 408 * @param Column $column 409 * @return string 410 */ 411 protected function getColumnComment(Column $column) 412 { 413 $comment = $column->getComment(); 414 415 if ($this->isCommentedDoctrineType($column->getType())) { 416 $comment .= $this->getDoctrineTypeComment($column->getType()); 417 } 418 419 return $comment; 420 } 421 422 /** 423 * Gets the character used for identifier quoting. 424 * 425 * @return string 426 */ 427 public function getIdentifierQuoteCharacter() 428 { 429 return '"'; 430 } 431 432 /** 433 * Gets the string portion that starts an SQL comment. 434 * 435 * @return string 436 */ 437 public function getSqlCommentStartString() 438 { 439 return "--"; 440 } 441 442 /** 443 * Gets the string portion that ends an SQL comment. 444 * 445 * @return string 446 */ 447 public function getSqlCommentEndString() 448 { 449 return "\n"; 450 } 451 452 /** 453 * Gets the maximum length of a varchar field. 454 * 455 * @return integer 456 */ 457 public function getVarcharMaxLength() 458 { 459 return 4000; 460 } 461 462 /** 463 * Gets the default length of a varchar field. 464 * 465 * @return integer 466 */ 467 public function getVarcharDefaultLength() 468 { 469 return 255; 470 } 471 472 /** 473 * Gets all SQL wildcard characters of the platform. 474 * 475 * @return array 476 */ 477 public function getWildcards() 478 { 479 return array('%', '_'); 480 } 481 482 /** 483 * Returns the regular expression operator. 484 * 485 * @return string 486 */ 487 public function getRegexpExpression() 488 { 489 throw DBALException::notSupported(__METHOD__); 490 } 491 492 /** 493 * Returns global unique identifier 494 * 495 * @return string to get global unique identifier 496 */ 497 public function getGuidExpression() 498 { 499 throw DBALException::notSupported(__METHOD__); 500 } 501 502 /** 503 * Returns the average value of a column 504 * 505 * @param string $column the column to use 506 * 507 * @return string generated sql including an AVG aggregate function 508 */ 509 public function getAvgExpression($column) 510 { 511 return 'AVG(' . $column . ')'; 512 } 513 514 /** 515 * Returns the number of rows (without a NULL value) of a column 516 * 517 * If a '*' is used instead of a column the number of selected rows 518 * is returned. 519 * 520 * @param string|integer $column the column to use 521 * 522 * @return string generated sql including a COUNT aggregate function 523 */ 524 public function getCountExpression($column) 525 { 526 return 'COUNT(' . $column . ')'; 527 } 528 529 /** 530 * Returns the highest value of a column 531 * 532 * @param string $column the column to use 533 * @return string generated sql including a MAX aggregate function 534 */ 535 public function getMaxExpression($column) 536 { 537 return 'MAX(' . $column . ')'; 538 } 539 540 /** 541 * Returns the lowest value of a column 542 * 543 * @param string $column the column to use 544 * @return string 545 */ 546 public function getMinExpression($column) 547 { 548 return 'MIN(' . $column . ')'; 549 } 550 551 /** 552 * Returns the total sum of a column 553 * 554 * @param string $column the column to use 555 * @return string 556 */ 557 public function getSumExpression($column) 558 { 559 return 'SUM(' . $column . ')'; 560 } 561 562 // scalar functions 563 564 /** 565 * Returns the md5 sum of a field. 566 * 567 * Note: Not SQL92, but common functionality 568 * 569 * @param string $column 570 * @return string 571 */ 572 public function getMd5Expression($column) 573 { 574 return 'MD5(' . $column . ')'; 575 } 576 577 /** 578 * Returns the length of a text field. 579 * 580 * @param string $column 581 * 582 * @return string 583 */ 584 public function getLengthExpression($column) 585 { 586 return 'LENGTH(' . $column . ')'; 587 } 588 589 /** 590 * Returns the squared value of a column 591 * 592 * @param string $column the column to use 593 * 594 * @return string generated sql including an SQRT aggregate function 595 */ 596 public function getSqrtExpression($column) 597 { 598 return 'SQRT(' . $column . ')'; 599 } 600 601 /** 602 * Rounds a numeric field to the number of decimals specified. 603 * 604 * @param string $column 605 * @param integer $decimals 606 * 607 * @return string 608 */ 609 public function getRoundExpression($column, $decimals = 0) 610 { 611 return 'ROUND(' . $column . ', ' . $decimals . ')'; 612 } 613 614 /** 615 * Returns the remainder of the division operation 616 * $expression1 / $expression2. 617 * 618 * @param string $expression1 619 * @param string $expression2 620 * 621 * @return string 622 */ 623 public function getModExpression($expression1, $expression2) 624 { 625 return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; 626 } 627 628 /** 629 * Trim a string, leading/trailing/both and with a given char which defaults to space. 630 * 631 * @param string $str 632 * @param integer $pos 633 * @param string $char has to be quoted already 634 * 635 * @return string 636 */ 637 public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) 638 { 639 $posStr = ''; 640 $trimChar = ($char != false) ? $char . ' FROM ' : ''; 641 642 switch ($pos) { 643 case self::TRIM_LEADING: 644 $posStr = 'LEADING '.$trimChar; 645 break; 646 647 case self::TRIM_TRAILING: 648 $posStr = 'TRAILING '.$trimChar; 649 break; 650 651 case self::TRIM_BOTH: 652 $posStr = 'BOTH '.$trimChar; 653 break; 654 } 655 656 return 'TRIM(' . $posStr . $str . ')'; 657 } 658 659 /** 660 * rtrim 661 * returns the string $str with proceeding space characters removed 662 * 663 * @param string $str literal string or column name 664 * 665 * @return string 666 */ 667 public function getRtrimExpression($str) 668 { 669 return 'RTRIM(' . $str . ')'; 670 } 671 672 /** 673 * ltrim 674 * returns the string $str with leading space characters removed 675 * 676 * @param string $str literal string or column name 677 * 678 * @return string 679 */ 680 public function getLtrimExpression($str) 681 { 682 return 'LTRIM(' . $str . ')'; 683 } 684 685 /** 686 * upper 687 * Returns the string $str with all characters changed to 688 * uppercase according to the current character set mapping. 689 * 690 * @param string $str literal string or column name 691 * 692 * @return string 693 */ 694 public function getUpperExpression($str) 695 { 696 return 'UPPER(' . $str . ')'; 697 } 698 699 /** 700 * lower 701 * Returns the string $str with all characters changed to 702 * lowercase according to the current character set mapping. 703 * 704 * @param string $str literal string or column name 705 * 706 * @return string 707 */ 708 public function getLowerExpression($str) 709 { 710 return 'LOWER(' . $str . ')'; 711 } 712 713 /** 714 * returns the position of the first occurrence of substring $substr in string $str 715 * 716 * @param string $str literal string 717 * @param string $substr literal string to find 718 * @param integer $startPos position to start at, beginning of string by default 719 * 720 * @return string 721 */ 722 public function getLocateExpression($str, $substr, $startPos = false) 723 { 724 throw DBALException::notSupported(__METHOD__); 725 } 726 727 /** 728 * Returns the current system date. 729 * 730 * @return string 731 */ 732 public function getNowExpression() 733 { 734 return 'NOW()'; 735 } 736 737 /** 738 * return string to call a function to get a substring inside an SQL statement 739 * 740 * Note: Not SQL92, but common functionality. 741 * 742 * SQLite only supports the 2 parameter variant of this function 743 * 744 * @param string $value an sql string literal or column name/alias 745 * @param integer $from where to start the substring portion 746 * @param integer $length the substring portion length 747 * 748 * @return string 749 */ 750 public function getSubstringExpression($value, $from, $length = null) 751 { 752 if ($length === null) { 753 return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; 754 } 755 756 return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; 757 } 758 759 /** 760 * Returns a series of strings concatinated 761 * 762 * concat() accepts an arbitrary number of parameters. Each parameter 763 * must contain an expression 764 * 765 * @param string $arg1, $arg2 ... $argN strings that will be concatenated. 766 * 767 * @return string 768 */ 769 public function getConcatExpression() 770 { 771 return join(' || ' , func_get_args()); 772 } 773 774 /** 775 * Returns the SQL for a logical not. 776 * 777 * Example: 778 * <code> 779 * $q = new Doctrine_Query(); 780 * $e = $q->expr; 781 * $q->select('*')->from('table') 782 * ->where($e->eq('id', $e->not('null')); 783 * </code> 784 * 785 * @param string $expression 786 * 787 * @return string a logical expression 788 */ 789 public function getNotExpression($expression) 790 { 791 return 'NOT(' . $expression . ')'; 792 } 793 794 /** 795 * Returns the SQL to check if a value is one in a set of 796 * given values. 797 * 798 * in() accepts an arbitrary number of parameters. The first parameter 799 * must always specify the value that should be matched against. Successive 800 * must contain a logical expression or an array with logical expressions. 801 * These expressions will be matched against the first parameter. 802 * 803 * @param string $column the value that should be matched against 804 * @param string|array<string> $values values that will be matched against $column 805 * 806 * @return string logical expression 807 */ 808 public function getInExpression($column, $values) 809 { 810 if ( ! is_array($values)) { 811 $values = array($values); 812 } 813 814 // TODO: fix this code: the method does not exist 815 $values = $this->getIdentifiers($values); 816 817 if (count($values) == 0) { 818 throw new \InvalidArgumentException('Values must not be empty.'); 819 } 820 821 return $column . ' IN (' . implode(', ', $values) . ')'; 822 } 823 824 /** 825 * Returns SQL that checks if a expression is null. 826 * 827 * @param string $expression the expression that should be compared to null 828 * 829 * @return string logical expression 830 */ 831 public function getIsNullExpression($expression) 832 { 833 return $expression . ' IS NULL'; 834 } 835 836 /** 837 * Returns SQL that checks if a expression is not null. 838 * 839 * @param string $expression the expression that should be compared to null 840 * 841 * @return string logical expression 842 */ 843 public function getIsNotNullExpression($expression) 844 { 845 return $expression . ' IS NOT NULL'; 846 } 847 848 /** 849 * Returns SQL that checks if an expression evaluates to a value between 850 * two values. 851 * 852 * The parameter $expression is checked if it is between $value1 and $value2. 853 * 854 * Note: There is a slight difference in the way BETWEEN works on some databases. 855 * http://www.w3schools.com/sql/sql_between.asp. If you want complete database 856 * independence you should avoid using between(). 857 * 858 * @param string $expression the value to compare to 859 * @param string $value1 the lower value to compare with 860 * @param string $value2 the higher value to compare with 861 * 862 * @return string logical expression 863 */ 864 public function getBetweenExpression($expression, $value1, $value2) 865 { 866 return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; 867 } 868 869 public function getAcosExpression($value) 870 { 871 return 'ACOS(' . $value . ')'; 872 } 873 874 public function getSinExpression($value) 875 { 876 return 'SIN(' . $value . ')'; 877 } 878 879 public function getPiExpression() 880 { 881 return 'PI()'; 882 } 883 884 public function getCosExpression($value) 885 { 886 return 'COS(' . $value . ')'; 887 } 888 889 /** 890 * Calculate the difference in days between the two passed dates. 891 * 892 * Computes diff = date1 - date2 893 * 894 * @param string $date1 895 * @param string $date2 896 * 897 * @return string 898 */ 899 public function getDateDiffExpression($date1, $date2) 900 { 901 throw DBALException::notSupported(__METHOD__); 902 } 903 904 /** 905 * Add the number of given days to a date. 906 * 907 * @param string $date 908 * @param integer $days 909 * 910 * @return string 911 */ 912 public function getDateAddDaysExpression($date, $days) 913 { 914 throw DBALException::notSupported(__METHOD__); 915 } 916 917 /** 918 * Substract the number of given days to a date. 919 * 920 * @param string $date 921 * @param integer $days 922 * 923 * @return string 924 */ 925 public function getDateSubDaysExpression($date, $days) 926 { 927 throw DBALException::notSupported(__METHOD__); 928 } 929 930 /** 931 * Add the number of given months to a date. 932 * 933 * @param string $date 934 * @param integer $months 935 * 936 * @return string 937 */ 938 public function getDateAddMonthExpression($date, $months) 939 { 940 throw DBALException::notSupported(__METHOD__); 941 } 942 943 /** 944 * Substract the number of given months to a date. 945 * 946 * @param string $date 947 * @param integer $months 948 * 949 * @return string 950 */ 951 public function getDateSubMonthExpression($date, $months) 952 { 953 throw DBALException::notSupported(__METHOD__); 954 } 955 956 /** 957 * Gets SQL bit AND comparison expression 958 * 959 * @param string $value1 960 * @param string $value2 961 * 962 * @return string 963 */ 964 public function getBitAndComparisonExpression($value1, $value2) 965 { 966 return '(' . $value1 . ' & ' . $value2 . ')'; 967 } 968 969 /** 970 * Gets SQL bit OR comparison expression 971 * 972 * @param string $value1 973 * @param string $value2 974 * 975 * @return string 976 */ 977 public function getBitOrComparisonExpression($value1, $value2) 978 { 979 return '(' . $value1 . ' | ' . $value2 . ')'; 980 } 981 982 public function getForUpdateSQL() 983 { 984 return 'FOR UPDATE'; 985 } 986 987 /** 988 * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification. 989 * 990 * @param string $fromClause 991 * @param integer $lockMode 992 * 993 * @return string 994 */ 995 public function appendLockHint($fromClause, $lockMode) 996 { 997 return $fromClause; 998 } 999 1000 /** 1001 * Get the sql snippet to append to any SELECT statement which locks rows in shared read lock. 1002 * 1003 * This defaults to the ASNI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database 1004 * vendors allow to lighten this constraint up to be a real read lock. 1005 * 1006 * @return string 1007 */ 1008 public function getReadLockSQL() 1009 { 1010 return $this->getForUpdateSQL(); 1011 } 1012 1013 /** 1014 * Get the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. 1015 * 1016 * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ASNI SQL standard. 1017 * 1018 * @return string 1019 */ 1020 public function getWriteLockSQL() 1021 { 1022 return $this->getForUpdateSQL(); 1023 } 1024 1025 /** 1026 * Get the SQL snippet to drop an existing database 1027 * 1028 * @param string $database name of the database that should be dropped 1029 * 1030 * @return string 1031 */ 1032 public function getDropDatabaseSQL($database) 1033 { 1034 return 'DROP DATABASE ' . $database; 1035 } 1036 1037 /** 1038 * Drop a Table 1039 * 1040 * @throws \InvalidArgumentException 1041 * 1042 * @param Table|string $table 1043 * 1044 * @return string 1045 */ 1046 public function getDropTableSQL($table) 1047 { 1048 $tableArg = $table; 1049 1050 if ($table instanceof Table) { 1051 $table = $table->getQuotedName($this); 1052 } else if(!is_string($table)) { 1053 throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); 1054 } 1055 1056 if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { 1057 $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); 1058 $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); 1059 1060 if ($eventArgs->isDefaultPrevented()) { 1061 return $eventArgs->getSql(); 1062 } 1063 } 1064 1065 return 'DROP TABLE ' . $table; 1066 } 1067 1068 /** 1069 * Get SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. 1070 * 1071 * @param Table|string $table 1072 * 1073 * @return string 1074 */ 1075 public function getDropTemporaryTableSQL($table) 1076 { 1077 return $this->getDropTableSQL($table); 1078 } 1079 1080 /** 1081 * Drop index from a table 1082 * 1083 * @param Index|string $name 1084 * @param string|Table $table 1085 * 1086 * @return string 1087 */ 1088 public function getDropIndexSQL($index, $table = null) 1089 { 1090 if ($index instanceof Index) { 1091 $index = $index->getQuotedName($this); 1092 } else if(!is_string($index)) { 1093 throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); 1094 } 1095 1096 return 'DROP INDEX ' . $index; 1097 } 1098 1099 /** 1100 * Get drop constraint sql 1101 * 1102 * @param \Doctrine\DBAL\Schema\Constraint $constraint 1103 * @param string|Table $table 1104 * 1105 * @return string 1106 */ 1107 public function getDropConstraintSQL($constraint, $table) 1108 { 1109 if ($constraint instanceof Constraint) { 1110 $constraint = $constraint->getQuotedName($this); 1111 } 1112 1113 if ($table instanceof Table) { 1114 $table = $table->getQuotedName($this); 1115 } 1116 1117 return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; 1118 } 1119 1120 /** 1121 * @param ForeignKeyConstraint|string $foreignKey 1122 * @param Table|string $table 1123 * 1124 * @return string 1125 */ 1126 public function getDropForeignKeySQL($foreignKey, $table) 1127 { 1128 if ($foreignKey instanceof ForeignKeyConstraint) { 1129 $foreignKey = $foreignKey->getQuotedName($this); 1130 } 1131 1132 if ($table instanceof Table) { 1133 $table = $table->getQuotedName($this); 1134 } 1135 1136 return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; 1137 } 1138 1139 /** 1140 * Gets the SQL statement(s) to create a table with the specified name, columns and constraints 1141 * on this platform. 1142 * 1143 * @param string $table The name of the table. 1144 * @param integer $createFlags 1145 * 1146 * @return array The sequence of SQL statements. 1147 */ 1148 public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) 1149 { 1150 if ( ! is_int($createFlags)) { 1151 throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer."); 1152 } 1153 1154 if (count($table->getColumns()) === 0) { 1155 throw DBALException::noColumnsSpecifiedForTable($table->getName()); 1156 } 1157 1158 $tableName = $table->getQuotedName($this); 1159 $options = $table->getOptions(); 1160 $options['uniqueConstraints'] = array(); 1161 $options['indexes'] = array(); 1162 $options['primary'] = array(); 1163 1164 if (($createFlags&self::CREATE_INDEXES) > 0) { 1165 foreach ($table->getIndexes() as $index) { 1166 /* @var $index Index */ 1167 if ($index->isPrimary()) { 1168 $platform = $this; 1169 $options['primary'] = array_map(function ($columnName) use ($table, $platform) { 1170 return $table->getColumn($columnName)->getQuotedName($platform); 1171 }, $index->getColumns()); 1172 $options['primary_index'] = $index; 1173 } else { 1174 $options['indexes'][$index->getName()] = $index; 1175 } 1176 } 1177 } 1178 1179 $columnSql = array(); 1180 $columns = array(); 1181 1182 foreach ($table->getColumns() as $column) { 1183 /* @var \Doctrine\DBAL\Schema\Column $column */ 1184 1185 if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) { 1186 $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); 1187 $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); 1188 1189 $columnSql = array_merge($columnSql, $eventArgs->getSql()); 1190 1191 if ($eventArgs->isDefaultPrevented()) { 1192 continue; 1193 } 1194 } 1195 1196 $columnData = array(); 1197 $columnData['name'] = $column->getQuotedName($this); 1198 $columnData['type'] = $column->getType(); 1199 $columnData['length'] = $column->getLength(); 1200 $columnData['notnull'] = $column->getNotNull(); 1201 $columnData['fixed'] = $column->getFixed(); 1202 $columnData['unique'] = false; // TODO: what do we do about this? 1203 $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false; 1204 1205 if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) { 1206 $columnData['length'] = 255; 1207 } 1208 1209 $columnData['unsigned'] = $column->getUnsigned(); 1210 $columnData['precision'] = $column->getPrecision(); 1211 $columnData['scale'] = $column->getScale(); 1212 $columnData['default'] = $column->getDefault(); 1213 $columnData['columnDefinition'] = $column->getColumnDefinition(); 1214 $columnData['autoincrement'] = $column->getAutoincrement(); 1215 $columnData['comment'] = $this->getColumnComment($column); 1216 1217 if (in_array($column->getName(), $options['primary'])) { 1218 $columnData['primary'] = true; 1219 } 1220 1221 $columns[$columnData['name']] = $columnData; 1222 } 1223 1224 if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { 1225 $options['foreignKeys'] = array(); 1226 foreach ($table->getForeignKeys() as $fkConstraint) { 1227 $options['foreignKeys'][] = $fkConstraint; 1228 } 1229 } 1230 1231 if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { 1232 $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); 1233 $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); 1234 1235 if ($eventArgs->isDefaultPrevented()) { 1236 return array_merge($eventArgs->getSql(), $columnSql); 1237 } 1238 } 1239 1240 $sql = $this->_getCreateTableSQL($tableName, $columns, $options); 1241 if ($this->supportsCommentOnStatement()) { 1242 foreach ($table->getColumns() as $column) { 1243 if ($this->getColumnComment($column)) { 1244 $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getName(), $this->getColumnComment($column)); 1245 } 1246 } 1247 } 1248 1249 return array_merge($sql, $columnSql); 1250 } 1251 1252 public function getCommentOnColumnSQL($tableName, $columnName, $comment) 1253 { 1254 return "COMMENT ON COLUMN " . $tableName . "." . $columnName . " IS '" . $comment . "'"; 1255 } 1256 1257 /** 1258 * Gets the SQL used to create a table. 1259 * 1260 * @param string $tableName 1261 * @param array $columns 1262 * @param array $options 1263 * 1264 * @return array 1265 */ 1266 protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) 1267 { 1268 $columnListSql = $this->getColumnDeclarationListSQL($columns); 1269 1270 if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { 1271 foreach ($options['uniqueConstraints'] as $name => $definition) { 1272 $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); 1273 } 1274 } 1275 1276 if (isset($options['primary']) && ! empty($options['primary'])) { 1277 $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; 1278 } 1279 1280 if (isset($options['indexes']) && ! empty($options['indexes'])) { 1281 foreach($options['indexes'] as $index => $definition) { 1282 $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); 1283 } 1284 } 1285 1286 $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; 1287 1288 $check = $this->getCheckDeclarationSQL($columns); 1289 if ( ! empty($check)) { 1290 $query .= ', ' . $check; 1291 } 1292 $query .= ')'; 1293 1294 $sql[] = $query; 1295 1296 if (isset($options['foreignKeys'])) { 1297 foreach ((array) $options['foreignKeys'] as $definition) { 1298 $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); 1299 } 1300 } 1301 1302 return $sql; 1303 } 1304 1305 public function getCreateTemporaryTableSnippetSQL() 1306 { 1307 return "CREATE TEMPORARY TABLE"; 1308 } 1309 1310 /** 1311 * Gets the SQL to create a sequence on this platform. 1312 * 1313 * @param \Doctrine\DBAL\Schema\Sequence $sequence 1314 * 1315 * @return string 1316 * 1317 * @throws DBALException 1318 */ 1319 public function getCreateSequenceSQL(Sequence $sequence) 1320 { 1321 throw DBALException::notSupported(__METHOD__); 1322 } 1323 1324 /** 1325 * Gets the SQL statement to change a sequence on this platform. 1326 * 1327 * @param \Doctrine\DBAL\Schema\Sequence $sequence 1328 * 1329 * @return string 1330 */ 1331 public function getAlterSequenceSQL(Sequence $sequence) 1332 { 1333 throw DBALException::notSupported(__METHOD__); 1334 } 1335 1336 /** 1337 * Gets the SQL to create a constraint on a table on this platform. 1338 * 1339 * @param \Doctrine\DBAL\Schema\Constraint $constraint 1340 * @param string|Table $table 1341 * 1342 * @return string 1343 */ 1344 public function getCreateConstraintSQL(Constraint $constraint, $table) 1345 { 1346 if ($table instanceof Table) { 1347 $table = $table->getQuotedName($this); 1348 } 1349 1350 $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); 1351 1352 $columns = array(); 1353 foreach ($constraint->getColumns() as $column) { 1354 $columns[] = $column; 1355 } 1356 $columnList = '('. implode(', ', $columns) . ')'; 1357 1358 $referencesClause = ''; 1359 if ($constraint instanceof Index) { 1360 if($constraint->isPrimary()) { 1361 $query .= ' PRIMARY KEY'; 1362 } elseif ($constraint->isUnique()) { 1363 $query .= ' UNIQUE'; 1364 } else { 1365 throw new \InvalidArgumentException( 1366 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' 1367 ); 1368 } 1369 } else if ($constraint instanceof ForeignKeyConstraint) { 1370 $query .= ' FOREIGN KEY'; 1371 1372 $foreignColumns = array(); 1373 foreach ($constraint->getForeignColumns() as $column) { 1374 $foreignColumns[] = $column; 1375 } 1376 1377 $referencesClause = ' REFERENCES '.$constraint->getForeignTableName(). ' ('.implode(', ', $foreignColumns).')'; 1378 } 1379 $query .= ' '.$columnList.$referencesClause; 1380 1381 return $query; 1382 } 1383 1384 /** 1385 * Gets the SQL to create an index on a table on this platform. 1386 * 1387 * @param Index $index 1388 * @param string|Table $table name of the table on which the index is to be created 1389 * 1390 * @return string 1391 */ 1392 public function getCreateIndexSQL(Index $index, $table) 1393 { 1394 if ($table instanceof Table) { 1395 $table = $table->getQuotedName($this); 1396 } 1397 $name = $index->getQuotedName($this); 1398 $columns = $index->getColumns(); 1399 1400 if (count($columns) == 0) { 1401 throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); 1402 } 1403 1404 if ($index->isPrimary()) { 1405 return $this->getCreatePrimaryKeySQL($index, $table); 1406 } 1407 1408 $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; 1409 $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')'; 1410 1411 return $query; 1412 } 1413 1414 /** 1415 * Adds additional flags for index generation 1416 * 1417 * @param Index $index 1418 * 1419 * @return string 1420 */ 1421 protected function getCreateIndexSQLFlags(Index $index) 1422 { 1423 return $index->isUnique() ? 'UNIQUE ' : ''; 1424 } 1425 1426 /** 1427 * Get SQL to create an unnamed primary key constraint. 1428 * 1429 * @param Index $index 1430 * @param string|Table $table 1431 * 1432 * @return string 1433 */ 1434 public function getCreatePrimaryKeySQL(Index $index, $table) 1435 { 1436 return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')'; 1437 } 1438 1439 /** 1440 * Quotes a string so that it can be safely used as a table or column name, 1441 * even if it is a reserved word of the platform. This also detects identifier 1442 * chains separated by dot and quotes them independently. 1443 * 1444 * NOTE: Just because you CAN use quoted identifiers doesn't mean 1445 * you SHOULD use them. In general, they end up causing way more 1446 * problems than they solve. 1447 * 1448 * @param string $str identifier name to be quoted 1449 * 1450 * @return string quoted identifier string 1451 */ 1452 public function quoteIdentifier($str) 1453 { 1454 if (strpos($str, ".") !== false) { 1455 $parts = array_map(array($this, "quoteIdentifier"), explode(".", $str)); 1456 1457 return implode(".", $parts); 1458 } 1459 1460 return $this->quoteSingleIdentifier($str); 1461 } 1462 1463 /** 1464 * Quote a single identifier (no dot chain separation) 1465 * 1466 * @param string $str 1467 * 1468 * @return string 1469 */ 1470 public function quoteSingleIdentifier($str) 1471 { 1472 $c = $this->getIdentifierQuoteCharacter(); 1473 1474 return $c . str_replace($c, $c.$c, $str) . $c; 1475 } 1476 1477 /** 1478 * Create a new foreign key 1479 * 1480 * @param ForeignKeyConstraint $foreignKey ForeignKey instance 1481 * @param string|Table $table name of the table on which the foreign key is to be created 1482 * 1483 * @return string 1484 */ 1485 public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) 1486 { 1487 if ($table instanceof Table) { 1488 $table = $table->getQuotedName($this); 1489 } 1490 1491 $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); 1492 1493 return $query; 1494 } 1495 1496 /** 1497 * Gets the sql statements for altering an existing table. 1498 * 1499 * The method returns an array of sql statements, since some platforms need several statements. 1500 * 1501 * @param TableDiff $diff 1502 * 1503 * @return array 1504 */ 1505 public function getAlterTableSQL(TableDiff $diff) 1506 { 1507 throw DBALException::notSupported(__METHOD__); 1508 } 1509 1510 /** 1511 * @param Column $column 1512 * @param TableDiff $diff 1513 * @param array $columnSql 1514 * 1515 * @return boolean 1516 */ 1517 protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) 1518 { 1519 if (null === $this->_eventManager) { 1520 return false; 1521 } 1522 1523 if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { 1524 return false; 1525 } 1526 1527 $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); 1528 $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); 1529 1530 $columnSql = array_merge($columnSql, $eventArgs->getSql()); 1531 1532 return $eventArgs->isDefaultPrevented(); 1533 } 1534 1535 /** 1536 * @param Column $column 1537 * @param TableDiff $diff 1538 * @param array $columnSql 1539 * 1540 * @return boolean 1541 */ 1542 protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) 1543 { 1544 if (null === $this->_eventManager) { 1545 return false; 1546 } 1547 1548 if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { 1549 return false; 1550 } 1551 1552 $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); 1553 $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); 1554 1555 $columnSql = array_merge($columnSql, $eventArgs->getSql()); 1556 1557 return $eventArgs->isDefaultPrevented(); 1558 } 1559 1560 /** 1561 * @param ColumnDiff $columnDiff 1562 * @param TableDiff $diff 1563 * @param array $columnSql 1564 * 1565 * @return boolean 1566 */ 1567 protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) 1568 { 1569 if (null === $this->_eventManager) { 1570 return false; 1571 } 1572 1573 if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { 1574 return false; 1575 } 1576 1577 $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); 1578 $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); 1579 1580 $columnSql = array_merge($columnSql, $eventArgs->getSql()); 1581 1582 return $eventArgs->isDefaultPrevented(); 1583 } 1584 1585 /** 1586 * @param string $oldColumnName 1587 * @param Column $column 1588 * @param TableDiff $diff 1589 * @param array $columnSql 1590 * 1591 * @return boolean 1592 */ 1593 protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) 1594 { 1595 if (null === $this->_eventManager) { 1596 return false; 1597 } 1598 1599 if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { 1600 return false; 1601 } 1602 1603 $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); 1604 $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); 1605 1606 $columnSql = array_merge($columnSql, $eventArgs->getSql()); 1607 1608 return $eventArgs->isDefaultPrevented(); 1609 } 1610 1611 /** 1612 * @param TableDiff $diff 1613 * @param array $sql 1614 * 1615 * @return boolean 1616 */ 1617 protected function onSchemaAlterTable(TableDiff $diff, &$sql) 1618 { 1619 if (null === $this->_eventManager) { 1620 return false; 1621 } 1622 1623 if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { 1624 return false; 1625 } 1626 1627 $eventArgs = new SchemaAlterTableEventArgs($diff, $this); 1628 $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); 1629 1630 $sql = array_merge($sql, $eventArgs->getSql()); 1631 1632 return $eventArgs->isDefaultPrevented(); 1633 } 1634 1635 protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) 1636 { 1637 $tableName = $diff->name; 1638 1639 $sql = array(); 1640 if ($this->supportsForeignKeyConstraints()) { 1641 foreach ($diff->removedForeignKeys as $foreignKey) { 1642 $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); 1643 } 1644 foreach ($diff->changedForeignKeys as $foreignKey) { 1645 $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); 1646 } 1647 } 1648 1649 foreach ($diff->removedIndexes as $index) { 1650 $sql[] = $this->getDropIndexSQL($index, $tableName); 1651 } 1652 foreach ($diff->changedIndexes as $index) { 1653 $sql[] = $this->getDropIndexSQL($index, $tableName); 1654 } 1655 1656 return $sql; 1657 } 1658 1659 protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) 1660 { 1661 $tableName = false !== $diff->newName ? $diff->newName : $diff->name; 1662 1663 $sql = array(); 1664 if ($this->supportsForeignKeyConstraints()) { 1665 foreach ($diff->addedForeignKeys as $foreignKey) { 1666 $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); 1667 } 1668 foreach ($diff->changedForeignKeys as $foreignKey) { 1669 $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); 1670 } 1671 } 1672 1673 foreach ($diff->addedIndexes as $index) { 1674 $sql[] = $this->getCreateIndexSQL($index, $tableName); 1675 } 1676 foreach ($diff->changedIndexes as $index) { 1677 $sql[] = $this->getCreateIndexSQL($index, $tableName); 1678 } 1679 1680 return $sql; 1681 } 1682 1683 /** 1684 * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. 1685 * 1686 * @param TableDiff $diff 1687 * 1688 * @return array 1689 */ 1690 protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) 1691 { 1692 return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); 1693 } 1694 1695 /** 1696 * Get declaration of a number of fields in bulk 1697 * 1698 * @param array $fields a multidimensional associative array. 1699 * The first dimension determines the field name, while the second 1700 * dimension is keyed with the name of the properties 1701 * of the field being declared as array indexes. Currently, the types 1702 * of supported field properties are as follows: 1703 * 1704 * length 1705 * Integer value that determines the maximum length of the text 1706 * field. If this argument is missing the field should be 1707 * declared to have the longest length allowed by the DBMS. 1708 * 1709 * default 1710 * Text value to be used as default for this field. 1711 * 1712 * notnull 1713 * Boolean flag that indicates whether this field is constrained 1714 * to not be set to null. 1715 * charset 1716 * Text value with the default CHARACTER SET for this field. 1717 * collation 1718 * Text value with the default COLLATION for this field. 1719 * unique 1720 * unique constraint 1721 * 1722 * @return string 1723 */ 1724 public function getColumnDeclarationListSQL(array $fields) 1725 { 1726 $queryFields = array(); 1727 1728 foreach ($fields as $fieldName => $field) { 1729 $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field); 1730 } 1731 1732 return implode(', ', $queryFields); 1733 } 1734 1735 /** 1736 * Obtain DBMS specific SQL code portion needed to declare a generic type 1737 * field to be used in statements like CREATE TABLE. 1738 * 1739 * @param string $name name the field to be declared. 1740 * @param array $field associative array with the name of the properties 1741 * of the field being declared as array indexes. Currently, the types 1742 * of supported field properties are as follows: 1743 * 1744 * length 1745 * Integer value that determines the maximum length of the text 1746 * field. If this argument is missing the field should be 1747 * declared to have the longest length allowed by the DBMS. 1748 * 1749 * default 1750 * Text value to be used as default for this field. 1751 * 1752 * notnull 1753 * Boolean flag that indicates whether this field is constrained 1754 * to not be set to null. 1755 * charset 1756 * Text value with the default CHARACTER SET for this field. 1757 * collation 1758 * Text value with the default COLLATION for this field. 1759 * unique 1760 * unique constraint 1761 * check 1762 * column check constraint 1763 * columnDefinition 1764 * a string that defines the complete column 1765 * 1766 * @return string DBMS specific SQL code portion that should be used to declare the column. 1767 */ 1768 public function getColumnDeclarationSQL($name, array $field) 1769 { 1770 if (isset($field['columnDefinition'])) { 1771 $columnDef = $this->getCustomTypeDeclarationSQL($field); 1772 } else { 1773 $default = $this->getDefaultValueDeclarationSQL($field); 1774 1775 $charset = (isset($field['charset']) && $field['charset']) ? 1776 ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : ''; 1777 1778 $collation = (isset($field['collation']) && $field['collation']) ? 1779 ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; 1780 1781 $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; 1782 1783 $unique = (isset($field['unique']) && $field['unique']) ? 1784 ' ' . $this->getUniqueFieldDeclarationSQL() : ''; 1785 1786 $check = (isset($field['check']) && $field['check']) ? 1787 ' ' . $field['check'] : ''; 1788 1789 $typeDecl = $field['type']->getSqlDeclaration($field, $this); 1790 $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; 1791 } 1792 1793 if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment']) { 1794 $columnDef .= " COMMENT '" . $field['comment'] . "'"; 1795 } 1796 1797 return $name . ' ' . $columnDef; 1798 } 1799 1800 /** 1801 * Gets the SQL snippet that declares a floating point column of arbitrary precision. 1802 * 1803 * @param array $columnDef 1804 * 1805 * @return string 1806 */ 1807 public function getDecimalTypeDeclarationSQL(array $columnDef) 1808 { 1809 $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision'])) 1810 ? 10 : $columnDef['precision']; 1811 $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale'])) 1812 ? 0 : $columnDef['scale']; 1813 1814 return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')'; 1815 } 1816 1817 /** 1818 * Obtain DBMS specific SQL code portion needed to set a default value 1819 * declaration to be used in statements like CREATE TABLE. 1820 * 1821 * @param array $field field definition array 1822 * 1823 * @return string DBMS specific SQL code portion needed to set a default value 1824 */ 1825 public function getDefaultValueDeclarationSQL($field) 1826 { 1827 $default = empty($field['notnull']) ? ' DEFAULT NULL' : ''; 1828 1829 if (isset($field['default'])) { 1830 $default = " DEFAULT '".$field['default']."'"; 1831 if (isset($field['type'])) { 1832 if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { 1833 $default = " DEFAULT ".$field['default']; 1834 } else if ((string)$field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) { 1835 $default = " DEFAULT ".$this->getCurrentTimestampSQL(); 1836 } else if ((string) $field['type'] == 'Boolean') { 1837 $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; 1838 } 1839 } 1840 } 1841 return $default; 1842 } 1843 1844 /** 1845 * Obtain DBMS specific SQL code portion needed to set a CHECK constraint 1846 * declaration to be used in statements like CREATE TABLE. 1847 * 1848 * @param array $definition check definition 1849 * 1850 * @return string DBMS specific SQL code portion needed to set a CHECK constraint 1851 */ 1852 public function getCheckDeclarationSQL(array $definition) 1853 { 1854 $constraints = array(); 1855 foreach ($definition as $field => $def) { 1856 if (is_string($def)) { 1857 $constraints[] = 'CHECK (' . $def . ')'; 1858 } else { 1859 if (isset($def['min'])) { 1860 $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; 1861 } 1862 1863 if (isset($def['max'])) { 1864 $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; 1865 } 1866 } 1867 } 1868 1869 return implode(', ', $constraints); 1870 } 1871 1872 /** 1873 * Obtain DBMS specific SQL code portion needed to set a unique 1874 * constraint declaration to be used in statements like CREATE TABLE. 1875 * 1876 * @param string $name name of the unique constraint 1877 * @param Index $index index definition 1878 * 1879 * @return string DBMS specific SQL code portion needed 1880 * to set a constraint 1881 */ 1882 public function getUniqueConstraintDeclarationSQL($name, Index $index) 1883 { 1884 if (count($index->getColumns()) === 0) { 1885 throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); 1886 } 1887 1888 return 'CONSTRAINT ' . $name . ' UNIQUE (' 1889 . $this->getIndexFieldDeclarationListSQL($index->getColumns()) 1890 . ')'; 1891 } 1892 1893 /** 1894 * Obtain DBMS specific SQL code portion needed to set an index 1895 * declaration to be used in statements like CREATE TABLE. 1896 * 1897 * @param string $name name of the index 1898 * @param Index $index index definition 1899 * 1900 * @return string DBMS specific SQL code portion needed to set an index 1901 */ 1902 public function getIndexDeclarationSQL($name, Index $index) 1903 { 1904 $type = ''; 1905 1906 if ($index->isUnique()) { 1907 $type = 'UNIQUE '; 1908 } 1909 1910 if (count($index->getColumns()) === 0) { 1911 throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); 1912 } 1913 1914 return $type . 'INDEX ' . $name . ' (' 1915 . $this->getIndexFieldDeclarationListSQL($index->getColumns()) 1916 . ')'; 1917 } 1918 1919 /** 1920 * getCustomTypeDeclarationSql 1921 * Obtail SQL code portion needed to create a custom column, 1922 * e.g. when a field has the "columnDefinition" keyword. 1923 * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. 1924 * 1925 * @param array $columnDef 1926 * 1927 * @return string 1928 */ 1929 public function getCustomTypeDeclarationSQL(array $columnDef) 1930 { 1931 return $columnDef['columnDefinition']; 1932 } 1933 1934 /** 1935 * getIndexFieldDeclarationList 1936 * Obtain DBMS specific SQL code portion needed to set an index 1937 * declaration to be used in statements like CREATE TABLE. 1938 * 1939 * @param array $fields 1940 * 1941 * @return string 1942 */ 1943 public function getIndexFieldDeclarationListSQL(array $fields) 1944 { 1945 $ret = array(); 1946 1947 foreach ($fields as $field => $definition) { 1948 if (is_array($definition)) { 1949 $ret[] = $field; 1950 } else { 1951 $ret[] = $definition; 1952 } 1953 } 1954 1955 return implode(', ', $ret); 1956 } 1957 1958 /** 1959 * A method to return the required SQL string that fits between CREATE ... TABLE 1960 * to create the table as a temporary table. 1961 * 1962 * Should be overridden in driver classes to return the correct string for the 1963 * specific database type. 1964 * 1965 * The default is to return the string "TEMPORARY" - this will result in a 1966 * SQL error for any database that does not support temporary tables, or that 1967 * requires a different SQL command from "CREATE TEMPORARY TABLE". 1968 * 1969 * @return string The string required to be placed between "CREATE" and "TABLE" 1970 * to generate a temporary table, if possible. 1971 */ 1972 public function getTemporaryTableSQL() 1973 { 1974 return 'TEMPORARY'; 1975 } 1976 1977 /** 1978 * Some vendors require temporary table names to be qualified specially. 1979 * 1980 * @param string $tableName 1981 * 1982 * @return string 1983 */ 1984 public function getTemporaryTableName($tableName) 1985 { 1986 return $tableName; 1987 } 1988 1989 /** 1990 * Get sql query to show a list of database. 1991 * 1992 * @return string 1993 */ 1994 public function getShowDatabasesSQL() 1995 { 1996 throw DBALException::notSupported(__METHOD__); 1997 } 1998 1999 /** 2000 * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint 2001 * of a field declaration to be used in statements like CREATE TABLE. 2002 * 2003 * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey 2004 * 2005 * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint 2006 * of a field declaration. 2007 */ 2008 public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) 2009 { 2010 $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); 2011 $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); 2012 2013 return $sql; 2014 } 2015 2016 /** 2017 * Return the FOREIGN KEY query section dealing with non-standard options 2018 * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... 2019 * 2020 * @param ForeignKeyConstraint $foreignKey foreign key definition 2021 * 2022 * @return string 2023 */ 2024 public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) 2025 { 2026 $query = ''; 2027 if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) { 2028 $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); 2029 } 2030 if ($foreignKey->hasOption('onDelete')) { 2031 $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); 2032 } 2033 return $query; 2034 } 2035 2036 /** 2037 * returns given referential action in uppercase if valid, otherwise throws 2038 * an exception 2039 * 2040 * @throws \InvalidArgumentException if unknown referential action given 2041 * 2042 * @param string $action foreign key referential action 2043 * 2044 * @return string 2045 */ 2046 public function getForeignKeyReferentialActionSQL($action) 2047 { 2048 $upper = strtoupper($action); 2049 switch ($upper) { 2050 case 'CASCADE': 2051 case 'SET NULL': 2052 case 'NO ACTION': 2053 case 'RESTRICT': 2054 case 'SET DEFAULT': 2055 return $upper; 2056 default: 2057 throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper); 2058 } 2059 } 2060 2061 /** 2062 * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint 2063 * of a field declaration to be used in statements like CREATE TABLE. 2064 * 2065 * @param ForeignKeyConstraint $foreignKey 2066 * 2067 * @return string 2068 */ 2069 public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) 2070 { 2071 $sql = ''; 2072 if (strlen($foreignKey->getName())) { 2073 $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; 2074 } 2075 $sql .= 'FOREIGN KEY ('; 2076 2077 if (count($foreignKey->getLocalColumns()) === 0) { 2078 throw new \InvalidArgumentException("Incomplete definition. 'local' required."); 2079 } 2080 if (count($foreignKey->getForeignColumns()) === 0) { 2081 throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); 2082 } 2083 if (strlen($foreignKey->getForeignTableName()) === 0) { 2084 throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); 2085 } 2086 2087 $sql .= implode(', ', $foreignKey->getLocalColumns()) 2088 . ') REFERENCES ' 2089 . $foreignKey->getQuotedForeignTableName($this) . ' (' 2090 . implode(', ', $foreignKey->getForeignColumns()) . ')'; 2091 2092 return $sql; 2093 } 2094 2095 /** 2096 * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint 2097 * of a field declaration to be used in statements like CREATE TABLE. 2098 * 2099 * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint 2100 * of a field declaration. 2101 */ 2102 public function getUniqueFieldDeclarationSQL() 2103 { 2104 return 'UNIQUE'; 2105 } 2106 2107 /** 2108 * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET 2109 * of a field declaration to be used in statements like CREATE TABLE. 2110 * 2111 * @param string $charset name of the charset 2112 * 2113 * @return string DBMS specific SQL code portion needed to set the CHARACTER SET 2114 * of a field declaration. 2115 */ 2116 public function getColumnCharsetDeclarationSQL($charset) 2117 { 2118 return ''; 2119 } 2120 2121 /** 2122 * Obtain DBMS specific SQL code portion needed to set the COLLATION 2123 * of a field declaration to be used in statements like CREATE TABLE. 2124 * 2125 * @param string $collation name of the collation 2126 * 2127 * @return string DBMS specific SQL code portion needed to set the COLLATION 2128 * of a field declaration. 2129 */ 2130 public function getColumnCollationDeclarationSQL($collation) 2131 { 2132 return ''; 2133 } 2134 2135 /** 2136 * Whether the platform prefers sequences for ID generation. 2137 * Subclasses should override this method to return TRUE if they prefer sequences. 2138 * 2139 * @return boolean 2140 */ 2141 public function prefersSequences() 2142 { 2143 return false; 2144 } 2145 2146 /** 2147 * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. 2148 * Subclasses should override this method to return TRUE if they prefer identity columns. 2149 * 2150 * @return boolean 2151 */ 2152 public function prefersIdentityColumns() 2153 { 2154 return false; 2155 } 2156 2157 /** 2158 * Some platforms need the boolean values to be converted. 2159 * 2160 * The default conversion in this implementation converts to integers (false => 0, true => 1). 2161 * 2162 * @param mixed $item 2163 * 2164 * @return mixed 2165 */ 2166 public function convertBooleans($item) 2167 { 2168 if (is_array($item)) { 2169 foreach ($item as $k => $value) { 2170 if (is_bool($value)) { 2171 $item[$k] = (int) $value; 2172 } 2173 } 2174 } else if (is_bool($item)) { 2175 $item = (int) $item; 2176 } 2177 2178 return $item; 2179 } 2180 2181 /** 2182 * Gets the SQL specific for the platform to get the current date. 2183 * 2184 * @return string 2185 */ 2186 public function getCurrentDateSQL() 2187 { 2188 return 'CURRENT_DATE'; 2189 } 2190 2191 /** 2192 * Gets the SQL specific for the platform to get the current time. 2193 * 2194 * @return string 2195 */ 2196 public function getCurrentTimeSQL() 2197 { 2198 return 'CURRENT_TIME'; 2199 } 2200 2201 /** 2202 * Gets the SQL specific for the platform to get the current timestamp 2203 * 2204 * @return string 2205 */ 2206 public function getCurrentTimestampSQL() 2207 { 2208 return 'CURRENT_TIMESTAMP'; 2209 } 2210 2211 /** 2212 * Get sql for transaction isolation level Connection constant 2213 * 2214 * @param integer $level 2215 * 2216 * @return string 2217 */ 2218 protected function _getTransactionIsolationLevelSQL($level) 2219 { 2220 switch ($level) { 2221 case Connection::TRANSACTION_READ_UNCOMMITTED: 2222 return 'READ UNCOMMITTED'; 2223 case Connection::TRANSACTION_READ_COMMITTED: 2224 return 'READ COMMITTED'; 2225 case Connection::TRANSACTION_REPEATABLE_READ: 2226 return 'REPEATABLE READ'; 2227 case Connection::TRANSACTION_SERIALIZABLE: 2228 return 'SERIALIZABLE'; 2229 default: 2230 throw new \InvalidArgumentException('Invalid isolation level:' . $level); 2231 } 2232 } 2233 2234 public function getListDatabasesSQL() 2235 { 2236 throw DBALException::notSupported(__METHOD__); 2237 } 2238 2239 public function getListSequencesSQL($database) 2240 { 2241 throw DBALException::notSupported(__METHOD__); 2242 } 2243 2244 public function getListTableConstraintsSQL($table) 2245 { 2246 throw DBALException::notSupported(__METHOD__); 2247 } 2248 2249 public function getListTableColumnsSQL($table, $database = null) 2250 { 2251 throw DBALException::notSupported(__METHOD__); 2252 } 2253 2254 public function getListTablesSQL() 2255 { 2256 throw DBALException::notSupported(__METHOD__); 2257 } 2258 2259 public function getListUsersSQL() 2260 { 2261 throw DBALException::notSupported(__METHOD__); 2262 } 2263 2264 /** 2265 * Get the SQL to list all views of a database or user. 2266 * 2267 * @param string $database 2268 * 2269 * @return string 2270 */ 2271 public function getListViewsSQL($database) 2272 { 2273 throw DBALException::notSupported(__METHOD__); 2274 } 2275 2276 /** 2277 * Get the list of indexes for the current database. 2278 * 2279 * The current database parameter is optional but will always be passed 2280 * when using the SchemaManager API and is the database the given table is in. 2281 * 2282 * Attention: Some platforms only support currentDatabase when they 2283 * are connected with that database. Cross-database information schema 2284 * requests may be impossible. 2285 * 2286 * @param string $table 2287 * @param string $currentDatabase 2288 * 2289 * @return string 2290 */ 2291 public function getListTableIndexesSQL($table, $currentDatabase = null) 2292 { 2293 throw DBALException::notSupported(__METHOD__); 2294 } 2295 2296 public function getListTableForeignKeysSQL($table) 2297 { 2298 throw DBALException::notSupported(__METHOD__); 2299 } 2300 2301 public function getCreateViewSQL($name, $sql) 2302 { 2303 throw DBALException::notSupported(__METHOD__); 2304 } 2305 2306 public function getDropViewSQL($name) 2307 { 2308 throw DBALException::notSupported(__METHOD__); 2309 } 2310 2311 /** 2312 * Get the SQL snippet to drop an existing sequence 2313 * 2314 * @param \Doctrine\DBAL\Schema\Sequence $sequence 2315 * 2316 * @return string 2317 */ 2318 public function getDropSequenceSQL($sequence) 2319 { 2320 throw DBALException::notSupported(__METHOD__); 2321 } 2322 2323 public function getSequenceNextValSQL($sequenceName) 2324 { 2325 throw DBALException::notSupported(__METHOD__); 2326 } 2327 2328 /** 2329 * create a new database 2330 * 2331 * @param string $database name of the database that should be created 2332 * 2333 * @return string 2334 */ 2335 public function getCreateDatabaseSQL($database) 2336 { 2337 throw DBALException::notSupported(__METHOD__); 2338 } 2339 2340 /** 2341 * Get sql to set the transaction isolation level 2342 * 2343 * @param integer $level 2344 * 2345 * @return string 2346 */ 2347 public function getSetTransactionIsolationSQL($level) 2348 { 2349 throw DBALException::notSupported(__METHOD__); 2350 } 2351 2352 /** 2353 * Obtain DBMS specific SQL to be used to create datetime fields in 2354 * statements like CREATE TABLE 2355 * 2356 * @param array $fieldDeclaration 2357 * 2358 * @return string 2359 */ 2360 public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) 2361 { 2362 throw DBALException::notSupported(__METHOD__); 2363 } 2364 2365 /** 2366 * Obtain DBMS specific SQL to be used to create datetime with timezone offset fields. 2367 * 2368 * @param array $fieldDeclaration 2369 * 2370 * @return string 2371 */ 2372 public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) 2373 { 2374 return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration); 2375 } 2376 2377 2378 /** 2379 * Obtain DBMS specific SQL to be used to create date fields in statements 2380 * like CREATE TABLE. 2381 * 2382 * @param array $fieldDeclaration 2383 * 2384 * @return string 2385 */ 2386 public function getDateTypeDeclarationSQL(array $fieldDeclaration) 2387 { 2388 throw DBALException::notSupported(__METHOD__); 2389 } 2390 2391 /** 2392 * Obtain DBMS specific SQL to be used to create time fields in statements 2393 * like CREATE TABLE. 2394 * 2395 * @param array $fieldDeclaration 2396 * 2397 * @return string 2398 */ 2399 public function getTimeTypeDeclarationSQL(array $fieldDeclaration) 2400 { 2401 throw DBALException::notSupported(__METHOD__); 2402 } 2403 2404 public function getFloatDeclarationSQL(array $fieldDeclaration) 2405 { 2406 return 'DOUBLE PRECISION'; 2407 } 2408 2409 /** 2410 * Gets the default transaction isolation level of the platform. 2411 * 2412 * @return integer The default isolation level. 2413 * 2414 * @see Doctrine\DBAL\Connection\TRANSACTION_* constants. 2415 */ 2416 public function getDefaultTransactionIsolationLevel() 2417 { 2418 return Connection::TRANSACTION_READ_COMMITTED; 2419 } 2420 2421 /* supports*() methods */ 2422 2423 /** 2424 * Whether the platform supports sequences. 2425 * 2426 * @return boolean 2427 */ 2428 public function supportsSequences() 2429 { 2430 return false; 2431 } 2432 2433 /** 2434 * Whether the platform supports identity columns. 2435 * Identity columns are columns that recieve an auto-generated value from the 2436 * database on insert of a row. 2437 * 2438 * @return boolean 2439 */ 2440 public function supportsIdentityColumns() 2441 { 2442 return false; 2443 } 2444 2445 /** 2446 * Whether the platform supports indexes. 2447 * 2448 * @return boolean 2449 */ 2450 public function supportsIndexes() 2451 { 2452 return true; 2453 } 2454 2455 /** 2456 * Whether the platform supports altering tables. 2457 * 2458 * @return boolean 2459 */ 2460 public function supportsAlterTable() 2461 { 2462 return true; 2463 } 2464 2465 /** 2466 * Whether the platform supports transactions. 2467 * 2468 * @return boolean 2469 */ 2470 public function supportsTransactions() 2471 { 2472 return true; 2473 } 2474 2475 /** 2476 * Whether the platform supports savepoints. 2477 * 2478 * @return boolean 2479 */ 2480 public function supportsSavepoints() 2481 { 2482 return true; 2483 } 2484 2485 /** 2486 * Whether the platform supports releasing savepoints. 2487 * 2488 * @return boolean 2489 */ 2490 public function supportsReleaseSavepoints() 2491 { 2492 return $this->supportsSavepoints(); 2493 } 2494 2495 /** 2496 * Whether the platform supports primary key constraints. 2497 * 2498 * @return boolean 2499 */ 2500 public function supportsPrimaryConstraints() 2501 { 2502 return true; 2503 } 2504 2505 /** 2506 * Does the platform supports foreign key constraints? 2507 * 2508 * @return boolean 2509 */ 2510 public function supportsForeignKeyConstraints() 2511 { 2512 return true; 2513 } 2514 2515 /** 2516 * Does this platform supports onUpdate in foreign key constraints? 2517 * 2518 * @return boolean 2519 */ 2520 public function supportsForeignKeyOnUpdate() 2521 { 2522 return ($this->supportsForeignKeyConstraints() && true); 2523 } 2524 2525 /** 2526 * Whether the platform supports database schemas. 2527 * 2528 * @return boolean 2529 */ 2530 public function supportsSchemas() 2531 { 2532 return false; 2533 } 2534 2535 /** 2536 * Can this platform emulate schemas? 2537 * 2538 * Platforms that either support or emulate schemas don't automatically 2539 * filter a schema for the namespaced elements in {@link 2540 * AbstractManager#createSchema}. 2541 * 2542 * @return boolean 2543 */ 2544 public function canEmulateSchemas() 2545 { 2546 return false; 2547 } 2548 2549 /** 2550 * Some databases don't allow to create and drop databases at all or only with certain tools. 2551 * 2552 * @return boolean 2553 */ 2554 public function supportsCreateDropDatabase() 2555 { 2556 return true; 2557 } 2558 2559 /** 2560 * Whether the platform supports getting the affected rows of a recent 2561 * update/delete type query. 2562 * 2563 * @return boolean 2564 */ 2565 public function supportsGettingAffectedRows() 2566 { 2567 return true; 2568 } 2569 2570 /** 2571 * Does this plaform support to add inline column comments as postfix. 2572 * 2573 * @return boolean 2574 */ 2575 public function supportsInlineColumnComments() 2576 { 2577 return false; 2578 } 2579 2580 /** 2581 * Does this platform support the propriortary synatx "COMMENT ON asset" 2582 * 2583 * @return boolean 2584 */ 2585 public function supportsCommentOnStatement() 2586 { 2587 return false; 2588 } 2589 2590 public function getIdentityColumnNullInsertSQL() 2591 { 2592 return ""; 2593 } 2594 2595 /** 2596 * Does this platform views ? 2597 * 2598 * @return boolean 2599 */ 2600 public function supportsViews() 2601 { 2602 return true; 2603 } 2604 2605 /** 2606 * Gets the format string, as accepted by the date() function, that describes 2607 * the format of a stored datetime value of this platform. 2608 * 2609 * @return string The format string. 2610 */ 2611 public function getDateTimeFormatString() 2612 { 2613 return 'Y-m-d H:i:s'; 2614 } 2615 2616 /** 2617 * Gets the format string, as accepted by the date() function, that describes 2618 * the format of a stored datetime with timezone value of this platform. 2619 * 2620 * @return string The format string. 2621 */ 2622 public function getDateTimeTzFormatString() 2623 { 2624 return 'Y-m-d H:i:s'; 2625 } 2626 2627 /** 2628 * Gets the format string, as accepted by the date() function, that describes 2629 * the format of a stored date value of this platform. 2630 * 2631 * @return string The format string. 2632 */ 2633 public function getDateFormatString() 2634 { 2635 return 'Y-m-d'; 2636 } 2637 2638 /** 2639 * Gets the format string, as accepted by the date() function, that describes 2640 * the format of a stored time value of this platform. 2641 * 2642 * @return string The format string. 2643 */ 2644 public function getTimeFormatString() 2645 { 2646 return 'H:i:s'; 2647 } 2648 2649 /** 2650 * Modify limit query 2651 * 2652 * @param string $query 2653 * @param integer $limit 2654 * @param integer $offset 2655 * 2656 * @return string 2657 */ 2658 final public function modifyLimitQuery($query, $limit, $offset = null) 2659 { 2660 if ($limit !== null) { 2661 $limit = (int)$limit; 2662 } 2663 2664 if ($offset !== null) { 2665 $offset = (int)$offset; 2666 2667 if ($offset < 0) { 2668 throw new DBALException("LIMIT argument offset=$offset is not valid"); 2669 } 2670 if ($offset > 0 && ! $this->supportsLimitOffset()) { 2671 throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName())); 2672 } 2673 } 2674 2675 return $this->doModifyLimitQuery($query, $limit, $offset); 2676 } 2677 2678 /** 2679 * Adds an driver-specific LIMIT clause to the query 2680 * 2681 * @param string $query 2682 * @param integer $limit 2683 * @param integer $offset 2684 * 2685 * @return string 2686 */ 2687 protected function doModifyLimitQuery($query, $limit, $offset) 2688 { 2689 if ($limit !== null) { 2690 $query .= ' LIMIT ' . $limit; 2691 } 2692 2693 if ($offset !== null) { 2694 $query .= ' OFFSET ' . $offset; 2695 } 2696 2697 return $query; 2698 } 2699 2700 /** 2701 * Does the database platform support offsets in modify limit clauses? 2702 * 2703 * @return boolean 2704 */ 2705 public function supportsLimitOffset() 2706 { 2707 return true; 2708 } 2709 2710 /** 2711 * Gets the character casing of a column in an SQL result set of this platform. 2712 * 2713 * @param string $column The column name for which to get the correct character casing. 2714 * 2715 * @return string The column name in the character casing used in SQL result sets. 2716 */ 2717 public function getSQLResultCasing($column) 2718 { 2719 return $column; 2720 } 2721 2722 /** 2723 * Makes any fixes to a name of a schema element (table, sequence, ...) that are required 2724 * by restrictions of the platform, like a maximum length. 2725 * 2726 * @param string $schemaElementName 2727 * 2728 * @return string 2729 */ 2730 public function fixSchemaElementName($schemaElementName) 2731 { 2732 return $schemaElementName; 2733 } 2734 2735 /** 2736 * Maximum length of any given databse identifier, like tables or column names. 2737 * 2738 * @return integer 2739 */ 2740 public function getMaxIdentifierLength() 2741 { 2742 return 63; 2743 } 2744 2745 /** 2746 * Get the insert sql for an empty insert statement 2747 * 2748 * @param string $tableName 2749 * @param string $identifierColumnName 2750 * 2751 * @return string $sql 2752 */ 2753 public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) 2754 { 2755 return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; 2756 } 2757 2758 /** 2759 * Generate a Truncate Table SQL statement for a given table. 2760 * 2761 * Cascade is not supported on many platforms but would optionally cascade the truncate by 2762 * following the foreign keys. 2763 * 2764 * @param string $tableName 2765 * @param boolean $cascade 2766 * 2767 * @return string 2768 */ 2769 public function getTruncateTableSQL($tableName, $cascade = false) 2770 { 2771 return 'TRUNCATE '.$tableName; 2772 } 2773 2774 /** 2775 * This is for test reasons, many vendors have special requirements for dummy statements. 2776 * 2777 * @return string 2778 */ 2779 public function getDummySelectSQL() 2780 { 2781 return 'SELECT 1'; 2782 } 2783 2784 /** 2785 * Generate SQL to create a new savepoint 2786 * 2787 * @param string $savepoint 2788 * 2789 * @return string 2790 */ 2791 public function createSavePoint($savepoint) 2792 { 2793 return 'SAVEPOINT ' . $savepoint; 2794 } 2795 2796 /** 2797 * Generate SQL to release a savepoint 2798 * 2799 * @param string $savepoint 2800 * 2801 * @return string 2802 */ 2803 public function releaseSavePoint($savepoint) 2804 { 2805 return 'RELEASE SAVEPOINT ' . $savepoint; 2806 } 2807 2808 /** 2809 * Generate SQL to rollback a savepoint 2810 * 2811 * @param string $savepoint 2812 * 2813 * @return string 2814 */ 2815 public function rollbackSavePoint($savepoint) 2816 { 2817 return 'ROLLBACK TO SAVEPOINT ' . $savepoint; 2818 } 2819 2820 /** 2821 * Return the keyword list instance of this platform. 2822 * 2823 * Throws exception if no keyword list is specified. 2824 * 2825 * @throws DBALException 2826 * 2827 * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList 2828 */ 2829 final public function getReservedKeywordsList() 2830 { 2831 // Check for an existing instantiation of the keywords class. 2832 if ($this->_keywords) { 2833 return $this->_keywords; 2834 } 2835 2836 $class = $this->getReservedKeywordsClass(); 2837 $keywords = new $class; 2838 if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) { 2839 throw DBALException::notSupported(__METHOD__); 2840 } 2841 2842 // Store the instance so it doesn't need to be generated on every request. 2843 $this->_keywords = $keywords; 2844 2845 return $keywords; 2846 } 2847 2848 /** 2849 * The class name of the reserved keywords list. 2850 * 2851 * @return string 2852 */ 2853 protected function getReservedKeywordsClass() 2854 { 2855 throw DBALException::notSupported(__METHOD__); 2856 } 2857} 2858