source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/DBAL/Platforms/AbstractPlatform.php @ 356

Last change on this file since 356 was 345, checked in by quyenla, 11 years ago

collaborator page

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