Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Platforms / AbstractPlatform.php
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
20 namespace Doctrine\DBAL\Platforms;
21
22 use 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  */
60 abstract 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                     $options['primary'] = $index->getColumns();
1169                     $options['primary_index'] = $index;
1170                 } else {
1171                     $options['indexes'][$index->getName()] = $index;
1172                 }
1173             }
1174         }
1175
1176         $columnSql = array();
1177         $columns = array();
1178
1179         foreach ($table->getColumns() as $column) {
1180             /* @var \Doctrine\DBAL\Schema\Column $column */
1181
1182             if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
1183                 $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
1184                 $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
1185
1186                 $columnSql = array_merge($columnSql, $eventArgs->getSql());
1187
1188                 if ($eventArgs->isDefaultPrevented()) {
1189                     continue;
1190                 }
1191             }
1192
1193             $columnData = array();
1194             $columnData['name'] = $column->getQuotedName($this);
1195             $columnData['type'] = $column->getType();
1196             $columnData['length'] = $column->getLength();
1197             $columnData['notnull'] = $column->getNotNull();
1198             $columnData['fixed'] = $column->getFixed();
1199             $columnData['unique'] = false; // TODO: what do we do about this?
1200             $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false;
1201
1202             if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) {
1203                 $columnData['length'] = 255;
1204             }
1205
1206             $columnData['unsigned'] = $column->getUnsigned();
1207             $columnData['precision'] = $column->getPrecision();
1208             $columnData['scale'] = $column->getScale();
1209             $columnData['default'] = $column->getDefault();
1210             $columnData['columnDefinition'] = $column->getColumnDefinition();
1211             $columnData['autoincrement'] = $column->getAutoincrement();
1212             $columnData['comment'] = $this->getColumnComment($column);
1213
1214             if (in_array($column->getName(), $options['primary'])) {
1215                 $columnData['primary'] = true;
1216             }
1217
1218             $columns[$columnData['name']] = $columnData;
1219         }
1220
1221         if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) {
1222             $options['foreignKeys'] = array();
1223             foreach ($table->getForeignKeys() as $fkConstraint) {
1224                 $options['foreignKeys'][] = $fkConstraint;
1225             }
1226         }
1227
1228         if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
1229             $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
1230             $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
1231
1232             if ($eventArgs->isDefaultPrevented()) {
1233                 return array_merge($eventArgs->getSql(), $columnSql);
1234             }
1235         }
1236
1237         $sql = $this->_getCreateTableSQL($tableName, $columns, $options);
1238         if ($this->supportsCommentOnStatement()) {
1239             foreach ($table->getColumns() as $column) {
1240                 if ($this->getColumnComment($column)) {
1241                     $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getName(), $this->getColumnComment($column));
1242                 }
1243             }
1244         }
1245
1246         return array_merge($sql, $columnSql);
1247     }
1248
1249     public function getCommentOnColumnSQL($tableName, $columnName, $comment)
1250     {
1251         return "COMMENT ON COLUMN " . $tableName . "." . $columnName . " IS '" . $comment . "'";
1252     }
1253
1254     /**
1255      * Gets the SQL used to create a table.
1256      *
1257      * @param string $tableName
1258      * @param array $columns
1259      * @param array $options
1260      *
1261      * @return array
1262      */
1263     protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
1264     {
1265         $columnListSql = $this->getColumnDeclarationListSQL($columns);
1266
1267         if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
1268             foreach ($options['uniqueConstraints'] as $name => $definition) {
1269                 $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
1270             }
1271         }
1272
1273         if (isset($options['primary']) && ! empty($options['primary'])) {
1274             $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
1275         }
1276
1277         if (isset($options['indexes']) && ! empty($options['indexes'])) {
1278             foreach($options['indexes'] as $index => $definition) {
1279                 $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
1280             }
1281         }
1282
1283         $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
1284
1285         $check = $this->getCheckDeclarationSQL($columns);
1286         if ( ! empty($check)) {
1287             $query .= ', ' . $check;
1288         }
1289         $query .= ')';
1290
1291         $sql[] = $query;
1292
1293         if (isset($options['foreignKeys'])) {
1294             foreach ((array) $options['foreignKeys'] as $definition) {
1295                 $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
1296             }
1297         }
1298
1299         return $sql;
1300     }
1301
1302     public function getCreateTemporaryTableSnippetSQL()
1303     {
1304         return "CREATE TEMPORARY TABLE";
1305     }
1306
1307     /**
1308      * Gets the SQL to create a sequence on this platform.
1309      *
1310      * @param \Doctrine\DBAL\Schema\Sequence $sequence
1311      *
1312      * @return string
1313      *
1314      * @throws DBALException
1315      */
1316     public function getCreateSequenceSQL(Sequence $sequence)
1317     {
1318         throw DBALException::notSupported(__METHOD__);
1319     }
1320
1321     /**
1322      * Gets the SQL statement to change a sequence on this platform.
1323      *
1324      * @param \Doctrine\DBAL\Schema\Sequence $sequence
1325      *
1326      * @return string
1327      */
1328     public function getAlterSequenceSQL(Sequence $sequence)
1329     {
1330         throw DBALException::notSupported(__METHOD__);
1331     }
1332
1333     /**
1334      * Gets the SQL to create a constraint on a table on this platform.
1335      *
1336      * @param \Doctrine\DBAL\Schema\Constraint $constraint
1337      * @param string|Table $table
1338      *
1339      * @return string
1340      */
1341     public function getCreateConstraintSQL(Constraint $constraint, $table)
1342     {
1343         if ($table instanceof Table) {
1344             $table = $table->getQuotedName($this);
1345         }
1346
1347         $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
1348
1349         $columns = array();
1350         foreach ($constraint->getColumns() as $column) {
1351             $columns[] = $column;
1352         }
1353         $columnList = '('. implode(', ', $columns) . ')';
1354
1355         $referencesClause = '';
1356         if ($constraint instanceof Index) {
1357             if($constraint->isPrimary()) {
1358                 $query .= ' PRIMARY KEY';
1359             } elseif ($constraint->isUnique()) {
1360                 $query .= ' UNIQUE';
1361             } else {
1362                 throw new \InvalidArgumentException(
1363                     'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
1364                 );
1365             }
1366         } else if ($constraint instanceof ForeignKeyConstraint) {
1367             $query .= ' FOREIGN KEY';
1368
1369             $foreignColumns = array();
1370             foreach ($constraint->getForeignColumns() as $column) {
1371                 $foreignColumns[] = $column;
1372             }
1373
1374             $referencesClause = ' REFERENCES '.$constraint->getForeignTableName(). ' ('.implode(', ', $foreignColumns).')';
1375         }
1376         $query .= ' '.$columnList.$referencesClause;
1377
1378         return $query;
1379     }
1380
1381     /**
1382      * Gets the SQL to create an index on a table on this platform.
1383      *
1384      * @param Index $index
1385      * @param string|Table $table name of the table on which the index is to be created
1386      *
1387      * @return string
1388      */
1389     public function getCreateIndexSQL(Index $index, $table)
1390     {
1391         if ($table instanceof Table) {
1392             $table = $table->getQuotedName($this);
1393         }
1394         $name = $index->getQuotedName($this);
1395         $columns = $index->getColumns();
1396
1397         if (count($columns) == 0) {
1398             throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
1399         }
1400
1401         if ($index->isPrimary()) {
1402             return $this->getCreatePrimaryKeySQL($index, $table);
1403         }
1404
1405         $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
1406         $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')';
1407
1408         return $query;
1409     }
1410
1411     /**
1412      * Adds additional flags for index generation
1413      *
1414      * @param Index $index
1415      *
1416      * @return string
1417      */
1418     protected function getCreateIndexSQLFlags(Index $index)
1419     {
1420         return $index->isUnique() ? 'UNIQUE ' : '';
1421     }
1422
1423     /**
1424      * Get SQL to create an unnamed primary key constraint.
1425      *
1426      * @param Index $index
1427      * @param string|Table $table
1428      *
1429      * @return string
1430      */
1431     public function getCreatePrimaryKeySQL(Index $index, $table)
1432     {
1433         return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')';
1434     }
1435
1436     /**
1437      * Quotes a string so that it can be safely used as a table or column name,
1438      * even if it is a reserved word of the platform. This also detects identifier
1439      * chains separated by dot and quotes them independently.
1440      *
1441      * NOTE: Just because you CAN use quoted identifiers doesn't mean
1442      * you SHOULD use them.  In general, they end up causing way more
1443      * problems than they solve.
1444      *
1445      * @param string $str           identifier name to be quoted
1446      *
1447      * @return string               quoted identifier string
1448      */
1449     public function quoteIdentifier($str)
1450     {
1451         if (strpos($str, ".") !== false) {
1452             $parts = array_map(array($this, "quoteIdentifier"), explode(".", $str));
1453
1454             return implode(".", $parts);
1455         }
1456
1457         return $this->quoteSingleIdentifier($str);
1458     }
1459
1460     /**
1461      * Quote a single identifier (no dot chain separation)
1462      *
1463      * @param string $str
1464      *
1465      * @return string
1466      */
1467     public function quoteSingleIdentifier($str)
1468     {
1469         $c = $this->getIdentifierQuoteCharacter();
1470
1471         return $c . str_replace($c, $c.$c, $str) . $c;
1472     }
1473
1474     /**
1475      * Create a new foreign key
1476      *
1477      * @param ForeignKeyConstraint  $foreignKey    ForeignKey instance
1478      * @param string|Table          $table         name of the table on which the foreign key is to be created
1479      *
1480      * @return string
1481      */
1482     public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
1483     {
1484         if ($table instanceof Table) {
1485             $table = $table->getQuotedName($this);
1486         }
1487
1488         $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
1489
1490         return $query;
1491     }
1492
1493     /**
1494      * Gets the sql statements for altering an existing table.
1495      *
1496      * The method returns an array of sql statements, since some platforms need several statements.
1497      *
1498      * @param TableDiff $diff
1499      *
1500      * @return array
1501      */
1502     public function getAlterTableSQL(TableDiff $diff)
1503     {
1504         throw DBALException::notSupported(__METHOD__);
1505     }
1506
1507     /**
1508      * @param Column $column
1509      * @param TableDiff $diff
1510      * @param array $columnSql
1511      *
1512      * @return boolean
1513      */
1514     protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
1515     {
1516         if (null === $this->_eventManager) {
1517             return false;
1518         }
1519
1520         if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
1521             return false;
1522         }
1523
1524         $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
1525         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
1526
1527         $columnSql = array_merge($columnSql, $eventArgs->getSql());
1528
1529         return $eventArgs->isDefaultPrevented();
1530     }
1531
1532     /**
1533      * @param Column $column
1534      * @param TableDiff $diff
1535      * @param array $columnSql
1536      *
1537      * @return boolean
1538      */
1539     protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
1540     {
1541         if (null === $this->_eventManager) {
1542             return false;
1543         }
1544
1545         if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
1546             return false;
1547         }
1548
1549         $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
1550         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
1551
1552         $columnSql = array_merge($columnSql, $eventArgs->getSql());
1553
1554         return $eventArgs->isDefaultPrevented();
1555     }
1556
1557     /**
1558      * @param ColumnDiff $columnDiff
1559      * @param TableDiff $diff
1560      * @param array $columnSql
1561      *
1562      * @return boolean
1563      */
1564     protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
1565     {
1566         if (null === $this->_eventManager) {
1567             return false;
1568         }
1569
1570         if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
1571             return false;
1572         }
1573
1574         $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
1575         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
1576
1577         $columnSql = array_merge($columnSql, $eventArgs->getSql());
1578
1579         return $eventArgs->isDefaultPrevented();
1580     }
1581
1582     /**
1583      * @param string $oldColumnName
1584      * @param Column $column
1585      * @param TableDiff $diff
1586      * @param array $columnSql
1587      *
1588      * @return boolean
1589      */
1590     protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
1591     {
1592         if (null === $this->_eventManager) {
1593             return false;
1594         }
1595
1596         if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
1597             return false;
1598         }
1599
1600         $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
1601         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
1602
1603         $columnSql = array_merge($columnSql, $eventArgs->getSql());
1604
1605         return $eventArgs->isDefaultPrevented();
1606     }
1607
1608     /**
1609      * @param TableDiff $diff
1610      * @param array $sql
1611      *
1612      * @return boolean
1613      */
1614     protected function onSchemaAlterTable(TableDiff $diff, &$sql)
1615     {
1616         if (null === $this->_eventManager) {
1617             return false;
1618         }
1619
1620         if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
1621             return false;
1622         }
1623
1624         $eventArgs = new SchemaAlterTableEventArgs($diff, $this);
1625         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
1626
1627         $sql = array_merge($sql, $eventArgs->getSql());
1628
1629         return $eventArgs->isDefaultPrevented();
1630     }
1631
1632     protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
1633     {
1634         $tableName = $diff->name;
1635
1636         $sql = array();
1637         if ($this->supportsForeignKeyConstraints()) {
1638             foreach ($diff->removedForeignKeys as $foreignKey) {
1639                 $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1640             }
1641             foreach ($diff->changedForeignKeys as $foreignKey) {
1642                 $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
1643             }
1644         }
1645
1646         foreach ($diff->removedIndexes as $index) {
1647             $sql[] = $this->getDropIndexSQL($index, $tableName);
1648         }
1649         foreach ($diff->changedIndexes as $index) {
1650             $sql[] = $this->getDropIndexSQL($index, $tableName);
1651         }
1652
1653         return $sql;
1654     }
1655
1656     protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
1657     {
1658         $tableName = false !== $diff->newName ? $diff->newName : $diff->name;
1659
1660         $sql = array();
1661         if ($this->supportsForeignKeyConstraints()) {
1662             foreach ($diff->addedForeignKeys as $foreignKey) {
1663                 $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1664             }
1665             foreach ($diff->changedForeignKeys as $foreignKey) {
1666                 $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
1667             }
1668         }
1669
1670         foreach ($diff->addedIndexes as $index) {
1671             $sql[] = $this->getCreateIndexSQL($index, $tableName);
1672         }
1673         foreach ($diff->changedIndexes as $index) {
1674             $sql[] = $this->getCreateIndexSQL($index, $tableName);
1675         }
1676
1677         return $sql;
1678     }
1679
1680     /**
1681      * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
1682      *
1683      * @param TableDiff $diff
1684      *
1685      * @return array
1686      */
1687     protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
1688     {
1689         return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff));
1690     }
1691
1692     /**
1693      * Get declaration of a number of fields in bulk
1694      *
1695      * @param array $fields  a multidimensional associative array.
1696      *      The first dimension determines the field name, while the second
1697      *      dimension is keyed with the name of the properties
1698      *      of the field being declared as array indexes. Currently, the types
1699      *      of supported field properties are as follows:
1700      *
1701      *      length
1702      *          Integer value that determines the maximum length of the text
1703      *          field. If this argument is missing the field should be
1704      *          declared to have the longest length allowed by the DBMS.
1705      *
1706      *      default
1707      *          Text value to be used as default for this field.
1708      *
1709      *      notnull
1710      *          Boolean flag that indicates whether this field is constrained
1711      *          to not be set to null.
1712      *      charset
1713      *          Text value with the default CHARACTER SET for this field.
1714      *      collation
1715      *          Text value with the default COLLATION for this field.
1716      *      unique
1717      *          unique constraint
1718      *
1719      * @return string
1720      */
1721     public function getColumnDeclarationListSQL(array $fields)
1722     {
1723         $queryFields = array();
1724
1725         foreach ($fields as $fieldName => $field) {
1726             $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field);
1727         }
1728
1729         return implode(', ', $queryFields);
1730     }
1731
1732     /**
1733      * Obtain DBMS specific SQL code portion needed to declare a generic type
1734      * field to be used in statements like CREATE TABLE.
1735      *
1736      * @param string $name   name the field to be declared.
1737      * @param array  $field  associative array with the name of the properties
1738      *      of the field being declared as array indexes. Currently, the types
1739      *      of supported field properties are as follows:
1740      *
1741      *      length
1742      *          Integer value that determines the maximum length of the text
1743      *          field. If this argument is missing the field should be
1744      *          declared to have the longest length allowed by the DBMS.
1745      *
1746      *      default
1747      *          Text value to be used as default for this field.
1748      *
1749      *      notnull
1750      *          Boolean flag that indicates whether this field is constrained
1751      *          to not be set to null.
1752      *      charset
1753      *          Text value with the default CHARACTER SET for this field.
1754      *      collation
1755      *          Text value with the default COLLATION for this field.
1756      *      unique
1757      *          unique constraint
1758      *      check
1759      *          column check constraint
1760      *      columnDefinition
1761      *          a string that defines the complete column
1762      *
1763      * @return string  DBMS specific SQL code portion that should be used to declare the column.
1764      */
1765     public function getColumnDeclarationSQL($name, array $field)
1766     {
1767         if (isset($field['columnDefinition'])) {
1768             $columnDef = $this->getCustomTypeDeclarationSQL($field);
1769         } else {
1770             $default = $this->getDefaultValueDeclarationSQL($field);
1771
1772             $charset = (isset($field['charset']) && $field['charset']) ?
1773                     ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : '';
1774
1775             $collation = (isset($field['collation']) && $field['collation']) ?
1776                     ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : '';
1777
1778             $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
1779
1780             $unique = (isset($field['unique']) && $field['unique']) ?
1781                     ' ' . $this->getUniqueFieldDeclarationSQL() : '';
1782
1783             $check = (isset($field['check']) && $field['check']) ?
1784                     ' ' . $field['check'] : '';
1785
1786             $typeDecl = $field['type']->getSqlDeclaration($field, $this);
1787             $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
1788         }
1789
1790         if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment']) {
1791             $columnDef .= " COMMENT '" . $field['comment'] . "'";
1792         }
1793
1794         return $name . ' ' . $columnDef;
1795     }
1796
1797     /**
1798      * Gets the SQL snippet that declares a floating point column of arbitrary precision.
1799      *
1800      * @param array $columnDef
1801      *
1802      * @return string
1803      */
1804     public function getDecimalTypeDeclarationSQL(array $columnDef)
1805     {
1806         $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision']))
1807             ? 10 : $columnDef['precision'];
1808         $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale']))
1809             ? 0 : $columnDef['scale'];
1810
1811         return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')';
1812     }
1813
1814     /**
1815      * Obtain DBMS specific SQL code portion needed to set a default value
1816      * declaration to be used in statements like CREATE TABLE.
1817      *
1818      * @param array $field      field definition array
1819      *
1820      * @return string           DBMS specific SQL code portion needed to set a default value
1821      */
1822     public function getDefaultValueDeclarationSQL($field)
1823     {
1824         $default = empty($field['notnull']) ? ' DEFAULT NULL' : '';
1825
1826         if (isset($field['default'])) {
1827             $default = " DEFAULT '".$field['default']."'";
1828             if (isset($field['type'])) {
1829                 if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) {
1830                     $default = " DEFAULT ".$field['default'];
1831                 } else if ((string)$field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) {
1832                     $default = " DEFAULT ".$this->getCurrentTimestampSQL();
1833                 } else if ((string) $field['type'] == 'Boolean') {
1834                     $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'";
1835                 }
1836             }
1837         }
1838         return $default;
1839     }
1840
1841     /**
1842      * Obtain DBMS specific SQL code portion needed to set a CHECK constraint
1843      * declaration to be used in statements like CREATE TABLE.
1844      *
1845      * @param array $definition     check definition
1846      *
1847      * @return string               DBMS specific SQL code portion needed to set a CHECK constraint
1848      */
1849     public function getCheckDeclarationSQL(array $definition)
1850     {
1851         $constraints = array();
1852         foreach ($definition as $field => $def) {
1853             if (is_string($def)) {
1854                 $constraints[] = 'CHECK (' . $def . ')';
1855             } else {
1856                 if (isset($def['min'])) {
1857                     $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
1858                 }
1859
1860                 if (isset($def['max'])) {
1861                     $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
1862                 }
1863             }
1864         }
1865
1866         return implode(', ', $constraints);
1867     }
1868
1869     /**
1870      * Obtain DBMS specific SQL code portion needed to set a unique
1871      * constraint declaration to be used in statements like CREATE TABLE.
1872      *
1873      * @param string $name          name of the unique constraint
1874      * @param Index $index          index definition
1875      *
1876      * @return string               DBMS specific SQL code portion needed
1877      *                              to set a constraint
1878      */
1879     public function getUniqueConstraintDeclarationSQL($name, Index $index)
1880     {
1881         if (count($index->getColumns()) === 0) {
1882             throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
1883         }
1884
1885         return 'CONSTRAINT ' . $name . ' UNIQUE ('
1886              . $this->getIndexFieldDeclarationListSQL($index->getColumns())
1887              . ')';
1888     }
1889
1890     /**
1891      * Obtain DBMS specific SQL code portion needed to set an index
1892      * declaration to be used in statements like CREATE TABLE.
1893      *
1894      * @param string $name          name of the index
1895      * @param Index $index          index definition
1896      *
1897      * @return string               DBMS specific SQL code portion needed to set an index
1898      */
1899     public function getIndexDeclarationSQL($name, Index $index)
1900     {
1901         $type = '';
1902
1903         if ($index->isUnique()) {
1904             $type = 'UNIQUE ';
1905         }
1906
1907         if (count($index->getColumns()) === 0) {
1908             throw new \InvalidArgumentException("Incomplete definition. 'columns' required.");
1909         }
1910
1911         return $type . 'INDEX ' . $name . ' ('
1912              . $this->getIndexFieldDeclarationListSQL($index->getColumns())
1913              . ')';
1914     }
1915
1916     /**
1917      * getCustomTypeDeclarationSql
1918      * Obtail SQL code portion needed to create a custom column,
1919      * e.g. when a field has the "columnDefinition" keyword.
1920      * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
1921      *
1922      * @param array $columnDef
1923      *
1924      * @return string
1925      */
1926     public function getCustomTypeDeclarationSQL(array $columnDef)
1927     {
1928         return $columnDef['columnDefinition'];
1929     }
1930
1931     /**
1932      * getIndexFieldDeclarationList
1933      * Obtain DBMS specific SQL code portion needed to set an index
1934      * declaration to be used in statements like CREATE TABLE.
1935      *
1936      * @param array $fields
1937      *
1938      * @return string
1939      */
1940     public function getIndexFieldDeclarationListSQL(array $fields)
1941     {
1942         $ret = array();
1943
1944         foreach ($fields as $field => $definition) {
1945             if (is_array($definition)) {
1946                 $ret[] = $field;
1947             } else {
1948                 $ret[] = $definition;
1949             }
1950         }
1951
1952         return implode(', ', $ret);
1953     }
1954
1955     /**
1956      * A method to return the required SQL string that fits between CREATE ... TABLE
1957      * to create the table as a temporary table.
1958      *
1959      * Should be overridden in driver classes to return the correct string for the
1960      * specific database type.
1961      *
1962      * The default is to return the string "TEMPORARY" - this will result in a
1963      * SQL error for any database that does not support temporary tables, or that
1964      * requires a different SQL command from "CREATE TEMPORARY TABLE".
1965      *
1966      * @return string The string required to be placed between "CREATE" and "TABLE"
1967      *                to generate a temporary table, if possible.
1968      */
1969     public function getTemporaryTableSQL()
1970     {
1971         return 'TEMPORARY';
1972     }
1973
1974     /**
1975      * Some vendors require temporary table names to be qualified specially.
1976      *
1977      * @param  string $tableName
1978      *
1979      * @return string
1980      */
1981     public function getTemporaryTableName($tableName)
1982     {
1983         return $tableName;
1984     }
1985
1986     /**
1987      * Get sql query to show a list of database.
1988      *
1989      * @return string
1990      */
1991     public function getShowDatabasesSQL()
1992     {
1993         throw DBALException::notSupported(__METHOD__);
1994     }
1995
1996     /**
1997      * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
1998      * of a field declaration to be used in statements like CREATE TABLE.
1999      *
2000      * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
2001      *
2002      * @return string  DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2003      *                 of a field declaration.
2004      */
2005     public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
2006     {
2007         $sql  = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
2008         $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
2009
2010         return $sql;
2011     }
2012
2013     /**
2014      * Return the FOREIGN KEY query section dealing with non-standard options
2015      * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
2016      *
2017      * @param ForeignKeyConstraint $foreignKey     foreign key definition
2018      *
2019      * @return string
2020      */
2021     public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
2022     {
2023         $query = '';
2024         if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
2025             $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
2026         }
2027         if ($foreignKey->hasOption('onDelete')) {
2028             $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
2029         }
2030         return $query;
2031     }
2032
2033     /**
2034      * returns given referential action in uppercase if valid, otherwise throws
2035      * an exception
2036      *
2037      * @throws \InvalidArgumentException if unknown referential action given
2038      *
2039      * @param string $action    foreign key referential action
2040      *
2041      * @return string
2042      */
2043     public function getForeignKeyReferentialActionSQL($action)
2044     {
2045         $upper = strtoupper($action);
2046         switch ($upper) {
2047             case 'CASCADE':
2048             case 'SET NULL':
2049             case 'NO ACTION':
2050             case 'RESTRICT':
2051             case 'SET DEFAULT':
2052                 return $upper;
2053             default:
2054                 throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper);
2055         }
2056     }
2057
2058     /**
2059      * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
2060      * of a field declaration to be used in statements like CREATE TABLE.
2061      *
2062      * @param ForeignKeyConstraint $foreignKey
2063      *
2064      * @return string
2065      */
2066     public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
2067     {
2068         $sql = '';
2069         if (strlen($foreignKey->getName())) {
2070             $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
2071         }
2072         $sql .= 'FOREIGN KEY (';
2073
2074         if (count($foreignKey->getLocalColumns()) === 0) {
2075             throw new \InvalidArgumentException("Incomplete definition. 'local' required.");
2076         }
2077         if (count($foreignKey->getForeignColumns()) === 0) {
2078             throw new \InvalidArgumentException("Incomplete definition. 'foreign' required.");
2079         }
2080         if (strlen($foreignKey->getForeignTableName()) === 0) {
2081             throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
2082         }
2083
2084         $sql .= implode(', ', $foreignKey->getLocalColumns())
2085               . ') REFERENCES '
2086               . $foreignKey->getQuotedForeignTableName($this) . ' ('
2087               . implode(', ', $foreignKey->getForeignColumns()) . ')';
2088
2089         return $sql;
2090     }
2091
2092     /**
2093      * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint
2094      * of a field declaration to be used in statements like CREATE TABLE.
2095      *
2096      * @return string  DBMS specific SQL code portion needed to set the UNIQUE constraint
2097      *                 of a field declaration.
2098      */
2099     public function getUniqueFieldDeclarationSQL()
2100     {
2101         return 'UNIQUE';
2102     }
2103
2104     /**
2105      * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
2106      * of a field declaration to be used in statements like CREATE TABLE.
2107      *
2108      * @param string $charset   name of the charset
2109      *
2110      * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
2111      *                 of a field declaration.
2112      */
2113     public function getColumnCharsetDeclarationSQL($charset)
2114     {
2115         return '';
2116     }
2117
2118     /**
2119      * Obtain DBMS specific SQL code portion needed to set the COLLATION
2120      * of a field declaration to be used in statements like CREATE TABLE.
2121      *
2122      * @param string $collation   name of the collation
2123      *
2124      * @return string  DBMS specific SQL code portion needed to set the COLLATION
2125      *                 of a field declaration.
2126      */
2127     public function getColumnCollationDeclarationSQL($collation)
2128     {
2129         return '';
2130     }
2131
2132     /**
2133      * Whether the platform prefers sequences for ID generation.
2134      * Subclasses should override this method to return TRUE if they prefer sequences.
2135      *
2136      * @return boolean
2137      */
2138     public function prefersSequences()
2139     {
2140         return false;
2141     }
2142
2143     /**
2144      * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
2145      * Subclasses should override this method to return TRUE if they prefer identity columns.
2146      *
2147      * @return boolean
2148      */
2149     public function prefersIdentityColumns()
2150     {
2151         return false;
2152     }
2153
2154     /**
2155      * Some platforms need the boolean values to be converted.
2156      *
2157      * The default conversion in this implementation converts to integers (false => 0, true => 1).
2158      *
2159      * @param mixed $item
2160      *
2161      * @return mixed
2162      */
2163     public function convertBooleans($item)
2164     {
2165         if (is_array($item)) {
2166             foreach ($item as $k => $value) {
2167                 if (is_bool($value)) {
2168                     $item[$k] = (int) $value;
2169                 }
2170             }
2171         } else if (is_bool($item)) {
2172             $item = (int) $item;
2173         }
2174
2175         return $item;
2176     }
2177
2178     /**
2179      * Gets the SQL specific for the platform to get the current date.
2180      *
2181      * @return string
2182      */
2183     public function getCurrentDateSQL()
2184     {
2185         return 'CURRENT_DATE';
2186     }
2187
2188     /**
2189      * Gets the SQL specific for the platform to get the current time.
2190      *
2191      * @return string
2192      */
2193     public function getCurrentTimeSQL()
2194     {
2195         return 'CURRENT_TIME';
2196     }
2197
2198     /**
2199      * Gets the SQL specific for the platform to get the current timestamp
2200      *
2201      * @return string
2202      */
2203     public function getCurrentTimestampSQL()
2204     {
2205         return 'CURRENT_TIMESTAMP';
2206     }
2207
2208     /**
2209      * Get sql for transaction isolation level Connection constant
2210      *
2211      * @param integer $level
2212      *
2213      * @return string
2214      */
2215     protected function _getTransactionIsolationLevelSQL($level)
2216     {
2217         switch ($level) {
2218             case Connection::TRANSACTION_READ_UNCOMMITTED:
2219                 return 'READ UNCOMMITTED';
2220             case Connection::TRANSACTION_READ_COMMITTED:
2221                 return 'READ COMMITTED';
2222             case Connection::TRANSACTION_REPEATABLE_READ:
2223                 return 'REPEATABLE READ';
2224             case Connection::TRANSACTION_SERIALIZABLE:
2225                 return 'SERIALIZABLE';
2226             default:
2227                 throw new \InvalidArgumentException('Invalid isolation level:' . $level);
2228         }
2229     }
2230
2231     public function getListDatabasesSQL()
2232     {
2233         throw DBALException::notSupported(__METHOD__);
2234     }
2235
2236     public function getListSequencesSQL($database)
2237     {
2238         throw DBALException::notSupported(__METHOD__);
2239     }
2240
2241     public function getListTableConstraintsSQL($table)
2242     {
2243         throw DBALException::notSupported(__METHOD__);
2244     }
2245
2246     public function getListTableColumnsSQL($table, $database = null)
2247     {
2248         throw DBALException::notSupported(__METHOD__);
2249     }
2250
2251     public function getListTablesSQL()
2252     {
2253         throw DBALException::notSupported(__METHOD__);
2254     }
2255
2256     public function getListUsersSQL()
2257     {
2258         throw DBALException::notSupported(__METHOD__);
2259     }
2260
2261     /**
2262      * Get the SQL to list all views of a database or user.
2263      *
2264      * @param string $database
2265      *
2266      * @return string
2267      */
2268     public function getListViewsSQL($database)
2269     {
2270         throw DBALException::notSupported(__METHOD__);
2271     }
2272
2273     /**
2274      * Get the list of indexes for the current database.
2275      *
2276      * The current database parameter is optional but will always be passed
2277      * when using the SchemaManager API and is the database the given table is in.
2278      *
2279      * Attention: Some platforms only support currentDatabase when they
2280      * are connected with that database. Cross-database information schema
2281      * requests may be impossible.
2282      *
2283      * @param string $table
2284      * @param string $currentDatabase
2285      *
2286      * @return string
2287      */
2288     public function getListTableIndexesSQL($table, $currentDatabase = null)
2289     {
2290         throw DBALException::notSupported(__METHOD__);
2291     }
2292
2293     public function getListTableForeignKeysSQL($table)
2294     {
2295         throw DBALException::notSupported(__METHOD__);
2296     }
2297
2298     public function getCreateViewSQL($name, $sql)
2299     {
2300         throw DBALException::notSupported(__METHOD__);
2301     }
2302
2303     public function getDropViewSQL($name)
2304     {
2305         throw DBALException::notSupported(__METHOD__);
2306     }
2307
2308     /**
2309      * Get the SQL snippet to drop an existing sequence
2310      *
2311      * @param  \Doctrine\DBAL\Schema\Sequence $sequence
2312      *
2313      * @return string
2314      */
2315     public function getDropSequenceSQL($sequence)
2316     {
2317         throw DBALException::notSupported(__METHOD__);
2318     }
2319
2320     public function getSequenceNextValSQL($sequenceName)
2321     {
2322         throw DBALException::notSupported(__METHOD__);
2323     }
2324
2325     /**
2326      * create a new database
2327      *
2328      * @param string $database name of the database that should be created
2329      *
2330      * @return string
2331      */
2332     public function getCreateDatabaseSQL($database)
2333     {
2334         throw DBALException::notSupported(__METHOD__);
2335     }
2336
2337     /**
2338      * Get sql to set the transaction isolation level
2339      *
2340      * @param integer $level
2341      *
2342      * @return string
2343      */
2344     public function getSetTransactionIsolationSQL($level)
2345     {
2346         throw DBALException::notSupported(__METHOD__);
2347     }
2348
2349     /**
2350      * Obtain DBMS specific SQL to be used to create datetime fields in
2351      * statements like CREATE TABLE
2352      *
2353      * @param array $fieldDeclaration
2354      *
2355      * @return string
2356      */
2357     public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
2358     {
2359         throw DBALException::notSupported(__METHOD__);
2360     }
2361
2362     /**
2363      * Obtain DBMS specific SQL to be used to create datetime with timezone offset fields.
2364      *
2365      * @param array $fieldDeclaration
2366      *
2367      * @return string
2368      */
2369     public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
2370     {
2371         return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration);
2372     }
2373
2374
2375     /**
2376      * Obtain DBMS specific SQL to be used to create date fields in statements
2377      * like CREATE TABLE.
2378      *
2379      * @param array $fieldDeclaration
2380      *
2381      * @return string
2382      */
2383     public function getDateTypeDeclarationSQL(array $fieldDeclaration)
2384     {
2385         throw DBALException::notSupported(__METHOD__);
2386     }
2387
2388     /**
2389      * Obtain DBMS specific SQL to be used to create time fields in statements
2390      * like CREATE TABLE.
2391      *
2392      * @param array $fieldDeclaration
2393      *
2394      * @return string
2395      */
2396     public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
2397     {
2398         throw DBALException::notSupported(__METHOD__);
2399     }
2400
2401     public function getFloatDeclarationSQL(array $fieldDeclaration)
2402     {
2403         return 'DOUBLE PRECISION';
2404     }
2405
2406     /**
2407      * Gets the default transaction isolation level of the platform.
2408      *
2409      * @return integer The default isolation level.
2410      *
2411      * @see Doctrine\DBAL\Connection\TRANSACTION_* constants.
2412      */
2413     public function getDefaultTransactionIsolationLevel()
2414     {
2415         return Connection::TRANSACTION_READ_COMMITTED;
2416     }
2417
2418     /* supports*() methods */
2419
2420     /**
2421      * Whether the platform supports sequences.
2422      *
2423      * @return boolean
2424      */
2425     public function supportsSequences()
2426     {
2427         return false;
2428     }
2429
2430     /**
2431      * Whether the platform supports identity columns.
2432      * Identity columns are columns that recieve an auto-generated value from the
2433      * database on insert of a row.
2434      *
2435      * @return boolean
2436      */
2437     public function supportsIdentityColumns()
2438     {
2439         return false;
2440     }
2441
2442     /**
2443      * Whether the platform supports indexes.
2444      *
2445      * @return boolean
2446      */
2447     public function supportsIndexes()
2448     {
2449         return true;
2450     }
2451
2452     /**
2453      * Whether the platform supports altering tables.
2454      *
2455      * @return boolean
2456      */
2457     public function supportsAlterTable()
2458     {
2459         return true;
2460     }
2461
2462     /**
2463      * Whether the platform supports transactions.
2464      *
2465      * @return boolean
2466      */
2467     public function supportsTransactions()
2468     {
2469         return true;
2470     }
2471
2472     /**
2473      * Whether the platform supports savepoints.
2474      *
2475      * @return boolean
2476      */
2477     public function supportsSavepoints()
2478     {
2479         return true;
2480     }
2481
2482     /**
2483      * Whether the platform supports releasing savepoints.
2484      *
2485      * @return boolean
2486      */
2487     public function supportsReleaseSavepoints()
2488     {
2489         return $this->supportsSavepoints();
2490     }
2491
2492     /**
2493      * Whether the platform supports primary key constraints.
2494      *
2495      * @return boolean
2496      */
2497     public function supportsPrimaryConstraints()
2498     {
2499         return true;
2500     }
2501
2502     /**
2503      * Does the platform supports foreign key constraints?
2504      *
2505      * @return boolean
2506      */
2507     public function supportsForeignKeyConstraints()
2508     {
2509         return true;
2510     }
2511
2512     /**
2513      * Does this platform supports onUpdate in foreign key constraints?
2514      *
2515      * @return boolean
2516      */
2517     public function supportsForeignKeyOnUpdate()
2518     {
2519         return ($this->supportsForeignKeyConstraints() && true);
2520     }
2521
2522     /**
2523      * Whether the platform supports database schemas.
2524      *
2525      * @return boolean
2526      */
2527     public function supportsSchemas()
2528     {
2529         return false;
2530     }
2531
2532     /**
2533      * Can this platform emulate schemas?
2534      *
2535      * Platforms that either support or emulate schemas don't automatically
2536      * filter a schema for the namespaced elements in {@link
2537      * AbstractManager#createSchema}.
2538      *
2539      * @return boolean
2540      */
2541     public function canEmulateSchemas()
2542     {
2543         return false;
2544     }
2545
2546     /**
2547      * Some databases don't allow to create and drop databases at all or only with certain tools.
2548      *
2549      * @return boolean
2550      */
2551     public function supportsCreateDropDatabase()
2552     {
2553         return true;
2554     }
2555
2556     /**
2557      * Whether the platform supports getting the affected rows of a recent
2558      * update/delete type query.
2559      *
2560      * @return boolean
2561      */
2562     public function supportsGettingAffectedRows()
2563     {
2564         return true;
2565     }
2566
2567     /**
2568      * Does this plaform support to add inline column comments as postfix.
2569      *
2570      * @return boolean
2571      */
2572     public function supportsInlineColumnComments()
2573     {
2574         return false;
2575     }
2576
2577     /**
2578      * Does this platform support the propriortary synatx "COMMENT ON asset"
2579      *
2580      * @return boolean
2581      */
2582     public function supportsCommentOnStatement()
2583     {
2584         return false;
2585     }
2586
2587     public function getIdentityColumnNullInsertSQL()
2588     {
2589         return "";
2590     }
2591
2592     /**
2593      * Does this platform views ?
2594      *
2595      * @return boolean
2596      */
2597     public function supportsViews()
2598     {
2599         return true;
2600     }
2601
2602     /**
2603      * Gets the format string, as accepted by the date() function, that describes
2604      * the format of a stored datetime value of this platform.
2605      *
2606      * @return string The format string.
2607      */
2608     public function getDateTimeFormatString()
2609     {
2610         return 'Y-m-d H:i:s';
2611     }
2612
2613     /**
2614      * Gets the format string, as accepted by the date() function, that describes
2615      * the format of a stored datetime with timezone value of this platform.
2616      *
2617      * @return string The format string.
2618      */
2619     public function getDateTimeTzFormatString()
2620     {
2621         return 'Y-m-d H:i:s';
2622     }
2623
2624     /**
2625      * Gets the format string, as accepted by the date() function, that describes
2626      * the format of a stored date value of this platform.
2627      *
2628      * @return string The format string.
2629      */
2630     public function getDateFormatString()
2631     {
2632         return 'Y-m-d';
2633     }
2634
2635     /**
2636      * Gets the format string, as accepted by the date() function, that describes
2637      * the format of a stored time value of this platform.
2638      *
2639      * @return string The format string.
2640      */
2641     public function getTimeFormatString()
2642     {
2643         return 'H:i:s';
2644     }
2645
2646     /**
2647      * Modify limit query
2648      *
2649      * @param string $query
2650      * @param integer $limit
2651      * @param integer $offset
2652      *
2653      * @return string
2654      */
2655     final public function modifyLimitQuery($query, $limit, $offset = null)
2656     {
2657         if ($limit !== null) {
2658             $limit = (int)$limit;
2659         }
2660
2661         if ($offset !== null) {
2662             $offset = (int)$offset;
2663
2664             if ($offset < 0) {
2665                 throw new DBALException("LIMIT argument offset=$offset is not valid");
2666             }
2667             if ($offset > 0 && ! $this->supportsLimitOffset()) {
2668                 throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName()));
2669             }
2670         }
2671
2672         return $this->doModifyLimitQuery($query, $limit, $offset);
2673     }
2674
2675     /**
2676      * Adds an driver-specific LIMIT clause to the query
2677      *
2678      * @param string $query
2679      * @param integer $limit
2680      * @param integer $offset
2681      *
2682      * @return string
2683      */
2684     protected function doModifyLimitQuery($query, $limit, $offset)
2685     {
2686         if ($limit !== null) {
2687             $query .= ' LIMIT ' . $limit;
2688         }
2689
2690         if ($offset !== null) {
2691             $query .= ' OFFSET ' . $offset;
2692         }
2693
2694         return $query;
2695     }
2696
2697     /**
2698      * Does the database platform support offsets in modify limit clauses?
2699      *
2700      * @return boolean
2701      */
2702     public function supportsLimitOffset()
2703     {
2704         return true;
2705     }
2706
2707     /**
2708      * Gets the character casing of a column in an SQL result set of this platform.
2709      *
2710      * @param string $column The column name for which to get the correct character casing.
2711      *
2712      * @return string The column name in the character casing used in SQL result sets.
2713      */
2714     public function getSQLResultCasing($column)
2715     {
2716         return $column;
2717     }
2718
2719     /**
2720      * Makes any fixes to a name of a schema element (table, sequence, ...) that are required
2721      * by restrictions of the platform, like a maximum length.
2722      *
2723      * @param string $schemaElementName
2724      *
2725      * @return string
2726      */
2727     public function fixSchemaElementName($schemaElementName)
2728     {
2729         return $schemaElementName;
2730     }
2731
2732     /**
2733      * Maximum length of any given databse identifier, like tables or column names.
2734      *
2735      * @return integer
2736      */
2737     public function getMaxIdentifierLength()
2738     {
2739         return 63;
2740     }
2741
2742     /**
2743      * Get the insert sql for an empty insert statement
2744      *
2745      * @param string $tableName
2746      * @param string $identifierColumnName
2747      *
2748      * @return string $sql
2749      */
2750     public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
2751     {
2752         return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
2753     }
2754
2755     /**
2756      * Generate a Truncate Table SQL statement for a given table.
2757      *
2758      * Cascade is not supported on many platforms but would optionally cascade the truncate by
2759      * following the foreign keys.
2760      *
2761      * @param  string $tableName
2762      * @param  boolean $cascade
2763      *
2764      * @return string
2765      */
2766     public function getTruncateTableSQL($tableName, $cascade = false)
2767     {
2768         return 'TRUNCATE '.$tableName;
2769     }
2770
2771     /**
2772      * This is for test reasons, many vendors have special requirements for dummy statements.
2773      *
2774      * @return string
2775      */
2776     public function getDummySelectSQL()
2777     {
2778         return 'SELECT 1';
2779     }
2780
2781     /**
2782      * Generate SQL to create a new savepoint
2783      *
2784      * @param string $savepoint
2785      *
2786      * @return string
2787      */
2788     public function createSavePoint($savepoint)
2789     {
2790         return 'SAVEPOINT ' . $savepoint;
2791     }
2792
2793     /**
2794      * Generate SQL to release a savepoint
2795      *
2796      * @param string $savepoint
2797      *
2798      * @return string
2799      */
2800     public function releaseSavePoint($savepoint)
2801     {
2802         return 'RELEASE SAVEPOINT ' . $savepoint;
2803     }
2804
2805     /**
2806      * Generate SQL to rollback a savepoint
2807      *
2808      * @param string $savepoint
2809      *
2810      * @return string
2811      */
2812     public function rollbackSavePoint($savepoint)
2813     {
2814         return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
2815     }
2816
2817     /**
2818      * Return the keyword list instance of this platform.
2819      *
2820      * Throws exception if no keyword list is specified.
2821      *
2822      * @throws DBALException
2823      *
2824      * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList
2825      */
2826     final public function getReservedKeywordsList()
2827     {
2828         // Check for an existing instantiation of the keywords class.
2829         if ($this->_keywords) {
2830             return $this->_keywords;
2831         }
2832
2833         $class = $this->getReservedKeywordsClass();
2834         $keywords = new $class;
2835         if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) {
2836             throw DBALException::notSupported(__METHOD__);
2837         }
2838
2839         // Store the instance so it doesn't need to be generated on every request.
2840         $this->_keywords = $keywords;
2841
2842         return $keywords;
2843     }
2844
2845     /**
2846      * The class name of the reserved keywords list.
2847      *
2848      * @return string
2849      */
2850     protected function getReservedKeywordsClass()
2851     {
2852         throw DBALException::notSupported(__METHOD__);
2853     }
2854 }