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