|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Created on: <21-Apr-2004 11:04:30 kk> 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 /*! \file ezdbschemainterface.php 00030 Database schema abstraction layer. 00031 */ 00032 00033 /*! \defgroup eZDbSchema Database schema abstraction layer */ 00034 00035 /*! 00036 \class eZDBSchemaInterface ezdbschemainterface.php 00037 \ingroup eZDbSchema 00038 \brief This class provide interface for DB schema library 00039 00040 Schema structure an array with Table structures, each key is the name of the 00041 table. 00042 00043 Table structure: 00044 - name - Name of table 00045 - fields - Array of Field structures, each key is the field name 00046 - indexes - Array of Index structures, each key is the index name 00047 - removed - Contains whether the table has been removed or not (Optional) 00048 - comments - An array with comment strings (Optional) 00049 00050 The \c removed entry will only be used when some comments have been added 00051 to the table. That way the comments can be added to the DROP TABLE statements. 00052 00053 Field structure: 00054 - length - A number which defines the length/size of the type or \c false 00055 - type - String containing the identifier of the Type, see Types below. 00056 - not_null - Is 1 if the type cannot be null, if 0 or not defined it can be null 00057 - default - The default value, the value depends on the type, \c false means no default value. 00058 00059 Index structure: 00060 - type - What kind of index, see Index Types. 00061 - fields - Array of field names the index is made on 00062 00063 Index Types: 00064 - primary - A primary key, there can only be one primary key. This key will be named PRIMARY. 00065 - non-unique - A standard index 00066 - unique - A unique index 00067 00068 Field Types: 00069 - int - Integer, uses \c length to define number of digits. 00070 - float - Float, uses \c length to define number of digits. 00071 - auto_increment - Integer that auto increments (uses sequence+trigger). 00072 - varchar - String with variable length, uses \c length for max length. 00073 - char - String with fixed length, uses \c length. 00074 - longtext - String with 2^32 number of possible characters 00075 - mediumtext - String with 2^24 number of possible characters 00076 - shorttext - String with 2^16 number of possible characters 00077 00078 When stored as a PHP array the schema structure will be placed in a variable 00079 called $schema. The data structure will be placed in $data. 00080 00081 */ 00082 00083 class eZDBSchemaInterface 00084 { 00085 /*! 00086 Constructor 00087 00088 \param eZDB instance 00089 00090 \sa eZDB 00091 */ 00092 function eZDBSchemaInterface( $params ) 00093 { 00094 $this->DBInstance = $params['instance']; 00095 $this->Schema = false; 00096 $this->Data = false; 00097 if ( isset( $params['schema'] ) ) 00098 $this->Schema = $params['schema']; 00099 if ( isset( $params['data'] ) ) 00100 $this->Data = $params['data']; 00101 } 00102 00103 /*! 00104 \pure 00105 Get SQL db schema 00106 00107 \return DB schema array 00108 */ 00109 function schema( $params = array() ) 00110 { 00111 } 00112 00113 /*! 00114 \virtual 00115 Fetches the data for all tables and returns an array containing the data. 00116 00117 \param $schema A schema array which defines tables to fetch from. 00118 If \c false it will call schema() to fetch it. 00119 \param $tableNameList An array with tables to include, will further narrow 00120 tables in \a $scema. Use \c false to fetch all tables. 00121 00122 \note You shouldn't need to reimplement this method unless since the default 00123 code will do simple SELECT queries 00124 \sa fetchTableData() 00125 */ 00126 function data( $schema = false, $tableNameList = false, $params = array() ) 00127 { 00128 $params = array_merge( array( 'meta_data' => false, 00129 'format' => 'generic' ), 00130 $params ); 00131 00132 if ( $this->Data === false ) 00133 { 00134 if ( $schema === false ) 00135 $schema = $this->schema( $params ); 00136 00137 // We need to transform schema to local format for data to be fetched correctly. 00138 if ( $schema['_info']['format'] == 'generic' ) 00139 $this->transformSchema( $schema, true ); 00140 00141 $data = array(); 00142 foreach ( $schema as $tableName => $tableInfo ) 00143 { 00144 // Skip the information array, this is not a table 00145 if ( $tableName == '_info' ) 00146 continue; 00147 00148 if ( is_array( $tableNameList ) and 00149 !in_array( $tableName, $tableNameList ) ) 00150 continue; 00151 00152 $tableEntry = $this->fetchTableData( $tableInfo ); 00153 if ( count( $tableEntry['rows'] ) > 0 ) 00154 $data[$tableName] = $tableEntry; 00155 } 00156 $this->transformData( $data, $params['format'] == 'local' ); 00157 ksort( $data ); 00158 $this->Data = $data; 00159 } 00160 else 00161 { 00162 $this->transformData( $this->Data, $params['format'] == 'local' ); 00163 $data = $this->Data; 00164 } 00165 00166 return $data; 00167 } 00168 00169 /*! 00170 \virtual 00171 Validates the current schema and returns \c true if it is correct or 00172 \c false if something must be fixed. 00173 \note This should not be reimplemented by normal handlers, only schema 00174 checkers. 00175 */ 00176 function validate() 00177 { 00178 return false; 00179 } 00180 00181 /*! 00182 \virtual 00183 \protected 00184 Fetches all rows for table defined in \a $tableInfo and returns this structure: 00185 - fields - Array with fields that were fetched from table, the order of the fields 00186 are the same as the order of the data 00187 - rows - Array with all rows, each row is an indexed array with the data. 00188 00189 \param $tableInfo Table structure from schema. 00190 \param $offset Which offset to start from or \c false to start at top 00191 \param $limit How many rows to fetch or \c false for no limit. 00192 00193 \note You shouldn't need to reimplement this method unless since the default 00194 code will do simple SELECT queries 00195 \sa data() 00196 */ 00197 function fetchTableData( $tableInfo, $offset = false, $limit = false ) 00198 { 00199 if ( count( $tableInfo['fields'] ) == 0 ) 00200 return false; 00201 00202 $tableName = $tableInfo['name']; 00203 $fieldText = ''; 00204 $i = 0; 00205 $fields = array(); 00206 foreach ( $tableInfo['fields'] as $fieldName => $field ) 00207 { 00208 if ( $i > 0 ) 00209 $fieldText .= ', '; 00210 $fieldText .= $fieldName; 00211 $fields[] = $fieldName; 00212 ++$i; 00213 } 00214 $rows = $this->DBInstance->arrayQuery( "SELECT $fieldText FROM $tableName" ); 00215 $resultArray = array(); 00216 foreach ( $rows as $row ) 00217 { 00218 $rowData = array(); 00219 foreach ( $tableInfo['fields'] as $fieldName => $field ) 00220 { 00221 if ( $field['type'] == 'char' ) 00222 { 00223 $rowData[$fieldName] = str_pad( $row[$fieldName], $field['length'], ' ' ); 00224 } 00225 else 00226 { 00227 $rowData[$fieldName] = $row[$fieldName]; 00228 } 00229 } 00230 $resultArray[] = array_values( $rowData ); 00231 } 00232 return array( 'fields' => $fields, 00233 'rows' => $resultArray ); 00234 } 00235 00236 /*! 00237 \pure 00238 Write upgrade sql to file 00239 00240 \param difference array 00241 \param filename 00242 */ 00243 function writeUpgradeFile( $differences, $filename, $params = array() ) 00244 { 00245 $params = array_merge( array( 'schema' => true, 00246 'data' => false, 00247 'allow_multi_insert' => false, 00248 'diff_friendly' => false ), 00249 $params ); 00250 $fp = @fopen( $filename, 'w' ); 00251 if ( $fp ) 00252 { 00253 fputs( $fp, $this->generateUpgradeFile( $differences, $params ) ); 00254 fclose( $fp ); 00255 return true; 00256 } 00257 else 00258 { 00259 return false; 00260 } 00261 } 00262 00263 /*! 00264 Write SQL schema definition to file 00265 00266 \param filename 00267 */ 00268 function writeSQLSchemaFile( $filename, $params = array() ) 00269 { 00270 $params = array_merge( array( 'schema' => true, 00271 'data' => false, 00272 'allow_multi_insert' => false, 00273 'diff_friendly' => false ), 00274 $params ); 00275 $includeSchema = $params['schema']; 00276 $includeData = $params['data']; 00277 $fp = @fopen( $filename, 'w' ); 00278 if ( $fp ) 00279 { 00280 $schema = $this->schema( $params ); 00281 $this->transformSchema( $schema, true ); 00282 if ( $includeSchema ) 00283 { 00284 fputs( $fp, $this->generateSchemaFile( $schema, $params ) ); 00285 } 00286 if ( $includeData ) 00287 { 00288 $data = $this->data( $schema ); 00289 $this->transformData( $data, true ); 00290 fputs( $fp, $this->generateDataFile( $schema, $data, $params ) ); 00291 } 00292 fclose( $fp ); 00293 return true; 00294 } 00295 else 00296 { 00297 return false; 00298 } 00299 } 00300 00301 /*! 00302 Write PHP schema definition to file using PHP serialized format. 00303 00304 \param filename 00305 */ 00306 function writeSerializedSchemaFile( $filename, $params = array() ) 00307 { 00308 $params = array_merge( array( 'schema' => true, 00309 'data' => false ), 00310 $params ); 00311 $includeSchema = $params['schema']; 00312 $includeData = $params['data']; 00313 $fp = @fopen( $filename, 'w' ); 00314 if ( $fp ) 00315 { 00316 $schema = $this->schema( $params ); 00317 if ( $includeSchema and $includeData ) 00318 { 00319 fputs( $fp, serialize( array( 'schema' => $schema, 00320 'data' => $this->data( $schema ) ) ) ); 00321 } 00322 else if ( $includeSchema ) 00323 { 00324 fputs( $fp, serialize( $schema ) ); 00325 } 00326 else if ( $includeData ) 00327 { 00328 fputs( $fp, serialize( $this->data( $schema ) ) ); 00329 } 00330 fclose( $fp ); 00331 return true; 00332 } 00333 else 00334 { 00335 return false; 00336 } 00337 } 00338 00339 /*! 00340 Write PHP schema definition to file using PHP array structures. 00341 00342 \param filename 00343 */ 00344 function writeArraySchemaFile( $filename, $params = array() ) 00345 { 00346 $params = array_merge( array( 'schema' => true, 00347 'data' => false ), 00348 $params ); 00349 $includeSchema = $params['schema']; 00350 $includeData = $params['data']; 00351 $fp = @fopen( $filename, 'w' ); 00352 if ( $fp ) 00353 { 00354 $schema = $this->schema( $params ); 00355 fputs( $fp, '<?' . 'php' . "\n" ); 00356 if ( $includeSchema ) 00357 { 00358 fputs( $fp, "// This array contains the database schema\n" ); 00359 if ( isset( $schema['_info'] ) ) 00360 { 00361 $info = $schema['_info']; 00362 unset( $schema['_info'] ); 00363 $schema['_info'] = $info; 00364 } 00365 fputs( $fp, '$schema = ' . var_export( $schema, true ) . ";\n" ); 00366 } 00367 if ( $includeData ) 00368 { 00369 $data = $this->data( $schema ); 00370 fputs( $fp, "// This array contains the database data\n" ); 00371 if ( isset( $data['_info'] ) ) 00372 { 00373 $info = $data['_info']; 00374 unset( $data['_info'] ); 00375 $data['_info'] = $info; 00376 } 00377 fputs( $fp, '$data = ' . var_export( $data, true ) . ";\n" ); 00378 } 00379 fputs( $fp, "\n" . '?>' ); 00380 fclose( $fp ); 00381 return true; 00382 } 00383 else 00384 { 00385 return false; 00386 } 00387 } 00388 00389 /*! 00390 Insert PHP schema to the current database instance by running one SQL at a time. 00391 00392 \param $params Optional parameter which controls what to insert: 00393 - schema - Whether to insert the schema or not, default is \c true. 00394 - data - Whether to insert the data or not, default is \c false 00395 \return \c false if the schema could not be inserted, \c true if successful 00396 */ 00397 function insertSchema( $params = array() ) 00398 { 00399 $params = array_merge( array( 'schema' => true, 00400 'data' => false ), 00401 $params ); 00402 00403 if ( !is_object( $this->DBInstance ) ) 00404 { 00405 eZDebug::writeError( "No database instance is available, cannot insert", 'eZDBSchemaInterface::insertSchema' ); 00406 return false; 00407 } 00408 00409 $oldOutputSQL = $this->DBInstance->OutputSQL; 00410 $this->DBInstance->OutputSQL = false; 00411 00412 $includeSchema = $params['schema']; 00413 $includeData = $params['data']; 00414 $params['format'] = 'local'; 00415 $schema = $this->schema( $params ); 00416 if ( $includeSchema ) 00417 { 00418 foreach ( $schema as $tableName => $table ) 00419 { 00420 // Skip the information array, this is not a table 00421 if ( $tableName == '_info' ) 00422 continue; 00423 00424 $sqlList = $this->generateTableSQLList( $tableName, $table, $params, false ); 00425 foreach ( $sqlList as $sql ) 00426 { 00427 if ( !$this->DBInstance->query( $sql ) ) 00428 { 00429 eZDebug::writeError( "Failed inserting the SQL:\n$sql" ); 00430 return false; 00431 } 00432 } 00433 } 00434 } 00435 if ( $includeData ) 00436 { 00437 $data = $this->data( $schema, false, array( 'format' => 'local' ) ); 00438 foreach ( $schema as $tableName => $table ) 00439 { 00440 // Skip the information array, this is not a table 00441 if ( $tableName == '_info' ) 00442 continue; 00443 00444 if ( !isset( $data[$tableName] ) ) 00445 { 00446 continue; 00447 } 00448 00449 $sqlList = $this->generateTableInsertSQLList( $tableName, $table, $data[$tableName], $params, false ); 00450 foreach ( $sqlList as $sql ) 00451 { 00452 if ( !$this->DBInstance->query( $sql ) ) 00453 { 00454 eZDebug::writeError( "Failed inserting the SQL:\n$sql" ); 00455 return false; 00456 } 00457 } 00458 } 00459 00460 // Update sequences for databases that require this 00461 if ( method_exists( $this->DBInstance, 'correctSequenceValues' ) ) 00462 { 00463 $status = $this->DBInstance->correctSequenceValues(); 00464 if ( !$status ) 00465 return false; 00466 } 00467 } 00468 $this->DBInstance->OutputSQL = $oldOutputSQL; 00469 return true; 00470 } 00471 00472 /*! 00473 \private 00474 \param database schema 00475 \return schema for file output 00476 */ 00477 function generateDataFile( $schema, $data, $params ) 00478 { 00479 $params = array_merge( array( 'allow_multi_insert' => false, 00480 'diff_friendly' => false ), 00481 $params ); 00482 $sql = ''; 00483 00484 $i = 0; 00485 foreach ( $schema as $tableName => $tableDef ) 00486 { 00487 // Skip the info structure, this is not a table 00488 if ( $tableName == '_info' ) 00489 continue; 00490 00491 if ( !isset( $data[$tableName] ) ) 00492 continue; 00493 if ( $i > 0 ) 00494 $sql .= "\n\n"; 00495 $dataEntries = $data[$tableName]; 00496 $sql .= $this->generateTableInsert( $tableName, $tableDef, $dataEntries, $params ); 00497 ++$i; 00498 } 00499 00500 return $sql; 00501 } 00502 00503 /*! 00504 \private 00505 \param database schema 00506 \return schema for file output 00507 */ 00508 function generateSchemaFile( $schema, $params = array() ) 00509 { 00510 $sql = ''; 00511 00512 $i = 0; 00513 foreach ( $schema as $table => $tableDef ) 00514 { 00515 // Skip the info structure, this is not a table 00516 if ( $table == '_info' ) 00517 continue; 00518 00519 if ( $i > 0 ) 00520 $sql .= "\n\n"; 00521 $sql .= $this->generateTableSchema( $table, $tableDef, $params ); 00522 ++$i; 00523 } 00524 00525 return $sql; 00526 } 00527 00528 /*! 00529 * \private 00530 */ 00531 function generateUpgradeFile( $differences, $params = array() ) 00532 { 00533 $params = array_merge( array( 'schema' => true, 00534 'data' => false, 00535 'allow_multi_insert' => false, 00536 'diff_friendly' => false ), 00537 $params ); 00538 $sql = ''; 00539 00540 /* Loop over all 'table_changes' */ 00541 if ( isset( $differences['table_changes'] ) ) 00542 { 00543 foreach ( $differences['table_changes'] as $table => $table_diff ) 00544 { 00545 if ( isset ( $table_diff['added_fields'] ) ) 00546 { 00547 foreach ( $table_diff['added_fields'] as $field_name => $added_field ) 00548 { 00549 $this->appendSQLComments( $added_field, $sql ); 00550 $sql .= $this->generateAddFieldSql( $table, $field_name, $added_field, $params ); 00551 } 00552 } 00553 00554 if ( isset ( $table_diff['changed_fields'] ) ) 00555 { 00556 foreach ( $table_diff['changed_fields'] as $field_name => $changed_field ) 00557 { 00558 $changed_field_def = $changed_field['field-def']; 00559 $diffPrams = array_merge( $params, array( 'different-options' => $changed_field['different-options'] ) ); 00560 $this->appendSQLComments( $changed_field_def, $sql ); 00561 $sql .= $this->generateAlterFieldSql( $table, $field_name, $changed_field_def, $diffPrams ); 00562 unset( $diffPrams ); 00563 } 00564 } 00565 if ( isset ( $table_diff['removed_fields'] ) ) 00566 { 00567 foreach ( $table_diff['removed_fields'] as $field_name => $removed_field) 00568 { 00569 $this->appendSQLComments( $removed_field, $sql ); 00570 $sql .= $this->generateDropFieldSql( $table, $field_name, $params ); 00571 } 00572 } 00573 00574 if ( isset ( $table_diff['removed_indexes'] ) ) 00575 { 00576 foreach ( $table_diff['removed_indexes'] as $index_name => $removed_index) 00577 { 00578 $this->appendSQLComments( $removed_index, $sql ); 00579 $sql .= $this->generateDropIndexSql( $table, $index_name, $removed_index, $params ); 00580 } 00581 } 00582 if ( isset ( $table_diff['added_indexes'] ) ) 00583 { 00584 foreach ( $table_diff['added_indexes'] as $index_name => $added_index) 00585 { 00586 $this->appendSQLComments( $added_index, $sql ); 00587 $sql .= $this->generateAddIndexSql( $table, $index_name, $added_index, $params ); 00588 } 00589 } 00590 00591 if ( isset ( $table_diff['changed_indexes'] ) ) 00592 { 00593 foreach ( $table_diff['changed_indexes'] as $index_name => $changed_index ) 00594 { 00595 //eZDebug::writeDebug( $changed_index, "changed index $index_name" ); 00596 $this->appendSQLComments( $changed_index, $sql ); 00597 $sql .= $this->generateDropIndexSql( $table, $index_name, $changed_index, $params ); 00598 $sql .= $this->generateAddIndexSql( $table, $index_name, $changed_index, $params ); 00599 //eZDebug::writeDebug( 'qqq' ); 00600 } 00601 } 00602 } 00603 } 00604 if ( isset( $differences['new_tables'] ) ) 00605 { 00606 foreach ( $differences['new_tables'] as $table => $table_def ) 00607 { 00608 $this->appendSQLComments( $table_def, $sql ); 00609 $sql .= $this->generateTableSchema( $table, $table_def, $params ); 00610 } 00611 } 00612 if ( isset( $differences['removed_tables'] ) ) 00613 { 00614 foreach ( $differences['removed_tables'] as $table => $table_def ) 00615 { 00616 $this->appendSQLComments( $table_def, $sql ); 00617 $sql .= $this->generateDropTable( $table, $params ); 00618 } 00619 } 00620 return $sql; 00621 } 00622 00623 /*! 00624 \pure 00625 \protected 00626 Generates the necessary SQLs to create the table and returns them all in an array. 00627 00628 \param $tableName The table name 00629 \param $table The table structure, see class definition for more details 00630 \param $params An associative array with optional parameters which controls the output of SQLs 00631 \param $separateTypes If \c true then the returned array must be an associative array 00632 containing the SQL arrays split into multiple groups. 00633 The groups are: 00634 - sequences - List of sequences 00635 - tables - List of tables 00636 - trigger - List of triggers 00637 - indexes - List of indexes 00638 - constraints - List of constraints/primary keys 00639 - other - Other SQLs that doesn't fit into the above 00640 . 00641 Each group can be omitted and will be run in order. 00642 00643 \note Each SQL in the array will be without a semi-colon 00644 \sa generateTableSchema() 00645 */ 00646 function generateTableSQLList( $tableName, $table, $params, $separateTypes ) 00647 { 00648 return false; 00649 } 00650 00651 /*! 00652 \pure 00653 \protected 00654 Generates the necessary SQLs to create the table and returns them all in a string. 00655 00656 \param $tableName The table name 00657 \param $table The table structure, see class definition for more details 00658 00659 \note The SQLs will be ended with a semi-colon. 00660 \sa generateTableSQLList() 00661 */ 00662 function generateTableSchema( $tableName, $table, $params ) 00663 { 00664 return false; 00665 } 00666 00667 /*! 00668 \virtual 00669 \protected 00670 00671 \note Calls generateTableInsertSQLList and joins the SQLs to a string 00672 */ 00673 function generateTableInsert( $tableName, $tableDef, $dataEntries, $params ) 00674 { 00675 return join( "\n", $this->generateTableInsertSQLList( $tableName, $tableDef, $dataEntries, $params, true ) ); 00676 } 00677 00678 /*! 00679 \virtual 00680 \protected 00681 */ 00682 function generateTableInsertSQLList( $tableName, $tableDef, $dataEntries, $params, $withClosure = true ) 00683 { 00684 $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false; 00685 $multiInsert = ( isset( $params['allow_multi_insert'] ) and $params['allow_multi_insert'] ) ? $this->isMultiInsertSupported() : false; 00686 00687 // Make sure we don't generate SQL when there are no rows 00688 if ( count( $dataEntries['rows'] ) == 0 ) 00689 return ''; 00690 00691 $sqlList = array(); 00692 $sql = ''; 00693 $defText = ''; 00694 $entryIndex = 0; 00695 foreach ( $dataEntries['fields'] as $fieldName ) 00696 { 00697 if ( !isset( $tableDef['fields'][$fieldName] ) ) 00698 continue; 00699 if ( $entryIndex == 0 ) 00700 { 00701 if ( $diffFriendly ) 00702 { 00703 $defText .= " "; 00704 } 00705 } 00706 else 00707 { 00708 if ( $diffFriendly ) 00709 { 00710 $defText .= ",\n "; 00711 } 00712 else 00713 { 00714 $defText .= ", "; 00715 } 00716 } 00717 $defText .= $fieldName; 00718 ++$entryIndex; 00719 } 00720 00721 if ( $multiInsert ) 00722 { 00723 if ( $diffFriendly ) 00724 { 00725 $sql .= "INSERT INTO $tableName (\n $defText\n)\nVALUES\n"; 00726 } 00727 else 00728 { 00729 $sql .= "INSERT INTO $tableName ($defText) VALUES "; 00730 } 00731 } 00732 $insertIndex = 0; 00733 foreach ( $dataEntries['rows'] as $row ) 00734 { 00735 if ( $multiInsert and $insertIndex > 0 ) 00736 { 00737 if ( $diffFriendly ) 00738 $sql .= "\n,\n"; 00739 else 00740 $sql .= ", "; 00741 } 00742 $dataText = ''; 00743 $entryIndex = 0; 00744 foreach ( $dataEntries['fields'] as $fieldName ) 00745 { 00746 if ( !isset( $tableDef['fields'][$fieldName] ) ) 00747 continue; 00748 if ( $entryIndex == 0 ) 00749 { 00750 if ( $diffFriendly ) 00751 { 00752 $dataText .= " "; 00753 } 00754 } 00755 else 00756 { 00757 if ( $diffFriendly ) 00758 { 00759 $dataText .= ",\n "; 00760 } 00761 else 00762 { 00763 $dataText .= ","; 00764 } 00765 } 00766 $dataText .= $this->generateDataValueTextSQL( $tableDef['fields'][$fieldName], $row[$entryIndex] ); 00767 ++$entryIndex; 00768 } 00769 if ( $multiInsert ) 00770 { 00771 if ( $diffFriendly ) 00772 { 00773 $sql .= "(\n $dataText\n)"; 00774 } 00775 else 00776 { 00777 $sql .= "($dataText)"; 00778 } 00779 ++$insertIndex; 00780 } 00781 else 00782 { 00783 if ( $diffFriendly ) 00784 { 00785 $sqlList[] = "INSERT INTO $tableName (\n$defText\n) VALUES (\n$dataText\n)" . ( $withClosure ? ";" : "" ); 00786 } 00787 else 00788 { 00789 $sqlList[] = "INSERT INTO $tableName ($defText) VALUES ($dataText)" . ( $withClosure ? ";" : "" ); 00790 } 00791 } 00792 } 00793 if ( $multiInsert ) 00794 { 00795 if ( $withClosure ) 00796 $sql .= "\n;"; 00797 $sqlList[] = $sql; 00798 } 00799 return $sqlList; 00800 } 00801 00802 /*! 00803 \virtual 00804 \protected 00805 */ 00806 function generateDataValueTextSQL( $fieldDef, $value ) 00807 { 00808 if ( $fieldDef['type'] == 'auto_increment' or 00809 $fieldDef['type'] == 'int' or 00810 $fieldDef['type'] == 'float' ) 00811 { 00812 if ( $value === null or 00813 $value === false ) 00814 return "NULL"; 00815 $value = (int)$value; 00816 $value = (string)$value; 00817 return $value; 00818 } 00819 else if ( is_string( $value ) ) 00820 { 00821 return "'" . $this->escapeSQLString( $value ) . "'"; 00822 } 00823 else 00824 { 00825 if ( $value === null or 00826 $value === false ) 00827 return "NULL"; 00828 return (string)$value; 00829 } 00830 } 00831 00832 /*! 00833 \pure 00834 This escapes the string according to the current database type and returns it. 00835 \note The default just returns the value so it must be reimplemented. 00836 */ 00837 function escapeSQLString( $value ) 00838 { 00839 return $value; 00840 } 00841 00842 /*! 00843 \pure 00844 \protected 00845 */ 00846 function generateAlterFieldSql( $table_name, $field_name, $def, $params ) 00847 { 00848 } 00849 00850 /*! 00851 \pure 00852 \protected 00853 */ 00854 function generateAddFieldSql( $table_name, $field_name, $def, $params ) 00855 { 00856 } 00857 00858 /*! 00859 \private 00860 */ 00861 function generateDropFieldSql( $table_name, $field_name, $params ) 00862 { 00863 $sql = "ALTER TABLE $table_name DROP COLUMN $field_name"; 00864 00865 return $sql . ";\n"; 00866 } 00867 00868 /*! 00869 Appends any comments found in \a $def to SQL text \a $sql as SQL comments. 00870 \return \c true if any comments were added. 00871 */ 00872 function appendSQLComments( $def, &$sql ) 00873 { 00874 if ( isset( $def['comments'] ) ) 00875 { 00876 if ( count( $def['comments'] ) > 0 ) 00877 $sql .= "\n"; 00878 foreach ( $def['comments'] as $comment ) 00879 { 00880 $commentLines = explode( "\n", $comment ); 00881 foreach ( $commentLines as $commentLine ) 00882 { 00883 $sql .= '-- ' . $commentLine . "\n"; 00884 } 00885 } 00886 return true; 00887 } 00888 return false; 00889 } 00890 00891 /*! 00892 \virtual 00893 \protected 00894 \return \c true if the schema system supports multi inserts. 00895 The default is to return \c false. 00896 */ 00897 function isMultiInsertSupported() 00898 { 00899 return false; 00900 } 00901 00902 /*! 00903 \pure 00904 00905 \return Identifier for schema type as string. 00906 Examples: 'mysql', 'postgresql', 'oracle' 00907 \sa schemaName() 00908 */ 00909 function schemaType() 00910 { 00911 } 00912 00913 /*! 00914 \pure 00915 00916 \return Displayable name for schema type as string. 00917 Examples: 'MySQL', 'PostgreSQL', 'Oracle' 00918 \sa schemaType() 00919 */ 00920 function schemaName() 00921 { 00922 } 00923 00924 /*! 00925 \private 00926 \static 00927 \return array of transformation rules on success, false otherwise 00928 */ 00929 function loadSchemaTransformationRules( $schemaType ) 00930 { 00931 //include_once( 'lib/ezutils/classes/ezini.php' ); 00932 $ini = eZINI::instance( 'dbschema.ini' ); 00933 00934 if ( !$ini ) 00935 { 00936 eZDebug::writeError( "Error loading $schemaType schema transformation rules" ); 00937 return false; 00938 } 00939 00940 $transformationRules = array(); 00941 00942 if ( $ini->hasVariable( $schemaType, 'ColumnNameTranslation' ) ) 00943 $transformationRules['column-name'] = $ini->variable( $schemaType, 'ColumnNameTranslation' ); 00944 00945 if ( $ini->hasVariable( $schemaType, 'ColumnTypeTranslation' ) ) 00946 { 00947 $transformationRules['column-type'] = $ini->variable( $schemaType, 'ColumnTypeTranslation' ); 00948 00949 // substitute values like "type1;type2" with an appropriate arrays 00950 if ( is_array( $transformationRules['column-type'] ) ) 00951 { 00952 foreach ( $transformationRules['column-type'] as $key => $val ) 00953 { 00954 $types = explode( ';', $val ); 00955 $transformationRules['column-type'][$key] = $types; 00956 } 00957 } 00958 } 00959 00960 $indexTranslations = array(); 00961 if ( $ini->hasVariable( $schemaType, 'IndexTranslation' ) ) 00962 { 00963 $translations = $ini->variable( $schemaType, 'IndexTranslation' ); 00964 foreach ( $translations as $combinedName => $translation ) 00965 { 00966 list( $tableName, $indexName ) = explode( '.', $combinedName ); 00967 $indexTranslations[$tableName][$indexName] = array(); 00968 $fields = explode( ';', $translation ); 00969 foreach ( $fields as $field ) 00970 { 00971 $entries = explode( '.', $field ); 00972 $fieldName = $entries[0]; 00973 $fieldData = array(); 00974 for ( $i = 1; $i < count( $entries ); ++$i ) 00975 { 00976 list( $metaName, $metaValue ) = explode( '/', $entries[$i], 2 ); 00977 if ( is_numeric( $metaValue ) ) 00978 $metaValue = (int)$metaValue; 00979 $fieldData[$metaName] = $metaValue; 00980 } 00981 $indexTranslations[$tableName][$indexName][$fieldName] = $fieldData; 00982 } 00983 } 00984 } 00985 $transformationRules['index-field'] = $indexTranslations; 00986 00987 $tableTranslations = array(); 00988 if ( $ini->hasVariable( $schemaType, 'TableOptionTranslation' ) ) 00989 { 00990 $translations = $ini->variable( $schemaType, 'TableOptionTranslation' ); 00991 foreach ( $translations as $tableName => $optionTexts ) 00992 { 00993 $tableTranslations[$tableName] = array(); 00994 $options = explode( ';', $optionTexts ); 00995 $optionData = array(); 00996 foreach ( $options as $option ) 00997 { 00998 list( $metaName, $metaValue ) = explode( '/', $option, 2 ); 00999 if ( is_numeric( $metaValue ) ) 01000 $metaValue = (int)$metaValue; 01001 $optionData[$metaName] = $metaValue; 01002 $tableTranslations[$tableName] = $optionData; 01003 } 01004 } 01005 } 01006 $transformationRules['table-option'] = $tableTranslations; 01007 01008 if ( $ini->hasVariable( $schemaType, 'IndexNameTranslation' ) ) 01009 { 01010 $tmpIdxNameTranslations = $ini->variable( $schemaType, 'IndexNameTranslation' ); 01011 01012 if ( is_array( $tmpIdxNameTranslations ) ) 01013 { 01014 foreach ( $tmpIdxNameTranslations as $key => $val ) 01015 { 01016 list( $tableName, $genericIdxName ) = explode( '.', $key ); 01017 $localIdxName = $val; 01018 if ( !$tableName || !$genericIdxName || !$localIdxName ) 01019 { 01020 eZDebug::writeWarning( "Malformed index name translation rule: $key => $val" ); 01021 continue; 01022 } 01023 $transformationRules['index-name'][] = array( $tableName, $genericIdxName, $localIdxName ); 01024 01025 } 01026 } 01027 unset( $tmpIdxNameTranslations ); 01028 } 01029 01030 if ( $ini->hasVariable( $schemaType, 'ColumnOptionTranslations' ) ) 01031 { 01032 $transformationRules['column-option'] = array(); 01033 foreach( $ini->variable( $schemaType, 'ColumnOptionTranslations' ) as $key => $val ) 01034 { 01035 list( $tableName, $colName ) = explode( '.', $key ); 01036 $colOptOverride = $val; 01037 if ( !$tableName || !$colName || !$colOptOverride ) 01038 { 01039 eZDebug::writeWarning( "Malformed column option translation rule: $key => $val" ); 01040 continue; 01041 } 01042 $transformationRules['column-option'][] = array( $tableName, $colName, $colOptOverride ); 01043 } 01044 } 01045 01046 // prevent PHP warnings when cycling through the rules 01047 foreach ( array( 'column-name', 'column-type', 'column-option', 'index-name' ) as $rulesType ) 01048 { 01049 if( !isset( $transformationRules[$rulesType] ) ) 01050 $transformationRules[$rulesType] = array(); 01051 } 01052 01053 return $transformationRules; 01054 } 01055 01056 /*! 01057 \protected 01058 \virtual 01059 \return true on success, false otherwise 01060 01061 Transforms database schema to the given direction, applying the transformation rules. 01062 */ 01063 function transformSchema( &$schema, /* bool */ $toLocal ) 01064 { 01065 // Check if it is already in correct format 01066 if ( isset( $schema['_info']['format'] ) ) 01067 { 01068 if ( $schema['_info']['format'] == ( $toLocal ? 'local' : 'generic' ) ) 01069 return true; 01070 } 01071 01072 // Set the new format it will get 01073 $schema['_info']['format'] = $toLocal ? 'local' : 'generic'; 01074 01075 // load the schema transformation rules 01076 $schemaType = $this->schemaType(); 01077 $schemaTransformationRules = eZDBSchemaInterface::loadSchemaTransformationRules( $schemaType ); 01078 if ( $schemaTransformationRules === false ) 01079 return false; 01080 01081 // transform column names 01082 foreach ( $schemaTransformationRules['column-name'] as $key => $val ) 01083 { 01084 list( $tableName, $genericColName ) = explode( '.', $key ); 01085 $localColName = $val; 01086 01087 if ( $toLocal ) 01088 { 01089 $searchColName = $genericColName; 01090 $replacementColName = $localColName; 01091 } 01092 else 01093 { 01094 $searchColName = $localColName; 01095 $replacementColName = $genericColName; 01096 } 01097 01098 if ( !isset( $schema[$tableName] ) ) 01099 continue; 01100 01101 // transform column names in tables 01102 if ( isset( $schema[$tableName]['fields'][$searchColName] ) ) 01103 { 01104 $schema[$tableName]['fields'][$replacementColName] = $schema[$tableName]['fields'][$searchColName]; 01105 unset( $schema[$tableName]['fields'][$searchColName] ); 01106 ksort( $schema[$tableName]['fields'] ); 01107 eZDebugSetting::writeDebug( 'lib-dbschema-transformation', '', 01108 "transformed table column name $tableName.$searchColName to $replacementColName" ); 01109 } 01110 01111 01112 // transform column names in indexes 01113 foreach ( $schema[$tableName]['indexes'] as $indexName => $indexSchema ) 01114 { 01115 if ( ( $key = array_search( $searchColName, $indexSchema['fields'] ) ) !== false ) 01116 { 01117 $schema[$tableName]['indexes'][$indexName]['fields'][$key] = $replacementColName; 01118 eZDebugSetting::writeDebug( 'lib-dbschema-transformation', '', 01119 "transformed index columnn name $indexName.$searchColName to $replacementColName" ); 01120 } 01121 } 01122 } 01123 01124 // tranform column types 01125 foreach ( $schemaTransformationRules['column-type'] as $key => $val ) 01126 { 01127 list( $tableName, $colName ) = explode( '.', $key ); 01128 list( $genericType, $localType ) = $val; 01129 01130 if ( !isset( $schema[$tableName] ) ) 01131 continue; 01132 01133 preg_match( '/(\w+)\((\d+)\)/', $localType, $matches ); 01134 $localLength = ( count($matches) == 3 ) ? $matches[2] : null; 01135 if ( count($matches) == 3 ) 01136 $localType = $matches[1]; 01137 01138 preg_match( '/(\w+)\((\d+)\)/', $genericType, $matches ); 01139 $genericLength = ( count($matches) == 3 ) ? $matches[2] : null; 01140 if ( count($matches) == 3 ) 01141 $genericType = $matches[1]; 01142 01143 if ( !isset( $schema[$tableName]['fields'][$colName] ) ) 01144 continue; 01145 01146 $fieldSchema = $schema[$tableName]['fields'][$colName]; 01147 01148 if ( $toLocal ) 01149 { 01150 $searchType = $genericType; 01151 $searchLength = $genericLength; 01152 $replacementType = $localType; 01153 $replacementLength = $localLength; 01154 } 01155 else // to generic 01156 { 01157 $searchType = $localType; 01158 $searchLength = $localLength; 01159 $replacementType = $genericType; 01160 $replacementLength = $genericLength; 01161 } 01162 01163 $fieldSchema['type'] = $replacementType; 01164 if ( $replacementLength !== null ) 01165 { 01166 $fieldSchema['length'] = $replacementLength; 01167 } 01168 else 01169 { 01170 unset( $fieldSchema['length'] ); 01171 } 01172 01173 eZDebugSetting::writeDebug( 'lib-dbschema-transformation', '', 01174 "transformed table column type $schemaType:$tableName.$colName from $searchType to $replacementType" ); 01175 01176 $schema[$tableName]['fields'][$colName] = $fieldSchema; 01177 } 01178 01179 // Find indexes that needs to be fixed 01180 foreach ( $schemaTransformationRules['index-field'] as $tableName => $indexes ) 01181 { 01182 foreach ( $indexes as $indexName => $fields ) 01183 { 01184 if ( !isset( $schema[$tableName]['indexes'][$indexName]['fields'] ) ) 01185 continue; 01186 01187 $newFields = array(); 01188 foreach ( $schema[$tableName]['indexes'][$indexName]['fields'] as $indexField ) 01189 { 01190 if ( !is_array( $indexField ) ) 01191 { 01192 $indexField = array( 'name' => $indexField ); 01193 } 01194 $fieldName = $indexField['name']; 01195 if ( isset( $fields[$fieldName] ) ) 01196 { 01197 if ( $toLocal ) 01198 { 01199 $indexField = array_merge( $indexField, 01200 $fields[$fieldName] ); 01201 } 01202 else 01203 { 01204 foreach ( $fields[$fieldName] as $removeName => $removeValue ) 01205 { 01206 unset( $indexField[$removeName] ); 01207 } 01208 } 01209 } 01210 01211 // Check if we have any entries other than 'name', if not we skip the array definition 01212 if ( count( array_diff( array_keys( $indexField ), array( 'name' ) ) ) == 0 ) 01213 { 01214 $indexField = $indexField['name']; 01215 } 01216 $newFields[] = $indexField; 01217 } 01218 $schema[$tableName]['indexes'][$indexName]['fields'] = $newFields; 01219 } 01220 } 01221 01222 // Find tables that needs to fix their options 01223 foreach ( $schemaTransformationRules['table-option'] as $tableName => $options ) 01224 { 01225 if ( !isset( $schema[$tableName] ) ) 01226 continue; 01227 01228 if ( !isset( $schema[$tableName]['options'] ) ) 01229 { 01230 if ( $toLocal ) 01231 $schema[$tableName]['options'] = $options; 01232 } 01233 else 01234 { 01235 if ( $toLocal ) 01236 { 01237 $schema[$tableName]['options'] = array_merge( $schema[$tableName]['options'], $options ); 01238 } 01239 else 01240 { 01241 foreach ( $options as $optionName => $optionValue ) 01242 { 01243 unset( $schema[$tableName]['options'][$optionName] ); 01244 } 01245 } 01246 } 01247 } 01248 01249 // Transform index names 01250 foreach ( $schemaTransformationRules['index-name'] as $idxTransRule ) 01251 { 01252 list( $tableName, $genericIdxName, $localIdxName ) = $idxTransRule; 01253 01254 if ( $toLocal ) 01255 { 01256 $searchIdxName = $genericIdxName; 01257 $replacementIdxName = $localIdxName; 01258 } 01259 else 01260 { 01261 $searchIdxName = $localIdxName; 01262 $replacementIdxName = $genericIdxName; 01263 } 01264 01265 if ( !isset( $schema[$tableName] ) ) 01266 continue; 01267 01268 if ( isset( $schema[$tableName]['indexes'][$searchIdxName] ) ) 01269 { 01270 eZDebugSetting::writeDebug( 'lib-dbschema-transformation', '', 01271 "replaced $tableName.$searchIdxName => $replacementIdxName" ); 01272 $schema[$tableName]['indexes'][$replacementIdxName] = $schema[$tableName]['indexes'][$searchIdxName]; 01273 unset( $schema[$tableName]['indexes'][$searchIdxName] ); 01274 ksort( $schema[$tableName]['indexes'] ); 01275 } 01276 01277 } 01278 01279 // Transform table column options 01280 foreach ( $schemaTransformationRules['column-option'] as $colOptTransRule ) 01281 { 01282 list( $tableName, $colName, $colOptOverride ) = $colOptTransRule; 01283 01284 if ( !isset( $schema[$tableName] ) || !isset( $schema[$tableName]['fields'][$colName] ) ) 01285 continue; 01286 01287 $fieldSchema = $schema[$tableName]['fields'][$colName]; 01288 01289 switch ( $colOptOverride ) 01290 { 01291 case 'null': 01292 { 01293 if ( $toLocal ) 01294 { 01295 // remove "NOT NULL" requirement 01296 unset( $fieldSchema['not_null'] ); 01297 eZDebugSetting::writeDebug( 'lib-dbschema-transformation', '', 01298 "transformed table column option: $schemaType:$tableName.$colName set to NULL" ); 01299 } 01300 else 01301 { 01302 // add "NOT NULL" requirement 01303 $fieldSchema['not_null'] = '1'; 01304 eZDebugSetting::writeDebug( 'lib-dbschema-transformation', '', 01305 "transformed table column option: $schemaType:$tableName.$colName set to NOT NULL" ); 01306 } 01307 01308 // FIXME: ugly hack preserving keys order in the field schema array, just to be diff-friendly 01309 { 01310 $tmp = $fieldSchema['default']; 01311 unset( $fieldSchema['default'] ); 01312 $fieldSchema['default'] = $tmp; 01313 } 01314 } break; 01315 default: 01316 { 01317 eZDebug::writeWarning( "Column option override '$colOptOverride' is not supported" ); 01318 } break; 01319 } 01320 01321 $schema[$tableName]['fields'][$colName] = $fieldSchema; 01322 } 01323 01324 01325 return true; 01326 } 01327 01328 /*! 01329 \protected 01330 \virtual 01331 \return true on success, false otherwise 01332 01333 Transforms database data to the given direction, applying the transformation rules. 01334 */ 01335 function transformData( &$data, /* bool */ $toLocal ) 01336 { 01337 // Check if it is already in correct format 01338 if ( isset( $data['_info']['format'] ) ) 01339 { 01340 if ( $data['_info']['format'] == ( $toLocal ? 'local' : 'generic' ) ) 01341 return true; 01342 } 01343 01344 // Set the new format it will get 01345 $data['_info']['format'] = $toLocal ? 'local' : 'generic'; 01346 01347 // load the schema transformation rules 01348 $schemaType = $this->schemaType(); 01349 $schemaTransformationRules = eZDBSchemaInterface::loadSchemaTransformationRules( $schemaType ); 01350 if ( $schemaTransformationRules === false ) 01351 return false; 01352 01353 // transform column names 01354 foreach ( $schemaTransformationRules['column-name'] as $key => $val ) 01355 { 01356 list( $tableName, $genericColName ) = explode( '.', $key ); 01357 $localColName = $val; 01358 01359 if ( $toLocal ) 01360 { 01361 $searchColName = $genericColName; 01362 $replacementColName = $localColName; 01363 } 01364 else 01365 { 01366 $searchColName = $localColName; 01367 $replacementColName = $genericColName; 01368 } 01369 01370 if ( !isset( $data[$tableName] ) ) 01371 continue; 01372 01373 // transform column names in tables 01374 $fieldsData = $data[$tableName]['fields']; 01375 foreach ( $fieldsData as $key => $fieldName ) 01376 { 01377 if ( $searchColName == $fieldName ) 01378 { 01379 $data[$tableName]['fields'][$key] = $replacementColName; 01380 eZDebugSetting::writeDebug( 'lib-dbschema-data-transformation', '', 01381 "transformed table column name $tableName.$searchColName to $replacementColName" ); 01382 } 01383 } 01384 } 01385 01386 return true; 01387 } 01388 01389 /// eZDB instance 01390 public $DBInstance; 01391 public $Schema; 01392 public $Data; 01393 } 01394 01395 ?>