Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Platforms / MySqlPlatform.php
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19
20 namespace Doctrine\DBAL\Platforms;
21
22 use Doctrine\DBAL\DBALException,
23     Doctrine\DBAL\Schema\TableDiff,
24     Doctrine\DBAL\Schema\Index,
25     Doctrine\DBAL\Schema\Table;
26
27 /**
28  * The MySqlPlatform provides the behavior, features and SQL dialect of the
29  * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that
30  * uses the InnoDB storage engine.
31  *
32  * @since 2.0
33  * @author Roman Borschel <roman@code-factory.org>
34  * @author Benjamin Eberlei <kontakt@beberlei.de>
35  * @todo Rename: MySQLPlatform
36  */
37 class MySqlPlatform extends AbstractPlatform
38 {
39     /**
40      * {@inheritDoc}
41      */
42     public function getIdentifierQuoteCharacter()
43     {
44         return '`';
45     }
46
47     /**
48      * {@inheritDoc}
49      */
50     public function getRegexpExpression()
51     {
52         return 'RLIKE';
53     }
54
55     /**
56      * {@inheritDoc}
57      */
58     public function getGuidExpression()
59     {
60         return 'UUID()';
61     }
62
63     /**
64      * {@inheritDoc}
65      */
66     public function getLocateExpression($str, $substr, $startPos = false)
67     {
68         if ($startPos == false) {
69             return 'LOCATE(' . $substr . ', ' . $str . ')';
70         }
71
72         return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
73     }
74
75     /**
76      * {@inheritDoc}
77      */
78     public function getConcatExpression()
79     {
80         $args = func_get_args();
81         return 'CONCAT(' . join(', ', (array) $args) . ')';
82     }
83
84     /**
85      * {@inheritDoc}
86      */
87     public function getDateDiffExpression($date1, $date2)
88     {
89         return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')';
90     }
91
92     /**
93      * {@inheritDoc}
94      */
95     public function getDateAddDaysExpression($date, $days)
96     {
97         return 'DATE_ADD(' . $date . ', INTERVAL ' . $days . ' DAY)';
98     }
99
100     /**
101      * {@inheritDoc}
102      */
103     public function getDateSubDaysExpression($date, $days)
104     {
105         return 'DATE_SUB(' . $date . ', INTERVAL ' . $days . ' DAY)';
106     }
107
108     /**
109      * {@inheritDoc}
110      */
111     public function getDateAddMonthExpression($date, $months)
112     {
113         return 'DATE_ADD(' . $date . ', INTERVAL ' . $months . ' MONTH)';
114     }
115
116     /**
117      * {@inheritDoc}
118      */
119     public function getDateSubMonthExpression($date, $months)
120     {
121         return 'DATE_SUB(' . $date . ', INTERVAL ' . $months . ' MONTH)';
122     }
123
124     public function getListDatabasesSQL()
125     {
126         return 'SHOW DATABASES';
127     }
128
129     public function getListTableConstraintsSQL($table)
130     {
131         return 'SHOW INDEX FROM ' . $table;
132     }
133
134     /**
135      * {@inheritDoc}
136      *
137      * Two approaches to listing the table indexes. The information_schema is
138      * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table".
139      *
140      * @param string $table
141      * @param string $currentDatabase
142      * @return string
143      */
144     public function getListTableIndexesSQL($table, $currentDatabase = null)
145     {
146         if ($currentDatabase) {
147             return "SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, ".
148                    "SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, ".
149                    "CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, " .
150                    "NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment " .
151                    "FROM information_schema.STATISTICS WHERE TABLE_NAME = '" . $table . "' AND TABLE_SCHEMA = '" . $currentDatabase . "'";
152         }
153
154         return 'SHOW INDEX FROM ' . $table;
155     }
156
157     public function getListViewsSQL($database)
158     {
159         return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '".$database."'";
160     }
161
162     public function getListTableForeignKeysSQL($table, $database = null)
163     {
164         $sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ".
165                "k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ".
166                "FROM information_schema.key_column_usage k /*!50116 ".
167                "INNER JOIN information_schema.referential_constraints c ON ".
168                "  c.constraint_name = k.constraint_name AND ".
169                "  c.table_name = '$table' */ WHERE k.table_name = '$table'";
170
171         if ($database) {
172             $sql .= " AND k.table_schema = '$database' /*!50116 AND c.constraint_schema = '$database' */";
173         }
174
175         $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL";
176
177         return $sql;
178     }
179
180     public function getCreateViewSQL($name, $sql)
181     {
182         return 'CREATE VIEW ' . $name . ' AS ' . $sql;
183     }
184
185     public function getDropViewSQL($name)
186     {
187         return 'DROP VIEW '. $name;
188     }
189
190     /**
191      * {@inheritDoc}
192      */
193     protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
194     {
195         return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
196                 : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
197     }
198
199     /**
200      * {@inheritDoc}
201      */
202     public function getClobTypeDeclarationSQL(array $field)
203     {
204         if ( ! empty($field['length']) && is_numeric($field['length'])) {
205             $length = $field['length'];
206             if ($length <= 255) {
207                 return 'TINYTEXT';
208             }
209
210             if ($length <= 65532) {
211                 return 'TEXT';
212             }
213
214             if ($length <= 16777215) {
215                 return 'MEDIUMTEXT';
216             }
217         }
218
219         return 'LONGTEXT';
220     }
221
222     /**
223      * {@inheritDoc}
224      */
225     public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
226     {
227         if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
228             return 'TIMESTAMP';
229         }
230
231         return 'DATETIME';
232     }
233
234     /**
235      * {@inheritDoc}
236      */
237     public function getDateTypeDeclarationSQL(array $fieldDeclaration)
238     {
239         return 'DATE';
240     }
241
242     /**
243      * {@inheritDoc}
244      */
245     public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
246     {
247         return 'TIME';
248     }
249
250     /**
251      * {@inheritDoc}
252      */
253     public function getBooleanTypeDeclarationSQL(array $field)
254     {
255         return 'TINYINT(1)';
256     }
257
258     /**
259      * Obtain DBMS specific SQL code portion needed to set the COLLATION
260      * of a field declaration to be used in statements like CREATE TABLE.
261      *
262      * @param string $collation   name of the collation
263      *
264      * @return string  DBMS specific SQL code portion needed to set the COLLATION
265      *                 of a field declaration.
266      */
267     public function getCollationFieldDeclaration($collation)
268     {
269         return 'COLLATE ' . $collation;
270     }
271
272     /**
273      * {@inheritDoc}
274      *
275      * MySql prefers "autoincrement" identity columns since sequences can only
276      * be emulated with a table.
277      */
278     public function prefersIdentityColumns()
279     {
280         return true;
281     }
282
283     /**
284      * {@inheritDoc}
285      *
286      * MySql supports this through AUTO_INCREMENT columns.
287      */
288     public function supportsIdentityColumns()
289     {
290         return true;
291     }
292
293     /**
294      * {@inheritDoc}
295      */
296     public function supportsInlineColumnComments()
297     {
298         return true;
299     }
300
301     /**
302      * {@inheritDoc}
303      */
304     public function getShowDatabasesSQL()
305     {
306         return 'SHOW DATABASES';
307     }
308
309     public function getListTablesSQL()
310     {
311         return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'";
312     }
313
314     public function getListTableColumnsSQL($table, $database = null)
315     {
316         if ($database) {
317             return "SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ".
318                    "COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, " .
319                    "CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS CollactionName ".
320                    "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '" . $database . "' AND TABLE_NAME = '" . $table . "'";
321         }
322
323         return 'DESCRIBE ' . $table;
324     }
325
326     /**
327      * {@inheritDoc}
328      */
329     public function getCreateDatabaseSQL($name)
330     {
331         return 'CREATE DATABASE ' . $name;
332     }
333
334     /**
335      * {@inheritDoc}
336      */
337     public function getDropDatabaseSQL($name)
338     {
339         return 'DROP DATABASE ' . $name;
340     }
341
342     /**
343      * {@inheritDoc}
344      */
345     protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
346     {
347         $queryFields = $this->getColumnDeclarationListSQL($columns);
348
349         if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
350             foreach ($options['uniqueConstraints'] as $index => $definition) {
351                 $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
352             }
353         }
354
355         // add all indexes
356         if (isset($options['indexes']) && ! empty($options['indexes'])) {
357             foreach($options['indexes'] as $index => $definition) {
358                 $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
359             }
360         }
361
362         // attach all primary keys
363         if (isset($options['primary']) && ! empty($options['primary'])) {
364             $keyColumns = array_unique(array_values($options['primary']));
365             $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
366         }
367
368         $query = 'CREATE ';
369         if (!empty($options['temporary'])) {
370             $query .= 'TEMPORARY ';
371         }
372         $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') ';
373
374         if (isset($options['comment'])) {
375             $query .= 'COMMENT = ' . $options['comment'] . ' ';
376         }
377
378         if ( ! isset($options['charset'])) {
379             $options['charset'] = 'utf8';
380         }
381
382         if ( ! isset($options['collate'])) {
383             $options['collate'] = 'utf8_unicode_ci';
384         }
385
386         $query .= 'DEFAULT CHARACTER SET ' . $options['charset'];
387         $query .= ' COLLATE ' . $options['collate'];
388
389         if ( ! isset($options['engine'])) {
390             $options['engine'] = 'InnoDB';
391         }
392         $query .= ' ENGINE = ' . $options['engine'];
393
394         $sql[] = $query;
395
396         if (isset($options['foreignKeys'])) {
397             foreach ((array) $options['foreignKeys'] as $definition) {
398                 $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
399             }
400         }
401
402         return $sql;
403     }
404
405     /**
406      * {@inheritDoc}
407      */
408     public function getAlterTableSQL(TableDiff $diff)
409     {
410         $columnSql = array();
411         $queryParts = array();
412         if ($diff->newName !== false) {
413             $queryParts[] = 'RENAME TO ' . $diff->newName;
414         }
415
416         foreach ($diff->addedColumns as $column) {
417             if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
418                 continue;
419             }
420
421             $columnArray = $column->toArray();
422             $columnArray['comment'] = $this->getColumnComment($column);
423             $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
424         }
425
426         foreach ($diff->removedColumns as $column) {
427             if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
428                 continue;
429             }
430
431             $queryParts[] =  'DROP ' . $column->getQuotedName($this);
432         }
433
434         foreach ($diff->changedColumns as $columnDiff) {
435             if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
436                 continue;
437             }
438
439             /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
440             $column = $columnDiff->column;
441             $columnArray = $column->toArray();
442             $columnArray['comment'] = $this->getColumnComment($column);
443             $queryParts[] =  'CHANGE ' . ($columnDiff->oldColumnName) . ' '
444                     . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
445         }
446
447         foreach ($diff->renamedColumns as $oldColumnName => $column) {
448             if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
449                 continue;
450             }
451
452             $columnArray = $column->toArray();
453             $columnArray['comment'] = $this->getColumnComment($column);
454             $queryParts[] =  'CHANGE ' . $oldColumnName . ' '
455                     . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
456         }
457
458         $sql = array();
459         $tableSql = array();
460
461         if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
462             if (count($queryParts) > 0) {
463                 $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(", ", $queryParts);
464             }
465             $sql = array_merge(
466                 $this->getPreAlterTableIndexForeignKeySQL($diff),
467                 $sql,
468                 $this->getPostAlterTableIndexForeignKeySQL($diff)
469             );
470         }
471
472         return array_merge($sql, $tableSql, $columnSql);
473     }
474
475     /**
476      * {@inheritDoc}
477      */
478     protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
479     {
480         $sql = array();
481         $table = $diff->name;
482
483         foreach ($diff->removedIndexes as $remKey => $remIndex) {
484
485             foreach ($diff->addedIndexes as $addKey => $addIndex) {
486                 if ($remIndex->getColumns() == $addIndex->getColumns()) {
487
488                     $columns = $addIndex->getColumns();
489                     $type = '';
490                     if ($addIndex->isUnique()) {
491                         $type = 'UNIQUE ';
492                     }
493
494                     $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
495                     $query .= 'ADD ' . $type . 'INDEX ' . $addIndex->getName();
496                     $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')';
497
498                     $sql[] = $query;
499
500                     unset($diff->removedIndexes[$remKey]);
501                     unset($diff->addedIndexes[$addKey]);
502
503                     break;
504                 }
505             }
506         }
507
508         $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
509
510         return $sql;
511     }
512
513     /**
514      * {@inheritDoc}
515      */
516     protected function getCreateIndexSQLFlags(Index $index)
517     {
518         $type = '';
519         if ($index->isUnique()) {
520             $type .= 'UNIQUE ';
521         } else if ($index->hasFlag('fulltext')) {
522             $type .= 'FULLTEXT ';
523         }
524
525         return $type;
526     }
527
528     /**
529      * {@inheritDoc}
530      */
531     public function getIntegerTypeDeclarationSQL(array $field)
532     {
533         return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
534     }
535
536     /**
537      * {@inheritDoc}
538      */
539     public function getBigIntTypeDeclarationSQL(array $field)
540     {
541         return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
542     }
543
544     /**
545      * {@inheritDoc}
546      */
547     public function getSmallIntTypeDeclarationSQL(array $field)
548     {
549         return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
550     }
551
552     /**
553      * {@inheritDoc}
554      */
555     protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
556     {
557         $autoinc = '';
558         if ( ! empty($columnDef['autoincrement'])) {
559             $autoinc = ' AUTO_INCREMENT';
560         }
561         $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : '';
562
563         return $unsigned . $autoinc;
564     }
565
566     /**
567      * {@inheritDoc}
568      */
569     public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey)
570     {
571         $query = '';
572         if ($foreignKey->hasOption('match')) {
573             $query .= ' MATCH ' . $foreignKey->getOption('match');
574         }
575         $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
576         return $query;
577     }
578
579     /**
580      * {@inheritDoc}
581      */
582     public function getDropIndexSQL($index, $table=null)
583     {
584         if ($index instanceof Index) {
585             $indexName = $index->getQuotedName($this);
586         } else if(is_string($index)) {
587             $indexName = $index;
588         } else {
589             throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
590         }
591
592         if ($table instanceof Table) {
593             $table = $table->getQuotedName($this);
594         } else if(!is_string($table)) {
595             throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
596         }
597
598         if ($index instanceof Index && $index->isPrimary()) {
599             // mysql primary keys are always named "PRIMARY",
600             // so we cannot use them in statements because of them being keyword.
601             return $this->getDropPrimaryKeySQL($table);
602         }
603
604         return 'DROP INDEX ' . $indexName . ' ON ' . $table;
605     }
606
607     /**
608      * @param string $table
609      *
610      * @return string
611      */
612     protected function getDropPrimaryKeySQL($table)
613     {
614         return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
615     }
616
617     /**
618      * {@inheritDoc}
619      */
620     public function getSetTransactionIsolationSQL($level)
621     {
622         return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
623     }
624
625     /**
626      * {@inheritDoc}
627      */
628     public function getName()
629     {
630         return 'mysql';
631     }
632
633     /**
634      * {@inheritDoc}
635      */
636     public function getReadLockSQL()
637     {
638         return 'LOCK IN SHARE MODE';
639     }
640
641     /**
642      * {@inheritDoc}
643      */
644     protected function initializeDoctrineTypeMappings()
645     {
646         $this->doctrineTypeMapping = array(
647             'tinyint'       => 'boolean',
648             'smallint'      => 'smallint',
649             'mediumint'     => 'integer',
650             'int'           => 'integer',
651             'integer'       => 'integer',
652             'bigint'        => 'bigint',
653             'tinytext'      => 'text',
654             'mediumtext'    => 'text',
655             'longtext'      => 'text',
656             'text'          => 'text',
657             'varchar'       => 'string',
658             'string'        => 'string',
659             'char'          => 'string',
660             'date'          => 'date',
661             'datetime'      => 'datetime',
662             'timestamp'     => 'datetime',
663             'time'          => 'time',
664             'float'         => 'float',
665             'double'        => 'float',
666             'real'          => 'float',
667             'decimal'       => 'decimal',
668             'numeric'       => 'decimal',
669             'year'          => 'date',
670             'longblob'      => 'blob',
671             'blob'          => 'blob',
672             'mediumblob'    => 'blob',
673             'tinyblob'      => 'blob',
674             'binary'        => 'blob',
675             'varbinary'     => 'blob',
676             'set'           => 'simple_array',
677         );
678     }
679
680     /**
681      * {@inheritDoc}
682      */
683     public function getVarcharMaxLength()
684     {
685         return 65535;
686     }
687
688     /**
689      * {@inheritDoc}
690      */
691     protected function getReservedKeywordsClass()
692     {
693         return 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords';
694     }
695
696     /**
697      * {@inheritDoc}
698      *
699      * MySQL commits a transaction implicitly when DROP TABLE is executed, however not
700      * if DROP TEMPORARY TABLE is executed.
701      */
702     public function getDropTemporaryTableSQL($table)
703     {
704         if ($table instanceof Table) {
705             $table = $table->getQuotedName($this);
706         } else if(!is_string($table)) {
707             throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
708         }
709
710         return 'DROP TEMPORARY TABLE ' . $table;
711     }
712
713     /**
714      * {@inheritDoc}
715      */
716     public function getBlobTypeDeclarationSQL(array $field)
717     {
718         return 'LONGBLOB';
719     }
720 }