307 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /*
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
|  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
|  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
|  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
|  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
|  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
|  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  * This software consists of voluntary contributions made by many individuals
 | |
|  * and is licensed under the MIT license. For more information, see
 | |
|  * <http://www.doctrine-project.org>.
 | |
|  */
 | |
| 
 | |
| namespace Doctrine\DBAL\Schema;
 | |
| 
 | |
| use Doctrine\DBAL\Platforms\MariaDb1027Platform;
 | |
| use Doctrine\DBAL\Platforms\MySqlPlatform;
 | |
| use Doctrine\DBAL\Types\Type;
 | |
| use const CASE_LOWER;
 | |
| use function array_change_key_case;
 | |
| use function array_shift;
 | |
| use function array_values;
 | |
| use function end;
 | |
| use function preg_match;
 | |
| use function preg_replace;
 | |
| use function str_replace;
 | |
| use function stripslashes;
 | |
| use function strpos;
 | |
| use function strtok;
 | |
| use function strtolower;
 | |
| 
 | |
| /**
 | |
|  * Schema manager for the MySql RDBMS.
 | |
|  *
 | |
|  * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
 | |
|  * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
 | |
|  * @author Roman Borschel <roman@code-factory.org>
 | |
|  * @author Benjamin Eberlei <kontakt@beberlei.de>
 | |
|  * @since  2.0
 | |
|  */
 | |
| class MySqlSchemaManager extends AbstractSchemaManager
 | |
