2008-08-21 19:36:39 +00:00
< ? php
// $Id$
/**
* @ file
* Database schema code for MySQL database servers .
*/
/**
* @ ingroup schemaapi
* @ {
*/
class DatabaseSchema_mysql extends DatabaseSchema {
2009-03-14 20:34:17 +00:00
/**
* Maximum length of a table comment in MySQL .
*/
const COMMENT_MAX_TABLE = 60 ;
/**
* Maximum length of a column comment in MySQL .
*/
const COMMENT_MAX_COLUMN = 255 ;
2009-01-08 09:52:12 +00:00
/**
2009-01-26 14:08:44 +00:00
* Build a condition to match a table name against a standard information_schema .
2009-01-08 09:52:12 +00:00
*
* MySQL uses databases like schemas rather than catalogs so when we build
* a condition to query the information_schema . tables , we set the default
2009-01-26 14:08:44 +00:00
* database as the schema unless specified otherwise , and exclude table_catalog
2009-01-08 09:52:12 +00:00
* from the condition criteria .
*/
protected function buildTableNameCondition ( $table_name , $operator = '=' ) {
$info = Database :: getConnectionInfo ();
2008-09-15 20:48:10 +00:00
2009-01-08 09:52:12 +00:00
if ( strpos ( $table_name , '.' )) {
list ( $schema , $table_name ) = explode ( '.' , $table_name );
}
else {
$schema = $info [ 'default' ][ 'database' ];
}
2008-08-21 19:36:39 +00:00
2009-01-19 01:28:28 +00:00
$condition = new DatabaseCondition ( 'AND' );
$condition -> condition ( 'table_schema' , $schema );
$condition -> condition ( 'table_name' , $table_name , $operator );
2009-01-08 09:52:12 +00:00
return $condition ;
}
2008-08-21 19:36:39 +00:00
/**
* Generate SQL to create a new table from a Drupal schema definition .
*
* @ param $name
* The name of the table to create .
* @ param $table
* A Schema API table definition array .
* @ return
* An array of SQL statements to create the table .
*/
protected function createTableSql ( $name , $table ) {
2009-04-25 16:57:19 +00:00
// Provide some defaults if needed
$table += array (
'mysql_engine' => 'InnoDB' ,
'mysql_character_set' => 'UTF8' ,
);
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
$sql = " CREATE TABLE { " . $name . " } ( \n " ;
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
// Add the SQL statement for each field.
foreach ( $table [ 'fields' ] as $field_name => $field ) {
$sql .= $this -> createFieldSql ( $field_name , $this -> processField ( $field )) . " , \n " ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
// Process keys & indexes.
$keys = $this -> createKeysSql ( $table );
if ( count ( $keys )) {
$sql .= implode ( " , \n " , $keys ) . " , \n " ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
// Remove the last comma and space.
$sql = substr ( $sql , 0 , - 3 ) . " \n ) " ;
2008-09-15 20:48:10 +00:00
2009-04-25 16:57:19 +00:00
$sql .= 'ENGINE = ' . $table [ 'mysql_engine' ] . ' DEFAULT CHARACTER SET ' . $table [ 'mysql_character_set' ];
2008-09-15 20:48:10 +00:00
2009-03-14 20:34:17 +00:00
// Add table comment.
if ( ! empty ( $table [ 'description' ])) {
$sql .= ' COMMENT ' . $this -> prepareComment ( $table [ 'description' ], self :: COMMENT_MAX_TABLE );
}
2008-08-21 19:36:39 +00:00
return array ( $sql );
}
/**
* Create an SQL string for a field to be used in table creation or alteration .
*
* Before passing a field out of a schema definition into this function it has
* to be processed by _db_process_field () .
*
* @ param $name
* Name of the field .
* @ param $spec
* The field specification , as per the schema data structure format .
*/
protected function createFieldSql ( $name , $spec ) {
$sql = " ` " . $name . " ` " . $spec [ 'mysql_type' ];
2008-09-15 20:48:10 +00:00
2009-07-09 10:12:14 +00:00
if ( in_array ( $spec [ 'type' ], array ( 'varchar' , 'char' , 'text' )) && isset ( $spec [ 'length' ])) {
2008-08-21 19:36:39 +00:00
$sql .= '(' . $spec [ 'length' ] . ')' ;
}
elseif ( isset ( $spec [ 'precision' ]) && isset ( $spec [ 'scale' ])) {
$sql .= '(' . $spec [ 'precision' ] . ', ' . $spec [ 'scale' ] . ')' ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
if ( ! empty ( $spec [ 'unsigned' ])) {
$sql .= ' unsigned' ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
if ( ! empty ( $spec [ 'not null' ])) {
$sql .= ' NOT NULL' ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
if ( ! empty ( $spec [ 'auto_increment' ])) {
$sql .= ' auto_increment' ;
}
2008-09-15 20:48:10 +00:00
2008-12-02 19:45:01 +00:00
// $spec['default'] can be NULL, so we explicitly check for the key here.
2008-11-13 20:52:13 +00:00
if ( array_key_exists ( 'default' , $spec )) {
2008-08-21 19:36:39 +00:00
if ( is_string ( $spec [ 'default' ])) {
$spec [ 'default' ] = " ' " . $spec [ 'default' ] . " ' " ;
}
2008-11-13 20:52:13 +00:00
elseif ( is_null ( $spec [ 'default' ])) {
$spec [ 'default' ] = 'NULL' ;
}
2008-08-21 19:36:39 +00:00
$sql .= ' DEFAULT ' . $spec [ 'default' ];
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
if ( empty ( $spec [ 'not null' ]) && ! isset ( $spec [ 'default' ])) {
$sql .= ' DEFAULT NULL' ;
}
2008-09-15 20:48:10 +00:00
2009-03-14 20:34:17 +00:00
// Add column comment.
if ( ! empty ( $spec [ 'description' ])) {
$sql .= ' COMMENT ' . $this -> prepareComment ( $spec [ 'description' ], self :: COMMENT_MAX_COLUMN );
}
2008-08-21 19:36:39 +00:00
return $sql ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
/**
* Set database - engine specific properties for a field .
*
* @ param $field
* A field description array , as specified in the schema documentation .
*/
protected function processField ( $field ) {
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
if ( ! isset ( $field [ 'size' ])) {
$field [ 'size' ] = 'normal' ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
// Set the correct database-engine specific datatype.
if ( ! isset ( $field [ 'mysql_type' ])) {
$map = db_type_map ();
$field [ 'mysql_type' ] = $map [ $field [ 'type' ] . ':' . $field [ 'size' ]];
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
if ( $field [ 'type' ] == 'serial' ) {
$field [ 'auto_increment' ] = TRUE ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
return $field ;
}
public function getFieldTypeMap () {
2008-12-20 18:24:41 +00:00
// Put :normal last so it gets preserved by array_flip. This makes
2008-08-21 19:36:39 +00:00
// it much easier for modules (such as schema.module) to map
// database types back into schema types.
2009-04-14 06:22:10 +00:00
// $map does not use drupal_static as its value never changes.
2008-08-21 19:36:39 +00:00
static $map = array (
'varchar:normal' => 'VARCHAR' ,
'char:normal' => 'CHAR' ,
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
'text:tiny' => 'TINYTEXT' ,
'text:small' => 'TINYTEXT' ,
'text:medium' => 'MEDIUMTEXT' ,
'text:big' => 'LONGTEXT' ,
'text:normal' => 'TEXT' ,
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
'serial:tiny' => 'TINYINT' ,
'serial:small' => 'SMALLINT' ,
'serial:medium' => 'MEDIUMINT' ,
'serial:big' => 'BIGINT' ,
'serial:normal' => 'INT' ,
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
'int:tiny' => 'TINYINT' ,
'int:small' => 'SMALLINT' ,
'int:medium' => 'MEDIUMINT' ,
'int:big' => 'BIGINT' ,
'int:normal' => 'INT' ,
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
'float:tiny' => 'FLOAT' ,
'float:small' => 'FLOAT' ,
'float:medium' => 'FLOAT' ,
'float:big' => 'DOUBLE' ,
'float:normal' => 'FLOAT' ,
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
'numeric:normal' => 'DECIMAL' ,
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
'blob:big' => 'LONGBLOB' ,
'blob:normal' => 'BLOB' ,
2008-09-15 20:48:10 +00:00
2009-08-22 19:43:12 +00:00
'date:normal' => 'DATE' ,
2008-08-21 19:36:39 +00:00
'datetime:normal' => 'DATETIME' ,
2009-08-22 19:43:12 +00:00
'time:normal' => 'TIME' ,
2008-08-21 19:36:39 +00:00
);
return $map ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
protected function createKeysSql ( $spec ) {
$keys = array ();
if ( ! empty ( $spec [ 'primary key' ])) {
$keys [] = 'PRIMARY KEY (' . $this -> createKeysSqlHelper ( $spec [ 'primary key' ]) . ')' ;
}
if ( ! empty ( $spec [ 'unique keys' ])) {
foreach ( $spec [ 'unique keys' ] as $key => $fields ) {
2009-08-17 06:52:02 +00:00
$keys [] = 'UNIQUE KEY `' . $key . '` (' . $this -> createKeysSqlHelper ( $fields ) . ')' ;
2008-08-21 19:36:39 +00:00
}
}
if ( ! empty ( $spec [ 'indexes' ])) {
foreach ( $spec [ 'indexes' ] as $index => $fields ) {
2009-08-17 06:52:02 +00:00
$keys [] = 'INDEX `' . $index . '` (' . $this -> createKeysSqlHelper ( $fields ) . ')' ;
2008-08-21 19:36:39 +00:00
}
}
return $keys ;
}
2008-09-15 20:48:10 +00:00
2008-08-21 19:36:39 +00:00
protected function createKeySql ( $fields ) {
2009-09-29 15:13:57 +00:00
$return = array ();
2008-08-21 19:36:39 +00:00
foreach ( $fields as $field ) {
if ( is_array ( $field )) {
2009-09-29 15:13:57 +00:00
$return [] = '`' . $field [ 0 ] . '`(' . $field [ 1 ] . ')' ;
2008-08-21 19:36:39 +00:00
}
else {
2009-09-29 15:13:57 +00:00
$return [] = '`' . $field . '`' ;
2008-08-21 19:36:39 +00:00
}
}
2009-09-29 15:13:57 +00:00
return implode ( ', ' , $return );
2008-08-21 19:36:39 +00:00
}
protected function createKeysSqlHelper ( $fields ) {
2009-09-29 15:13:57 +00:00
$return = array ();
2008-08-21 19:36:39 +00:00
foreach ( $fields as $field ) {
if ( is_array ( $field )) {
2009-09-29 15:13:57 +00:00
$return [] = '`' . $field [ 0 ] . '`(' . $field [ 1 ] . ')' ;
2008-08-21 19:36:39 +00:00
}
else {
2009-09-29 15:13:57 +00:00
$return [] = '`' . $field . '`' ;
2008-08-21 19:36:39 +00:00
}
}
2009-09-29 15:13:57 +00:00
return implode ( ', ' , $return );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function renameTable ( $table , $new_name ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}' );
2008-08-21 19:36:39 +00:00
}
2008-09-15 20:48:10 +00:00
2009-09-29 15:13:57 +00:00
public function dropTable ( $table ) {
$this -> connection -> query ( 'DROP TABLE {' . $table . '}' );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function addField ( $table , $field , $spec , $keys_new = array ()) {
2008-08-21 19:36:39 +00:00
$fixnull = FALSE ;
if ( ! empty ( $spec [ 'not null' ]) && ! isset ( $spec [ 'default' ])) {
$fixnull = TRUE ;
$spec [ 'not null' ] = FALSE ;
}
$query = 'ALTER TABLE {' . $table . '} ADD ' ;
$query .= $this -> createFieldSql ( $field , $this -> processField ( $spec ));
if ( count ( $keys_new )) {
$query .= ', ADD ' . implode ( ', ADD ' , $this -> createKeysSql ( $keys_new ));
}
2009-09-29 15:13:57 +00:00
$this -> connection -> query ( $query );
2008-08-21 19:36:39 +00:00
if ( isset ( $spec [ 'initial' ])) {
2009-09-29 15:13:57 +00:00
$this -> connection -> update ( $table )
-> fields ( array ( $field , $spec [ 'initial' ]))
-> execute ();
2008-08-21 19:36:39 +00:00
}
if ( $fixnull ) {
$spec [ 'not null' ] = TRUE ;
2009-09-29 15:13:57 +00:00
$this -> changeField ( $table , $field , $field , $spec );
2008-08-21 19:36:39 +00:00
}
}
2009-09-29 15:13:57 +00:00
public function dropField ( $table , $field ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} DROP `' . $field . '`' );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function fieldSetDefault ( $table , $field , $default ) {
2008-11-13 20:52:13 +00:00
if ( is_null ( $default )) {
2008-08-21 19:36:39 +00:00
$default = 'NULL' ;
}
else {
$default = is_string ( $default ) ? " ' $default ' " : $default ;
}
2008-09-15 20:48:10 +00:00
2009-09-29 15:13:57 +00:00
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} ALTER COLUMN `' . $field . '` SET DEFAULT ' . $default );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function fieldSetNoDefault ( $table , $field ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} ALTER COLUMN `' . $field . '` DROP DEFAULT' );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function addPrimaryKey ( $table , $fields ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} ADD PRIMARY KEY (' . $this -> createKeySql ( $fields ) . ')' );
2008-08-21 19:36:39 +00:00
}
2008-09-15 20:48:10 +00:00
2009-09-29 15:13:57 +00:00
public function dropPrimaryKey ( $table ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} DROP PRIMARY KEY' );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function addUniqueKey ( $table , $name , $fields ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} ADD UNIQUE KEY `' . $name . '` (' . $this -> createKeySql ( $fields ) . ')' );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function dropUniqueKey ( $table , $name ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} DROP KEY `' . $name . '`' );
2008-08-21 19:36:39 +00:00
}
2008-09-15 20:48:10 +00:00
2009-09-29 15:13:57 +00:00
public function addIndex ( $table , $name , $fields ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} ADD INDEX `' . $name . '` (' . $this -> createKeySql ( $fields ) . ')' );
2008-08-21 19:36:39 +00:00
}
2009-09-29 15:13:57 +00:00
public function dropIndex ( $table , $name ) {
$this -> connection -> query ( 'ALTER TABLE {' . $table . '} DROP INDEX `' . $name . '`' );
2008-08-21 19:36:39 +00:00
}
2008-09-15 20:48:10 +00:00
2009-09-29 15:13:57 +00:00
public function changeField ( $table , $field , $field_new , $spec , $keys_new = array ()) {
2008-11-11 16:49:38 +00:00
$sql = 'ALTER TABLE {' . $table . '} CHANGE `' . $field . '` ' . $this -> createFieldSql ( $field_new , $this -> processField ( $spec ));
2008-08-21 19:36:39 +00:00
if ( count ( $keys_new )) {
$sql .= ', ADD ' . implode ( ', ADD ' , $this -> createKeysSql ( $keys_new ));
}
2009-09-29 15:13:57 +00:00
$this -> connection -> query ( $sql );
2008-08-21 19:36:39 +00:00
}
2009-03-14 20:34:17 +00:00
public function prepareComment ( $comment , $length = NULL ) {
// Work around a bug in some versions of PDO, see http://bugs.php.net/bug.php?id=41125
$comment = str_replace ( " ' " , '’ ' , $comment );
2009-04-20 20:02:31 +00:00
2009-03-14 20:34:17 +00:00
// Truncate comment to maximum comment length.
if ( isset ( $length )) {
// Add table prefixes before truncating.
$comment = truncate_utf8 ( $this -> connection -> prefixTables ( $comment ), $length , TRUE , TRUE );
}
return $this -> connection -> quote ( $comment );
}
/**
* Retrieve a table or column comment .
*/
public function getComment ( $table , $column = NULL ) {
$condition = $this -> buildTableNameCondition ( $this -> connection -> prefixTables ( '{' . $table . '}' ));
if ( isset ( $column )) {
$condition -> condition ( 'column_name' , $column );
2009-08-29 05:43:35 +00:00
$condition -> compile ( $this -> connection , $this );
2009-05-29 01:51:46 +00:00
// Don't use {} around information_schema.columns table.
2009-09-29 15:13:57 +00:00
return $this -> connection -> query ( " SELECT column_comment FROM information_schema.columns WHERE " . ( string ) $condition , $condition -> arguments ()) -> fetchField ();
2009-03-14 20:34:17 +00:00
}
2009-08-29 05:43:35 +00:00
$condition -> compile ( $this -> connection , $this );
2009-05-29 01:51:46 +00:00
// Don't use {} around information_schema.tables table.
2009-09-29 15:13:57 +00:00
$comment = $this -> connection -> query ( " SELECT table_comment FROM information_schema.tables WHERE " . ( string ) $condition , $condition -> arguments ()) -> fetchField ();
2009-04-05 12:08:49 +00:00
// Work-around for MySQL 5.0 bug http://bugs.mysql.com/bug.php?id=11379
return preg_replace ( '/; InnoDB free:.*$/' , '' , $comment );
2009-03-14 20:34:17 +00:00
}
2008-08-21 19:36:39 +00:00
}
/**
* @ } End of " ingroup schemaapi " .
*/