|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Created on: <30-Jan-2004 10:14:58 dr> 00004 // 00005 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00006 // SOFTWARE NAME: eZ Publish 00007 // SOFTWARE RELEASE: 4.0.x 00008 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS 00009 // SOFTWARE LICENSE: GNU General Public License v2.0 00010 // NOTICE: > 00011 // This program is free software; you can redistribute it and/or 00012 // modify it under the terms of version 2.0 of the GNU General 00013 // Public License as published by the Free Software Foundation. 00014 // 00015 // This program is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 // GNU General Public License for more details. 00019 // 00020 // You should have received a copy of version 2.0 of the GNU General 00021 // Public License along with this program; if not, write to the Free 00022 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00023 // MA 02110-1301, USA. 00024 // 00025 // 00026 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00027 // 00028 00029 /*! 00030 \class eZMysqlSchema ezmysqlschema.php 00031 \ingroup eZDbSchema 00032 \brief Handles schemas for MySQL 00033 00034 */ 00035 00036 //include_once( 'lib/ezdbschema/classes/ezdbschemainterface.php' ); 00037 00038 class eZMysqlSchema extends eZDBSchemaInterface 00039 { 00040 00041 /*! 00042 \reimp 00043 Constructor 00044 00045 \param db instance 00046 */ 00047 function eZMysqlSchema( $params ) 00048 { 00049 $this->eZDBSchemaInterface( $params ); 00050 } 00051 00052 /*! 00053 \reimp 00054 */ 00055 function schema( $params = array() ) 00056 { 00057 $params = array_merge( array( 'meta_data' => false, 00058 'format' => 'generic' ), 00059 $params ); 00060 $schema = array(); 00061 00062 if ( $this->Schema === false ) 00063 { 00064 $tableArray = $this->DBInstance->arrayQuery( "SHOW TABLES" ); 00065 00066 foreach( $tableArray as $tableNameArray ) 00067 { 00068 $table_name = current( $tableNameArray ); 00069 if ( !isset( $params['table_include'] ) or 00070 ( is_array( $params['table_include'] ) and 00071 in_array( $table_name, $params['table_include'] ) ) ) 00072 { 00073 $schema_table['name'] = $table_name; 00074 $schema_table['fields'] = $this->fetchTableFields( $table_name, $params ); 00075 $schema_table['indexes'] = $this->fetchTableIndexes( $table_name, $params ); 00076 00077 $schema[$table_name] = $schema_table; 00078 } 00079 } 00080 $this->transformSchema( $schema, $params['format'] == 'local' ); 00081 ksort( $schema ); 00082 $this->Schema = $schema; 00083 } 00084 else 00085 { 00086 $this->transformSchema( $this->Schema, $params['format'] == 'local' ); 00087 $schema = $this->Schema; 00088 } 00089 return $schema; 00090 } 00091 00092 /*! 00093 \private 00094 00095 \param table name 00096 */ 00097 function fetchTableFields( $table, $params ) 00098 { 00099 $fields = array(); 00100 00101 $resultArray = $this->DBInstance->arrayQuery( "DESCRIBE $table" ); 00102 00103 foreach( $resultArray as $row ) 00104 { 00105 $field = array(); 00106 $field['type'] = $this->parseType ( $row['Type'], $field['length'] ); 00107 if ( !$field['length'] ) 00108 { 00109 unset( $field['length'] ); 00110 } 00111 $field['not_null'] = 0; 00112 if ( $row['Null'] != 'YES' ) 00113 { 00114 $field['not_null'] = '1'; 00115 } 00116 $field['default'] = false; 00117 if ( !$field['not_null'] ) 00118 { 00119 if ( $row['Default'] === null ) 00120 $field['default'] = null; 00121 else 00122 $field['default'] = (string)$row['Default']; 00123 } 00124 else 00125 { 00126 $field['default'] = (string)$row['Default']; 00127 } 00128 00129 $numericTypes = array( 'float', 'int' ); 00130 $blobTypes = array( 'tinytext', 'text', 'mediumtext', 'longtext' ); 00131 $charTypes = array( 'varchar', 'char' ); 00132 if ( in_array( $field['type'], $charTypes ) ) 00133 { 00134 if ( !$field['not_null'] ) 00135 { 00136 if ( $field['default'] === null ) 00137 { 00138 $field['default'] = null; 00139 } 00140 else if ( $field['default'] === false ) 00141 { 00142 $field['default'] = ''; 00143 } 00144 } 00145 } 00146 else if ( in_array( $field['type'], $numericTypes ) ) 00147 { 00148 if ( $field['default'] === false ) 00149 { 00150 if ( $field['not_null'] ) 00151 { 00152 $field['default'] = 0; 00153 } 00154 } 00155 else if ( $field['type'] == 'int' ) 00156 { 00157 if ( $field['not_null'] or 00158 is_numeric( $field['default'] ) ) 00159 $field['default'] = (int)$field['default']; 00160 } 00161 else if ( $field['type'] == 'float' or 00162 is_numeric( $field['default'] ) ) 00163 { 00164 if ( $field['not_null'] or 00165 is_numeric( $field['default'] ) ) 00166 $field['default'] = (float)$field['default']; 00167 } 00168 } 00169 else if ( in_array( $field['type'], $blobTypes ) ) 00170 { 00171 // We do not want default for blobs. 00172 $field['default'] = false; 00173 } 00174 00175 if ( strpos ( $row['Extra'], 'auto_increment' ) !== false ) 00176 { 00177 unset( $field['length'] ); 00178 $field['not_null'] = 0; 00179 $field['default'] = false; 00180 $field['type'] = 'auto_increment'; 00181 } 00182 00183 if ( !$field['not_null'] ) 00184 unset( $field['not_null'] ); 00185 00186 $fields[$row['Field']] = $field; 00187 } 00188 ksort( $fields ); 00189 00190 return $fields; 00191 } 00192 00193 /*! 00194 * \private 00195 */ 00196 function fetchTableIndexes( $table, $params ) 00197 { 00198 $metaData = false; 00199 if ( isset( $params['meta_data'] ) ) 00200 { 00201 $metaData = $params['meta_data']; 00202 } 00203 00204 $indexes = array(); 00205 00206 $resultArray = $this->DBInstance->arrayQuery( "SHOW INDEX FROM $table" ); 00207 00208 foreach( $resultArray as $row ) 00209 { 00210 $kn = $row['Key_name']; 00211 00212 if ( $kn == 'PRIMARY' ) 00213 { 00214 $indexes[$kn]['type'] = 'primary'; 00215 } 00216 else 00217 { 00218 $indexes[$kn]['type'] = $row['Non_unique'] ? 'non-unique' : 'unique'; 00219 } 00220 00221 $indexFieldDef = array( 'name' => $row['Column_name'] ); 00222 00223 // Include length if one is defined 00224 if ( $row['Sub_part'] ) 00225 { 00226 $indexFieldDef['mysql:length'] = (int)$row['Sub_part']; 00227 } 00228 00229 // Check if we have any entries other than 'name', if not we skip the array definition 00230 if ( count( array_diff( array_keys( $indexFieldDef ), array( 'name' ) ) ) == 0 ) 00231 { 00232 $indexFieldDef = $indexFieldDef['name']; 00233 } 00234 $indexes[$kn]['fields'][$row['Seq_in_index'] - 1] = $indexFieldDef; 00235 } 00236 ksort( $indexes ); 00237 00238 return $indexes; 00239 } 00240 00241 function parseType( $type_info, &$length_info ) 00242 { 00243 preg_match ( "@([a-z ]*)(\(([0-9]*|[0-9]*,[0-9]*)\))?@", $type_info, $matches ); 00244 if ( isset( $matches[3] ) ) 00245 { 00246 $length_info = $matches[3]; 00247 if ( is_numeric( $length_info ) ) 00248 $length_info = (int)$length_info; 00249 } 00250 return $matches[1]; 00251 } 00252 00253 /*! 00254 * \private 00255 */ 00256 function generateAddIndexSql( $table_name, $index_name, $def, $params, $isEmbedded = false ) 00257 { 00258 $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false; 00259 // If the output should compatible with existing MySQL dumps 00260 $mysqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false; 00261 $sql = ''; 00262 00263 // Will be set to true when primary key is inside CREATE TABLE 00264 if ( !$isEmbedded ) 00265 { 00266 $sql .= "ALTER TABLE $table_name ADD"; 00267 $sql .= " "; 00268 } 00269 00270 switch ( $def['type'] ) 00271 { 00272 case 'primary': 00273 { 00274 $sql .= 'PRIMARY KEY'; 00275 if ( $mysqlCompatible ) 00276 $sql .= " "; 00277 } break; 00278 00279 case 'non-unique': 00280 { 00281 if ( $isEmbedded ) 00282 { 00283 $sql .= "KEY $index_name"; 00284 } 00285 else 00286 { 00287 $sql .= "INDEX $index_name"; 00288 } 00289 } break; 00290 00291 case 'unique': 00292 { 00293 if ( $isEmbedded ) 00294 { 00295 $sql .= "UNIQUE KEY $index_name"; 00296 } 00297 else 00298 { 00299 $sql .= "UNIQUE $index_name"; 00300 } 00301 } break; 00302 } 00303 00304 $sql .= ( $diffFriendly ? " (\n " : ( $mysqlCompatible ? " (" : " ( " ) ); 00305 $fields = $def['fields']; 00306 $i = 0; 00307 foreach ( $fields as $fieldDef ) 00308 { 00309 if ( $i > 0 ) 00310 { 00311 $sql .= $diffFriendly ? ",\n " : ( $mysqlCompatible ? ',' : ', ' ); 00312 } 00313 if ( is_array( $fieldDef ) ) 00314 { 00315 $sql .= $fieldDef['name']; 00316 if ( isset( $fieldDef['mysql:length'] ) ) 00317 { 00318 if ( $diffFriendly ) 00319 { 00320 $sql .= "(\n"; 00321 $sql .= " " . str_repeat( ' ', strlen( $fieldDef['name'] ) ); 00322 } 00323 else 00324 { 00325 $sql .= $mysqlCompatible ? "(" : "( "; 00326 } 00327 $sql .= $fieldDef['mysql:length']; 00328 if ( $diffFriendly ) 00329 { 00330 $sql .= ")"; 00331 } 00332 else 00333 { 00334 $sql .= $mysqlCompatible ? ")" : " )"; 00335 } 00336 } 00337 } 00338 else 00339 { 00340 $sql .= $fieldDef; 00341 } 00342 ++$i; 00343 } 00344 $sql .= ( $diffFriendly ? "\n)" : ( $mysqlCompatible ? ")" : " )" ) ); 00345 00346 if ( !$isEmbedded ) 00347 { 00348 return $sql . ";\n"; 00349 } 00350 return $sql; 00351 } 00352 00353 /*! 00354 * \private 00355 */ 00356 function generateDropIndexSql( $table_name, $index_name, $def, $params ) 00357 { 00358 $sql = ''; 00359 $sql .= "ALTER TABLE $table_name DROP "; 00360 00361 if ( $def['type'] == 'primary' ) 00362 { 00363 $sql .= 'PRIMARY KEY'; 00364 } 00365 else 00366 { 00367 $sql .= "INDEX $index_name"; 00368 } 00369 return $sql . ";\n"; 00370 } 00371 00372 /*! 00373 * \private 00374 */ 00375 function generateFieldDef( $field_name, $def, &$skip_primary, $params = null ) 00376 { 00377 $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false; 00378 // If the output should compatible with existing MySQL dumps 00379 $mysqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false; 00380 00381 $sql_def = $field_name . ' '; 00382 $defaultText = $mysqlCompatible ? "default" : "DEFAULT"; 00383 00384 if ( $def['type'] != 'auto_increment' ) 00385 { 00386 $defList = array(); 00387 $type = $def['type']; 00388 if ( isset( $def['length'] ) ) 00389 { 00390 $type .= "({$def['length']})"; 00391 } 00392 $defList[] = $type; 00393 if ( isset( $def['not_null'] ) && ( $def['not_null'] ) ) 00394 { 00395 $defList[] = 'NOT NULL'; 00396 } 00397 if ( array_key_exists( 'default', $def ) ) 00398 { 00399 if ( $def['default'] === null ) 00400 { 00401 $defList[] = "$defaultText NULL"; 00402 } 00403 else if ( $def['default'] !== false ) 00404 { 00405 $defList[] = "$defaultText '{$def['default']}'"; 00406 } 00407 } 00408 else if ( $def['type'] == 'varchar' ) 00409 { 00410 $defList[] = "$defaultText ''"; 00411 } 00412 $sql_def .= join( $diffFriendly ? "\n " : " ", $defList ); 00413 $skip_primary = false; 00414 } 00415 else 00416 { 00417 $incrementText = $mysqlCompatible ? "auto_increment" : "AUTO_INCREMENT"; 00418 if ( $diffFriendly ) 00419 { 00420 $sql_def .= "int(11)\n NOT NULL\n $incrementText"; 00421 } 00422 else 00423 { 00424 $sql_def .= "int(11) NOT NULL $incrementText"; 00425 } 00426 $skip_primary = true; 00427 } 00428 return $sql_def; 00429 } 00430 00431 /*! 00432 * \private 00433 */ 00434 function generateAddFieldSql( $table_name, $field_name, $def, $params ) 00435 { 00436 $sql = "ALTER TABLE $table_name ADD COLUMN "; 00437 $sql .= eZMysqlSchema::generateFieldDef ( $field_name, $def, $dummy ); 00438 00439 return $sql . ";\n"; 00440 } 00441 00442 /*! 00443 * \private 00444 */ 00445 function generateAlterFieldSql( $table_name, $field_name, $def, $params ) 00446 { 00447 $sql = "ALTER TABLE $table_name CHANGE COLUMN $field_name "; 00448 $sql .= eZMysqlSchema::generateFieldDef ( $field_name, $def, $dummy ); 00449 00450 return $sql . ";\n"; 00451 } 00452 00453 /*! 00454 \reimp 00455 \note Calls generateTableSQL() with \a $asArray set to \c false 00456 */ 00457 function generateTableSchema( $tableName, $table, $params ) 00458 { 00459 return $this->generateTableSQL( $tableName, $table, $params, false, false ); 00460 } 00461 00462 /*! 00463 \reimp 00464 \note Calls generateTableSQL() with \a $asArray set to \c true 00465 */ 00466 function generateTableSQLList( $tableName, $table, $params, $separateTypes ) 00467 { 00468 return $this->generateTableSQL( $tableName, $table, $params, true, $separateTypes ); 00469 } 00470 00471 /*! 00472 \private 00473 00474 \param $asArray If \c true all SQLs are return in an array, 00475 if not they are returned as a string. 00476 \note When returned as array the SQLs will not have a semi-colon to end the statement 00477 */ 00478 function generateTableSQL( $tableName, $tableDef, $params, $asArray, $separateTypes = false ) 00479 { 00480 $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false; 00481 $mysqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false; 00482 00483 if ( $asArray ) 00484 { 00485 if ( $separateTypes ) 00486 { 00487 $sqlList = array( 'tables' => array() ); 00488 } 00489 else 00490 { 00491 $sqlList = array(); 00492 } 00493 } 00494 00495 $sql = ''; 00496 $skip_pk = false; 00497 $sql_fields = array(); 00498 $sql .= "CREATE TABLE $tableName (\n"; 00499 00500 $fields = $tableDef['fields']; 00501 00502 foreach ( $fields as $field_name => $field_def ) 00503 { 00504 $sql_fields[] = ' ' . eZMysqlSchema::generateFieldDef( $field_name, $field_def, $skip_pk_flag, $params ); 00505 if ( $skip_pk_flag ) 00506 { 00507 $skip_pk = true; 00508 } 00509 } 00510 00511 // Make sure the order is as defined by 'offset' 00512 $indexes = $tableDef['indexes']; 00513 00514 // We need to add all keys in table definition 00515 foreach ( $indexes as $index_name => $index_def ) 00516 { 00517 $sql_fields[] = ( $diffFriendly ? '' : ' ' ) . eZMysqlSchema::generateAddIndexSql( $tableName, $index_name, $index_def, $params, true ); 00518 } 00519 $sql .= join( ",\n", $sql_fields ); 00520 $sql .= "\n)"; 00521 00522 // Add some extra table options if they are required 00523 $extraOptions = array(); 00524 if ( isset( $params['table_type'] ) and $params['table_type'] ) 00525 { 00526 $typeName = $this->tableStorageTypeName( $params['table_type'] ); 00527 if ( $typeName ) 00528 { 00529 $extraOptions[] = "ENGINE=" . $typeName; 00530 } 00531 } 00532 if ( isset( $params['table_charset'] ) and $params['table_charset'] ) 00533 { 00534 $charsetName = $this->tableCharsetName( $params['table_charset'] ); 00535 if ( $charsetName ) 00536 { 00537 $extraOptions[] = "DEFAULT CHARACTER SET " . $charsetName; 00538 } 00539 } 00540 if ( isset( $tableDef['options'] ) ) 00541 { 00542 foreach( $tableDef['options'] as $optionType => $optionValue ) 00543 { 00544 $optionText = $this->generateTableOption( $tableName, $tableDef, $optionType, $optionValue, $params ); 00545 if ( $optionText ) 00546 $extraOptions[] = $optionText; 00547 } 00548 } 00549 00550 if ( count( $extraOptions ) > 0 ) 00551 { 00552 $sql .= " " . implode( $diffFriendly ? "\n" : " ", $extraOptions ); 00553 } 00554 00555 if ( $asArray ) 00556 { 00557 if ( $separateTypes ) 00558 { 00559 $sqlList['tables'][] = $sql; 00560 } 00561 else 00562 { 00563 $sqlList[] = $sql; 00564 } 00565 } 00566 else 00567 { 00568 $sql .= ";\n"; 00569 00570 if ( $mysqlCompatible ) 00571 { 00572 $sql .= "\n\n\n"; 00573 } 00574 } 00575 00576 return $asArray ? $sqlList : $sql; 00577 } 00578 00579 /*! 00580 Detects known options and generates the MySQL SQL code for it. 00581 \return The SQL code as a string or \c false if not known. 00582 \param $optionType The type of option, the supported ones are: 00583 - delay_key_write - If \a $optionValue is true then adds DELAY_KEY_WRITE=1 00584 */ 00585 function generateTableOption( $tableName, $tableDef, $optionType, $optionValue, $params ) 00586 { 00587 switch ( $optionType ) 00588 { 00589 case 'mysql:delay_key_write': 00590 { 00591 if ( $optionValue ) 00592 return 'DELAY_KEY_WRITE=1'; 00593 } break; 00594 } 00595 return false; 00596 } 00597 00598 /*! 00599 \return The name of the charset \a $charset in a format MySQL understands. 00600 */ 00601 function tableCharsetName( $charset ) 00602 { 00603 //include_once( 'lib/ezi18n/classes/ezcharsetinfo.php' ); 00604 $charset = eZCharsetInfo::realCharsetCode( $charset ); 00605 // Convert charset names into something MySQL will understand 00606 $charsetMapping = array( 'iso-8859-1' => 'latin1', 00607 'iso-8859-2' => 'latin2', 00608 'iso-8859-8' => 'hebrew', 00609 'iso-8859-7' => 'greek', 00610 'iso-8859-9' => 'latin5', 00611 'iso-8859-13' => 'latin7', 00612 'windows-1250' => 'cp1250', 00613 'windows-1251' => 'cp1251', 00614 'windows-1256' => 'cp1256', 00615 'windows-1257' => 'cp1257', 00616 'utf-8' => 'utf8', 00617 'koi8-r' => 'koi8r', 00618 'koi8-u' => 'koi8u' ); 00619 $charset = strtolower( $charset ); 00620 if ( isset( $charsetMapping ) ) 00621 return $charsetMapping[$charset]; 00622 return $charset; 00623 } 00624 00625 /*! 00626 \return The name of storage type \a $type or \c false if not supported. 00627 00628 \note Currently supports \c bdb, \c myisam and \c innodb. 00629 00630 See http://dev.mysql.com/doc/mysql/en/CREATE_TABLE.html for overview of the types MySQL supports 00631 */ 00632 function tableStorageTypeName( $type ) 00633 { 00634 $type = strtolower( $type ); 00635 switch ( $type ) 00636 { 00637 case 'bdb': 00638 { 00639 return 'BDB'; 00640 } 00641 00642 case 'myisam': 00643 { 00644 return 'MyISAM'; 00645 } 00646 00647 case 'innodb': 00648 { 00649 return 'InnoDB'; 00650 } 00651 } 00652 return false; 00653 } 00654 00655 /*! 00656 * \private 00657 */ 00658 function generateDropTable( $table, $params ) 00659 { 00660 return "DROP TABLE $table;\n"; 00661 } 00662 00663 /*! 00664 \reimp 00665 MySQL 3.22.5 and higher support multi-insert queries so if the current 00666 database has sufficient version we return \c true. 00667 If no database is connected we return \true. 00668 */ 00669 function isMultiInsertSupported() 00670 { 00671 if ( $this->DBInstance instanceof eZDBInterface ) 00672 { 00673 $versionInfo = $this->DBInstance->databaseServerVersion(); 00674 00675 // We require MySQL 3.22.5 to use multi-insert queries 00676 // http://dev.mysql.com/doc/mysql/en/INSERT.html 00677 return ( version_compare( $versionInfo['string'], '3.22.5' ) >= 0 ); 00678 } 00679 return true; 00680 } 00681 00682 /*! 00683 \reimp 00684 */ 00685 function escapeSQLString( $value ) 00686 { 00687 if ( $this->DBInstance instanceof eZDBInterface ) 00688 { 00689 return $this->DBInstance->escapeString( $value ); 00690 } 00691 00692 return $value; 00693 } 00694 00695 /*! 00696 \reimp 00697 */ 00698 function schemaType() 00699 { 00700 return 'mysql'; 00701 } 00702 00703 /*! 00704 \reimp 00705 */ 00706 function schemaName() 00707 { 00708 return 'MySQL'; 00709 } 00710 00711 } 00712 ?>