| {
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableViewDefinition($view)
 | |
|     {
 | |
|         return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableTableDefinition($table)
 | |
|     {
 | |
|         return array_shift($table);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableUserDefinition($user)
 | |
|     {
 | |
|         return [
 | |
|             'user' => $user['User'],
 | |
|             'password' => $user['Password'],
 | |
|         ];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
 | |
|     {
 | |
|         foreach ($tableIndexes as $k => $v) {
 | |
|             $v = array_change_key_case($v, CASE_LOWER);
 | |
|             if ($v['key_name'] === 'PRIMARY') {
 | |
|                 $v['primary'] = true;
 | |
|             } else {
 | |
|                 $v['primary'] = false;
 | |
|             }
 | |
|             if (strpos($v['index_type'], 'FULLTEXT') !== false) {
 | |
|                 $v['flags'] = ['FULLTEXT'];
 | |
|             } elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
 | |
|                 $v['flags'] = ['SPATIAL'];
 | |
|             }
 | |
|             $tableIndexes[$k] = $v;
 | |
|         }
 | |
| 
 | |
|         return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableSequenceDefinition($sequence)
 | |
|     {
 | |
|         return end($sequence);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableDatabaseDefinition($database)
 | |
|     {
 | |
|         return $database['Database'];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableTableColumnDefinition($tableColumn)
 | |
|     {
 | |
|         $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
 | |
| 
 | |
|         $dbType = strtolower($tableColumn['type']);
 | |
|         $dbType = strtok($dbType, '(), ');
 | |
|         $length = $tableColumn['length'] ?? strtok('(), ');
 | |
| 
 | |
|         $fixed = null;
 | |
| 
 | |
|         if ( ! isset($tableColumn['name'])) {
 | |
|             $tableColumn['name'] = '';
 | |
|         }
 | |
| 
 | |
|         $scale     = null;
 | |
|         $precision = null;
 | |
| 
 | |
|         $type = $this->_platform->getDoctrineTypeMapping($dbType);
 | |
| 
 | |
|         // In cases where not connected to a database DESCRIBE $table does not return 'Comment'
 | |
|         if (isset($tableColumn['comment'])) {
 | |
|             $type                   = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
 | |
|             $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
 | |
|         }
 | |
| 
 | |
|         switch ($dbType) {
 | |
|             case 'char':
 | |
|             case 'binary':
 | |
|                 $fixed = true;
 | |
|                 break;
 | |
|             case 'float':
 | |
|             case 'double':
 | |
|             case 'real':
 | |
|             case 'numeric':
 | |
|             case 'decimal':
 | |
|                 if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) {
 | |
|                     $precision = $match[1];
 | |
|                     $scale     = $match[2];
 | |
|                     $length    = null;
 | |
|                 }
 | |
|                 break;
 | |
|             case 'tinytext':
 | |
|                 $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT;
 | |
|                 break;
 | |
|             case 'text':
 | |
|                 $length = MySqlPlatform::LENGTH_LIMIT_TEXT;
 | |
|                 break;
 | |
|             case 'mediumtext':
 | |
|                 $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT;
 | |
|                 break;
 | |
|             case 'tinyblob':
 | |
|                 $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB;
 | |
|                 break;
 | |
|             case 'blob':
 | |
|                 $length = MySqlPlatform::LENGTH_LIMIT_BLOB;
 | |
|                 break;
 | |
|             case 'mediumblob':
 | |
|                 $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB;
 | |
|                 break;
 | |
|             case 'tinyint':
 | |
|             case 'smallint':
 | |
|             case 'mediumint':
 | |
|             case 'int':
 | |
|             case 'integer':
 | |
|             case 'bigint':
 | |
|             case 'year':
 | |
|                 $length = null;
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         if ($this->_platform instanceof MariaDb1027Platform) {
 | |
|             $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
 | |
|         } else {
 | |
|             $columnDefault = $tableColumn['default'];
 | |
|         }
 | |
| 
 | |
|         $options = [
 | |
|             'length'        => $length !== null ? (int) $length : null,
 | |
|             'unsigned'      => strpos($tableColumn['type'], 'unsigned') !== false,
 | |
|             'fixed'         => (bool) $fixed,
 | |
|             'default'       => $columnDefault,
 | |
|             'notnull'       => $tableColumn['null'] !== 'YES',
 | |
|             'scale'         => null,
 | |
|             'precision'     => null,
 | |
|             'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
 | |
|             'comment'       => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
 | |
|                 ? $tableColumn['comment']
 | |
|                 : null,
 | |
|         ];
 | |
| 
 | |
|         if ($scale !== null && $precision !== null) {
 | |
|             $options['scale']     = (int) $scale;
 | |
|             $options['precision'] = (int) $precision;
 | |
|         }
 | |
| 
 | |
|         $column = new Column($tableColumn['field'], Type::getType($type), $options);
 | |
| 
 | |
|         if (isset($tableColumn['collation'])) {
 | |
|             $column->setPlatformOption('collation', $tableColumn['collation']);
 | |
|         }
 | |
| 
 | |
|         return $column;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
 | |
|      *
 | |
|      * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted
 | |
|      *   to distinguish them from expressions (see MDEV-10134).
 | |
|      * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema
 | |
|      *   as current_timestamp(), currdate(), currtime()
 | |
|      * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have
 | |
|      *   null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053)
 | |
|      * - \' is always stored as '' in information_schema (normalized)
 | |
|      *
 | |
|      * @link https://mariadb.com/kb/en/library/information-schema-columns-table/
 | |
|      * @link https://jira.mariadb.org/browse/MDEV-13132
 | |
|      *
 | |
|      * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
 | |
|      */
 | |
|     private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string
 | |
|     {
 | |
|         if ($columnDefault === 'NULL' || $columnDefault === null) {
 | |
|             return null;
 | |
|         }
 | |
|         if ($columnDefault[0] === "'") {
 | |
|             return stripslashes(
 | |
|                 str_replace("''", "'",
 | |
|                     preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)
 | |
|                 )
 | |
|             );
 | |
|         }
 | |
|         switch ($columnDefault) {
 | |
|             case 'current_timestamp()':
 | |
|                 return $platform->getCurrentTimestampSQL();
 | |
|             case 'curdate()':
 | |
|                 return $platform->getCurrentDateSQL();
 | |
|             case 'curtime()':
 | |
|                 return $platform->getCurrentTimeSQL();
 | |
|         }
 | |
|         return $columnDefault;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function _getPortableTableForeignKeysList($tableForeignKeys)
 | |
|     {
 | |
|         $list = [];
 | |
|         foreach ($tableForeignKeys as $value) {
 | |
|             $value = array_change_key_case($value, CASE_LOWER);
 | |
|             if ( ! isset($list[$value['constraint_name']])) {
 | |
|                 if ( ! isset($value['delete_rule']) || $value['delete_rule'] === "RESTRICT") {
 | |
|                     $value['delete_rule'] = null;
 | |
|                 }
 | |
|                 if ( ! isset($value['update_rule']) || $value['update_rule'] === "RESTRICT") {
 | |
|                     $value['update_rule'] = null;
 | |
|                 }
 | |
| 
 | |
|                 $list[$value['constraint_name']] = [
 | |
|                     'name' => $value['constraint_name'],
 | |
|                     'local' => [],
 | |
|                     'foreign' => [],
 | |
|                     'foreignTable' => $value['referenced_table_name'],
 | |
|                     'onDelete' => $value['delete_rule'],
 | |
|                     'onUpdate' => $value['update_rule'],
 | |
|                 ];
 | |
|             }
 | |
|             $list[$value['constraint_name']]['local'][]   = $value['column_name'];
 | |
|             $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
 | |
|         }
 | |
| 
 | |
|         $result = [];
 | |
|         foreach ($list as $constraint) {
 | |
|             $result[] = new ForeignKeyConstraint(
 | |
|                 array_values($constraint['local']),
 | |
|                 $constraint['foreignTable'],
 | |
|                 array_values($constraint['foreign']),
 | |
|                 $constraint['name'],
 | |
|                 [
 | |
|                     'onDelete' => $constraint['onDelete'],
 | |
|                     'onUpdate' => $constraint['onUpdate'],
 | |
|                 ]
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return $result;
 | |
|     }
 | |
| }
 | 
