|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZMySQLiDB class. 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package lib 00009 */ 00010 00011 /*! 00012 \class eZMySQLiDB eZMySQLiDB.php 00013 \ingroup eZDB 00014 \brief The eZMySQLiDB class provides MySQL implementation of the database interface. 00015 00016 eZMySQLiDB is the MySQL implementation of eZDB. 00017 \sa eZDB 00018 */ 00019 00020 class eZMySQLiDB extends eZDBInterface 00021 { 00022 const RELATION_FOREIGN_KEY = 5; 00023 const RELATION_FOREIGN_KEY_BIT = 32; 00024 00025 /*! 00026 Create a new eZMySQLiDB object and connects to the database backend. 00027 */ 00028 function eZMySQLiDB( $parameters ) 00029 { 00030 $this->eZDBInterface( $parameters ); 00031 00032 if ( !extension_loaded( 'mysqli' ) ) 00033 { 00034 if ( function_exists( 'eZAppendWarningItem' ) ) 00035 { 00036 eZAppendWarningItem( array( 'error' => array( 'type' => 'ezdb', 00037 'number' => eZDBInterface::ERROR_MISSING_EXTENSION ), 00038 'text' => 'MySQLi extension was not found, the DB handler will not be initialized.' ) ); 00039 $this->IsConnected = false; 00040 } 00041 eZDebug::writeWarning( 'MySQLi extension was not found, the DB handler will not be initialized.', 'eZMySQLiDB' ); 00042 return; 00043 } 00044 00045 eZDebug::createAccumulatorGroup( 'mysqli_total', 'Mysql Total' ); 00046 00047 /// Connect to master server 00048 if ( !$this->DBWriteConnection ) 00049 { 00050 $connection = $this->connect( $this->Server, $this->DB, $this->User, $this->Password, $this->SocketPath, $this->Charset, $this->Port ); 00051 if ( $this->IsConnected ) 00052 { 00053 $this->DBWriteConnection = $connection; 00054 } 00055 } 00056 00057 // Connect to slave 00058 if ( !$this->DBConnection ) 00059 { 00060 if ( $this->UseSlaveServer === true ) 00061 { 00062 $connection = $this->connect( $this->SlaveServer, $this->SlaveDB, $this->SlaveUser, $this->SlavePassword, $this->SocketPath, $this->Charset, $this->SlavePort ); 00063 } 00064 else 00065 { 00066 $connection = $this->DBWriteConnection; 00067 } 00068 00069 if ( $connection && $this->DBWriteConnection ) 00070 { 00071 $this->DBConnection = $connection; 00072 $this->IsConnected = true; 00073 } 00074 } 00075 00076 // Initialize TempTableList 00077 $this->TempTableList = array(); 00078 } 00079 00080 /*! 00081 \private 00082 Opens a new connection to a MySQL database and returns the connection 00083 */ 00084 function connect( $server, $db, $user, $password, $socketPath, $charset = null, $port = false ) 00085 { 00086 $connection = false; 00087 00088 if ( $socketPath !== false ) 00089 { 00090 ini_set( "mysqli.default_socket", $socketPath ); 00091 } 00092 00093 if ( $this->UsePersistentConnection == true ) 00094 { 00095 // Only supported on PHP 5.3 (mysqlnd) 00096 if ( version_compare( PHP_VERSION, '5.3' ) > 0 ) 00097 $this->Server = 'p:' . $this->Server; 00098 else 00099 eZDebug::writeWarning( 'mysqli only supports persistent connections when using php 5.3 and higher', __METHOD__ ); 00100 } 00101 00102 $oldHandling = eZDebug::setHandleType( eZDebug::HANDLE_EXCEPTION ); 00103 eZDebug::accumulatorStart( 'mysqli_connection', 'mysqli_total', 'Database connection' ); 00104 try { 00105 $connection = mysqli_connect( $server, $user, $password, null, (int)$port, $socketPath ); 00106 } catch( ErrorException $e ) {} 00107 eZDebug::accumulatorStop( 'mysqli_connection' ); 00108 eZDebug::setHandleType( $oldHandling ); 00109 00110 $maxAttempts = $this->connectRetryCount(); 00111 $waitTime = $this->connectRetryWaitTime(); 00112 $numAttempts = 1; 00113 while ( !$connection && $numAttempts <= $maxAttempts ) 00114 { 00115 sleep( $waitTime ); 00116 00117 $oldHandling = eZDebug::setHandleType( eZDebug::HANDLE_EXCEPTION ); 00118 eZDebug::accumulatorStart( 'mysqli_connection', 'mysqli_total', 'Database connection' ); 00119 try { 00120 $connection = mysqli_connect( $this->Server, $this->User, $this->Password, null, (int)$this->Port, $this->SocketPath ); 00121 } catch( ErrorException $e ) {} 00122 eZDebug::accumulatorStop( 'mysqli_connection' ); 00123 eZDebug::setHandleType( $oldHandling ); 00124 00125 $numAttempts++; 00126 } 00127 $this->setError(); 00128 00129 $this->IsConnected = true; 00130 00131 if ( !$connection ) 00132 { 00133 eZDebug::writeError( "Connection error: Couldn't connect to database server. Please try again later or inform the system administrator.\n{$this->ErrorMessage}", __CLASS__ ); 00134 $this->IsConnected = false; 00135 throw new eZDBNoConnectionException( $server, $this->ErrorMessage, $this->ErrorNumber ); 00136 } 00137 00138 if ( $this->IsConnected && $db != null ) 00139 { 00140 $ret = mysqli_select_db( $connection, $db ); 00141 if ( !$ret ) 00142 { 00143 $this->setError( $connection ); 00144 eZDebug::writeError( "Connection error: Couldn't select the database. Please try again later or inform the system administrator.\n{$this->ErrorMessage}", __CLASS__ ); 00145 $this->IsConnected = false; 00146 } 00147 } 00148 00149 if ( $charset !== null ) 00150 { 00151 $originalCharset = $charset; 00152 $charset = eZCharsetInfo::realCharsetCode( $charset ); 00153 } 00154 00155 if ( $this->IsConnected and $charset !== null ) 00156 { 00157 $status = mysqli_set_charset( $connection, eZMySQLCharset::mapTo( $charset ) ); 00158 if ( !$status ) 00159 { 00160 $this->setError(); 00161 eZDebug::writeWarning( "Connection warning: " . mysqli_errno( $connection ) . ": " . mysqli_error( $connection ), "eZMySQLiDB" ); 00162 } 00163 } 00164 00165 return $connection; 00166 } 00167 00168 function databaseName() 00169 { 00170 return 'mysql'; 00171 } 00172 00173 function bindingType( ) 00174 { 00175 return eZDBInterface::BINDING_NO; 00176 } 00177 00178 function bindVariable( $value, $fieldDef = false ) 00179 { 00180 return $value; 00181 } 00182 00183 /*! 00184 Checks if the requested character set matches the one used in the database. 00185 00186 \return \c true if it matches or \c false if it differs. 00187 \param[out] $currentCharset The charset that the database uses. 00188 will only be set if the match fails. 00189 Note: This will be specific to the database. 00190 */ 00191 function checkCharset( $charset, &$currentCharset ) 00192 { 00193 // If we don't have a database yet we shouldn't check it 00194 if ( !$this->DB ) 00195 return true; 00196 00197 $versionInfo = $this->databaseServerVersion(); 00198 00199 if ( is_array( $charset ) ) 00200 { 00201 $realCharset = array(); 00202 foreach ( $charset as $charsetItem ) 00203 $realCharset[] = eZCharsetInfo::realCharsetCode( $charsetItem ); 00204 } 00205 else 00206 $realCharset = eZCharsetInfo::realCharsetCode( $charset ); 00207 00208 return $this->checkCharsetPriv( $realCharset, $currentCharset ); 00209 } 00210 00211 /*! 00212 \private 00213 */ 00214 function checkCharsetPriv( $charset, &$currentCharset ) 00215 { 00216 $query = "SHOW CREATE DATABASE `{$this->DB}`"; 00217 $status = mysqli_query( $this->DBConnection, $query ); 00218 $this->reportQuery( __CLASS__, $query, false, false ); 00219 if ( !$status ) 00220 { 00221 $this->setError(); 00222 eZDebug::writeWarning( "Connection warning: " . mysqli_errno( $this->DBConnection ) . ": " . mysqli_error( $this->DBConnection ), "eZMySQLiDB" ); 00223 return false; 00224 } 00225 00226 $numRows = mysqli_num_rows( $status ); 00227 if ( $numRows == 0 ) 00228 return false; 00229 00230 for ( $i = 0; $i < $numRows; ++$i ) 00231 { 00232 $tmpRow = mysqli_fetch_array( $status, MYSQLI_ASSOC ); 00233 if ( $tmpRow['Database'] == $this->DB ) 00234 { 00235 $createText = $tmpRow['Create Database']; 00236 if ( preg_match( '#DEFAULT CHARACTER SET ([a-zA-Z0-9_-]+)#', $createText, $matches ) ) 00237 { 00238 $currentCharset = $matches[1]; 00239 $currentCharset = eZCharsetInfo::realCharsetCode( $currentCharset ); 00240 // Convert charset names into something MySQL will understand 00241 $unmappedCurrentCharset = eZMySQLCharset::mapFrom( $currentCharset ); 00242 00243 if ( is_array( $charset ) ) 00244 { 00245 if ( in_array( $unmappedCurrentCharset, $charset ) ) 00246 { 00247 return $unmappedCurrentCharset; 00248 } 00249 } 00250 else if ( $unmappedCurrentCharset == $charset ) 00251 { 00252 return true; 00253 } 00254 return false; 00255 } 00256 break; 00257 } 00258 } 00259 return true; 00260 } 00261 00262 function query( $sql, $server = false ) 00263 { 00264 if ( $this->IsConnected ) 00265 { 00266 eZDebug::accumulatorStart( 'mysqli_query', 'mysqli_total', 'Mysqli_queries' ); 00267 $orig_sql = $sql; 00268 // The converted sql should not be output 00269 if ( $this->InputTextCodec ) 00270 { 00271 eZDebug::accumulatorStart( 'mysqli_conversion', 'mysqli_total', 'String conversion in mysqli' ); 00272 $sql = $this->InputTextCodec->convertString( $sql ); 00273 eZDebug::accumulatorStop( 'mysqli_conversion' ); 00274 } 00275 00276 if ( $this->OutputSQL ) 00277 { 00278 $this->startTimer(); 00279 } 00280 00281 $sql = trim( $sql ); 00282 00283 // Check if we need to use the master or slave server by default 00284 if ( $server === false ) 00285 { 00286 $server = strncasecmp( $sql, 'select', 6 ) === 0 && $this->TransactionCounter == 0 ? 00287 eZDBInterface::SERVER_SLAVE : eZDBInterface::SERVER_MASTER; 00288 } 00289 00290 $connection = ( $server == eZDBInterface::SERVER_SLAVE ) ? $this->DBConnection : $this->DBWriteConnection; 00291 00292 $analysisText = false; 00293 // If query analysis is enable we need to run the query 00294 // with an EXPLAIN in front of it 00295 // Then we build a human-readable table out of the result 00296 if ( $this->QueryAnalysisOutput ) 00297 { 00298 $analysisResult = mysqli_query( $connection, 'EXPLAIN ' . $sql ); 00299 if ( $analysisResult ) 00300 { 00301 $numRows = mysqli_num_rows( $analysisResult ); 00302 $rows = array(); 00303 if ( $numRows > 0 ) 00304 { 00305 for ( $i = 0; $i < $numRows; ++$i ) 00306 { 00307 if ( $this->InputTextCodec ) 00308 { 00309 $tmpRow = mysqli_fetch_array( $analysisResult, MYSQLI_ASSOC ); 00310 $convRow = array(); 00311 foreach( $tmpRow as $key => $row ) 00312 { 00313 $convRow[$key] = $this->OutputTextCodec->convertString( $row ); 00314 } 00315 $rows[$i] = $convRow; 00316 } 00317 else 00318 $rows[$i] = mysqli_fetch_array( $analysisResult, MYSQLI_ASSOC ); 00319 } 00320 } 00321 00322 // Figure out all columns and their maximum display size 00323 $columns = array(); 00324 foreach ( $rows as $row ) 00325 { 00326 foreach ( $row as $col => $data ) 00327 { 00328 if ( !isset( $columns[$col] ) ) 00329 $columns[$col] = array( 'name' => $col, 00330 'size' => strlen( $col ) ); 00331 $columns[$col]['size'] = max( $columns[$col]['size'], strlen( $data ) ); 00332 } 00333 } 00334 00335 $delimiterLine = array(); 00336 $colLine = array(); 00337 // Generate the column line and the vertical delimiter 00338 // The look of the table is taken from the MySQL CLI client 00339 // It looks like this: 00340 // +-------+-------+ 00341 // | col_a | col_b | 00342 // +-------+-------+ 00343 // | txt | 42 | 00344 // +-------+-------+ 00345 foreach ( $columns as $col ) 00346 { 00347 $delimiterLine[] = str_repeat( '-', $col['size'] + 2 ); 00348 $colLine[] = ' ' . str_pad( $col['name'], $col['size'], ' ', STR_PAD_RIGHT ) . ' '; 00349 } 00350 $delimiterLine = '+' . join( '+', $delimiterLine ) . "+\n"; 00351 $analysisText = $delimiterLine; 00352 $analysisText .= '|' . join( '|', $colLine ) . "|\n"; 00353 $analysisText .= $delimiterLine; 00354 00355 // Go trough all data and pad them to create the table correctly 00356 foreach ( $rows as $row ) 00357 { 00358 $rowLine = array(); 00359 foreach ( $columns as $col ) 00360 { 00361 $name = $col['name']; 00362 $size = $col['size']; 00363 $data = isset( $row[$name] ) ? $row[$name] : ''; 00364 // Align numerical values to the right (ie. pad left) 00365 $rowLine[] = ' ' . str_pad( $data, $size, ' ', 00366 is_numeric( $data ) ? STR_PAD_LEFT : STR_PAD_RIGHT ) . ' '; 00367 } 00368 $analysisText .= '|' . join( '|', $rowLine ) . "|\n"; 00369 $analysisText .= $delimiterLine; 00370 } 00371 00372 // Reduce memory usage 00373 unset( $rows, $delimiterLine, $colLine, $columns ); 00374 } 00375 } 00376 00377 $result = mysqli_query( $connection, $sql ); 00378 00379 if ( $this->RecordError and !$result ) 00380 $this->setError(); 00381 00382 if ( $this->OutputSQL ) 00383 { 00384 $this->endTimer(); 00385 00386 if ($this->timeTaken() > $this->SlowSQLTimeout) 00387 { 00388 $num_rows = mysqli_affected_rows( $connection ); 00389 $text = $sql; 00390 00391 // If we have some analysis text we append this to the SQL output 00392 if ( $analysisText !== false ) 00393 $text = "EXPLAIN\n" . $text . "\n\nANALYSIS:\n" . $analysisText; 00394 00395 $this->reportQuery( __CLASS__ . '[' . $connection->host_info . ( $server == eZDBInterface::SERVER_MASTER ? ', on master' : '' ) . ']', $text, $num_rows, $this->timeTaken() ); 00396 } 00397 } 00398 eZDebug::accumulatorStop( 'mysqli_query' ); 00399 if ( $result ) 00400 { 00401 return $result; 00402 } 00403 else 00404 { 00405 $errorMessage = 'Query error (' . mysqli_errno( $connection ) . '): ' . mysqli_error( $connection ) . '. Query: ' . $sql; 00406 eZDebug::writeError( $errorMessage, __CLASS__ ); 00407 $oldRecordError = $this->RecordError; 00408 // Turn off error handling while we unlock 00409 $this->RecordError = false; 00410 mysqli_query( $connection, 'UNLOCK TABLES' ); 00411 $this->RecordError = $oldRecordError; 00412 00413 $this->reportError(); 00414 00415 // This is to behave the same way as other RDBMS PHP API as PostgreSQL 00416 // functions which throws an error with a failing request. 00417 if ( $this->errorHandling == eZDB::ERROR_HANDLING_STANDARD ) 00418 { 00419 trigger_error( "mysqli_query(): $errorMessage", E_USER_ERROR ); 00420 } 00421 else 00422 { 00423 throw new eZDBException( $this->ErrorMessage, $this->ErrorNumber ); 00424 } 00425 00426 return false; 00427 } 00428 } 00429 else 00430 { 00431 eZDebug::writeError( "Trying to do a query without being connected to a database!", __CLASS__ ); 00432 } 00433 00434 00435 } 00436 00437 function arrayQuery( $sql, $params = array(), $server = false ) 00438 { 00439 $retArray = array(); 00440 if ( $this->IsConnected ) 00441 { 00442 $limit = false; 00443 $offset = 0; 00444 $column = false; 00445 // check for array parameters 00446 if ( is_array( $params ) ) 00447 { 00448 if ( isset( $params["limit"] ) and is_numeric( $params["limit"] ) ) 00449 $limit = $params["limit"]; 00450 00451 if ( isset( $params["offset"] ) and is_numeric( $params["offset"] ) ) 00452 $offset = $params["offset"]; 00453 00454 if ( isset( $params["column"] ) and ( is_numeric( $params["column"] ) or is_string( $params["column"] ) ) ) 00455 $column = $params["column"]; 00456 } 00457 00458 if ( $limit !== false and is_numeric( $limit ) ) 00459 { 00460 $sql .= "\nLIMIT $offset, $limit "; 00461 } 00462 else if ( $offset !== false and is_numeric( $offset ) and $offset > 0 ) 00463 { 00464 $sql .= "\nLIMIT $offset, 18446744073709551615"; // 2^64-1 00465 } 00466 $result = $this->query( $sql, $server ); 00467 00468 if ( $result == false ) 00469 { 00470 $this->reportQuery( __CLASS__, $sql, false, false ); 00471 return false; 00472 } 00473 00474 $numRows = mysqli_num_rows( $result ); 00475 if ( $numRows > 0 ) 00476 { 00477 if ( !is_string( $column ) ) 00478 { 00479 eZDebug::accumulatorStart( 'mysqli_loop', 'mysqli_total', 'Looping result' ); 00480 for ( $i=0; $i < $numRows; $i++ ) 00481 { 00482 if ( $this->InputTextCodec ) 00483 { 00484 $tmpRow = mysqli_fetch_array( $result, MYSQLI_ASSOC ); 00485 $convRow = array(); 00486 foreach( $tmpRow as $key => $row ) 00487 { 00488 eZDebug::accumulatorStart( 'mysqli_conversion', 'mysqli_total', 'String conversion in mysqli' ); 00489 $convRow[$key] = $this->OutputTextCodec->convertString( $row ); 00490 eZDebug::accumulatorStop( 'mysqli_conversion' ); 00491 } 00492 $retArray[$i + $offset] = $convRow; 00493 } 00494 else 00495 $retArray[$i + $offset] = mysqli_fetch_array( $result, MYSQLI_ASSOC ); 00496 } 00497 eZDebug::accumulatorStop( 'mysqli_loop' ); 00498 00499 } 00500 else 00501 { 00502 eZDebug::accumulatorStart( 'mysqli_loop', 'mysqli_total', 'Looping result' ); 00503 for ( $i=0; $i < $numRows; $i++ ) 00504 { 00505 $tmp_row = mysqli_fetch_array( $result, MYSQLI_ASSOC ); 00506 if ( $this->InputTextCodec ) 00507 { 00508 eZDebug::accumulatorStart( 'mysqli_conversion', 'mysqli_total', 'String conversion in mysqli' ); 00509 $retArray[$i + $offset] = $this->OutputTextCodec->convertString( $tmp_row[$column] ); 00510 eZDebug::accumulatorStop( 'mysqli_conversion' ); 00511 } 00512 else 00513 $retArray[$i + $offset] =& $tmp_row[$column]; 00514 } 00515 eZDebug::accumulatorStop( 'mysqli_loop' ); 00516 } 00517 } 00518 } 00519 return $retArray; 00520 } 00521 00522 function subString( $string, $from, $len = null ) 00523 { 00524 if ( $len == null ) 00525 { 00526 return " substring( $string from $from ) "; 00527 }else 00528 { 00529 return " substring( $string from $from for $len ) "; 00530 } 00531 } 00532 00533 function concatString( $strings = array() ) 00534 { 00535 $str = implode( "," , $strings ); 00536 return " concat( $str ) "; 00537 } 00538 00539 function md5( $str ) 00540 { 00541 return " MD5( $str ) "; 00542 } 00543 00544 function bitAnd( $arg1, $arg2 ) 00545 { 00546 return 'cast(' . $arg1 . ' & ' . $arg2 . ' AS SIGNED ) '; 00547 } 00548 00549 function bitOr( $arg1, $arg2 ) 00550 { 00551 return 'cast( ' . $arg1 . ' | ' . $arg2 . ' AS SIGNED ) '; 00552 } 00553 00554 function supportedRelationTypeMask() 00555 { 00556 return eZDBInterface::RELATION_TABLE_BIT | self::RELATION_FOREIGN_KEY_BIT; 00557 } 00558 00559 function supportedRelationTypes() 00560 { 00561 return array( self::RELATION_FOREIGN_KEY, eZDBInterface::RELATION_TABLE ); 00562 } 00563 00564 function relationCounts( $relationMask ) 00565 { 00566 if ( $relationMask & eZDBInterface::RELATION_TABLE_BIT ) 00567 return $this->relationCount(); 00568 else 00569 return 0; 00570 } 00571 00572 function relationCount( $relationType = eZDBInterface::RELATION_TABLE ) 00573 { 00574 if ( !in_array( $relationType, $this->supportedRelationTypes() ) ) 00575 { 00576 eZDebug::writeError( "Unsupported relation type '$relationType'", __METHOD__ ); 00577 return false; 00578 } 00579 $count = false; 00580 if ( $this->IsConnected ) 00581 { 00582 switch ( $relationType ) 00583 { 00584 case eZDBInterface::RELATION_TABLE: 00585 { 00586 $query = 'SHOW TABLES from `' . $this->DB .'`'; 00587 $result = mysqli_query( $this->DBConnection, $query ); 00588 $this->reportQuery( __CLASS__, $query, false, false ); 00589 $count = mysqli_num_rows( $result ); 00590 mysqli_free_result( $result ); 00591 } break; 00592 00593 case self::RELATION_FOREIGN_KEY: 00594 { 00595 $count = count( $this->relationList( self::RELATION_FOREIGN_KEY ) ); 00596 } break; 00597 } 00598 } 00599 return $count; 00600 } 00601 00602 function relationList( $relationType = eZDBInterface::RELATION_TABLE ) 00603 { 00604 if ( !in_array( $relationType, $this->supportedRelationTypes() ) ) 00605 { 00606 eZDebug::writeError( "Unsupported relation type '$relationType'", __METHOD__ ); 00607 return false; 00608 } 00609 if ( $this->IsConnected ) 00610 { 00611 switch ( $relationType ) 00612 { 00613 case eZDBInterface::RELATION_TABLE: 00614 { 00615 $tables = array(); 00616 $query = 'SHOW TABLES from `' . $this->DB .'`'; 00617 $result = mysqli_query( $this->DBConnection, $query ); 00618 $this->reportQuery( __CLASS__, $query, false, false ); 00619 while( $row = mysqli_fetch_row( $result ) ) 00620 { 00621 $tables[] = $row[0]; 00622 } 00623 mysqli_free_result( $result ); 00624 return $tables; 00625 } break; 00626 00627 case self::RELATION_FOREIGN_KEY: 00628 { 00629 /** 00630 * Ideally, we would have queried information_schema.KEY_COLUMN_USAGE 00631 * However, a known bug causes queries on this table to potentially be VERY slow (http://bugs.mysql.com/bug.php?id=19588) 00632 * 00633 * The query would look like this: 00634 * SELECT table_name AS from_table, column_name AS from_column, referenced_table_name AS to_table, 00635 * referenced_column_name AS to_column 00636 * FROM information_schema.KEY_COLUMN_USAGE 00637 * WHERE REFERENCED_TABLE_SCHEMA = '{$this->DB}' 00638 * AND REFERENCED_TABLE_NAME is not null; 00639 * 00640 * Result as of MySQL 5.1.48 / August 2010: 00641 * 00642 * +---------------+-------------+----------+-----------+ 00643 * | from_table | from_column | to_table | to_column | 00644 * +---------------+-------------+----------+-----------+ 00645 * | ezdbfile_data | name_hash | ezdbfile | name_hash | 00646 * +---------------+-------------+----------+-----------+ 00647 * 1 row in set (12.56 sec) 00648 * 00649 * The only way out right now is to parse SHOW CREATE TABLE for each table and extract CONSTRAINT lines 00650 */ 00651 00652 $foreignKeys = array(); 00653 foreach( $this->relationList( eZDBInterface::RELATION_TABLE ) as $table ) 00654 { 00655 $query = "SHOW CREATE TABLE $table"; 00656 $result = mysqli_query( $this->DBConnection, $query ); 00657 $this->reportQuery( __CLASS__, $query, false, false ); 00658 if ( mysqli_num_rows( $result ) === 1 ) 00659 { 00660 $row = mysqli_fetch_row( $result ); 00661 if ( strpos( $row[1], "CONSTRAINT" ) !== false ) 00662 { 00663 if ( preg_match_all( '#CONSTRAINT [`"]([^`"]+)[`"] FOREIGN KEY \([`"].*[`"]\) REFERENCES [`"]([^`"]+)[`"] \([`"].*[`"]\)#', $row[1], $matches, PREG_PATTERN_ORDER ) ) 00664 { 00665 // $foreignKeys[] = array( 'table' => $table, 'keys' => $matches[1] ); 00666 foreach( $matches[1] as $fkMatch ) 00667 { 00668 $foreignKeys[] = array( 'table' => $table, 'fk' => $fkMatch ); 00669 } 00670 } 00671 } 00672 } 00673 } 00674 return $foreignKeys; 00675 } 00676 } 00677 } 00678 } 00679 00680 function eZTableList( $server = eZDBInterface::SERVER_MASTER ) 00681 { 00682 $tables = array(); 00683 if ( $this->IsConnected ) 00684 { 00685 if ( $this->UseSlaveServer && $server == eZDBInterface::SERVER_SLAVE ) 00686 { 00687 $connection = $this->DBConnection; 00688 $db = $this->SlaveDB; 00689 } 00690 else 00691 { 00692 $connection = $this->DBWriteConnection; 00693 $db = $this->DB; 00694 } 00695 00696 $query = 'SHOW TABLES from `' . $db .'`'; 00697 $result = mysqli_query( $connection, $query ); 00698 $this->reportQuery( __CLASS__, $query, false, false ); 00699 while( $row = mysqli_fetch_row( $result ) ) 00700 { 00701 $tableName = $row[0]; 00702 if ( substr( $tableName, 0, 2 ) == 'ez' ) 00703 { 00704 $tables[$tableName] = eZDBInterface::RELATION_TABLE; 00705 } 00706 } 00707 mysqli_free_result( $result ); 00708 } 00709 return $tables; 00710 } 00711 00712 function relationMatchRegexp( $relationType ) 00713 { 00714 return "#^ez#"; 00715 } 00716 00717 function removeRelation( $relationName, $relationType ) 00718 { 00719 $relationTypeName = $this->relationName( $relationType ); 00720 if ( !$relationTypeName ) 00721 { 00722 eZDebug::writeError( "Unknown relation type '$relationType'", __METHOD__ ); 00723 return false; 00724 } 00725 00726 if ( $this->IsConnected ) 00727 { 00728 switch ( $relationType ) 00729 { 00730 case self::RELATION_FOREIGN_KEY: 00731 { 00732 $sql = "ALTER TABLE {$relationName['table']} DROP FOREIGN KEY {$relationName['fk']}"; 00733 $this->query( $sql ); 00734 return true; 00735 } break; 00736 00737 default: 00738 { 00739 $sql = "DROP $relationTypeName $relationName"; 00740 return $this->query( $sql ); 00741 } 00742 } 00743 } 00744 return false; 00745 } 00746 00747 /** 00748 * Local eZDBInterface::relationName() override to support the foreign keys type relation 00749 * @param $relationType 00750 * @return string|false 00751 */ 00752 public function relationName( $relationType ) 00753 { 00754 if ( $relationType == self::RELATION_FOREIGN_KEY ) 00755 return 'FOREIGN_KEY'; 00756 else 00757 return parent::relationName( $relationType ); 00758 } 00759 00760 function lock( $table ) 00761 { 00762 if ( $this->IsConnected ) 00763 { 00764 if ( is_array( $table ) ) 00765 { 00766 $lockQuery = "LOCK TABLES"; 00767 $first = true; 00768 foreach( array_keys( $table ) as $tableKey ) 00769 { 00770 if ( $first == true ) 00771 $first = false; 00772 else 00773 $lockQuery .= ","; 00774 $lockQuery .= " " . $table[$tableKey]['table'] . " WRITE"; 00775 } 00776 $this->query( $lockQuery ); 00777 } 00778 else 00779 { 00780 $this->query( "LOCK TABLES $table WRITE" ); 00781 } 00782 } 00783 } 00784 00785 function unlock() 00786 { 00787 if ( $this->IsConnected ) 00788 { 00789 $this->query( "UNLOCK TABLES" ); 00790 } 00791 } 00792 00793 /*! 00794 The query to start the transaction. 00795 */ 00796 function beginQuery() 00797 { 00798 return $this->query("BEGIN WORK"); 00799 } 00800 00801 /*! 00802 The query to commit the transaction. 00803 */ 00804 function commitQuery() 00805 { 00806 return $this->query( "COMMIT" ); 00807 } 00808 00809 /*! 00810 The query to cancel the transaction. 00811 */ 00812 function rollbackQuery() 00813 { 00814 return mysqli_query( $this->DBWriteConnection, "ROLLBACK" ); 00815 } 00816 00817 function lastSerialID( $table = false, $column = false ) 00818 { 00819 if ( $this->IsConnected ) 00820 { 00821 $id = mysqli_insert_id( $this->DBWriteConnection ); 00822 return $id; 00823 } 00824 else 00825 return false; 00826 } 00827 00828 function escapeString( $str ) 00829 { 00830 if ( $this->IsConnected ) 00831 { 00832 return mysqli_real_escape_string( $this->DBConnection, $str ); 00833 } 00834 else 00835 { 00836 eZDebug::writeDebug( 'escapeString called before connection is made', __METHOD__ ); 00837 return mysql_escape_string( $str ); 00838 } 00839 } 00840 00841 function close() 00842 { 00843 if ( $this->IsConnected ) 00844 { 00845 if ( $this->UseSlaveServer === true ) 00846 mysqli_close( $this->DBConnection ); 00847 mysqli_close( $this->DBWriteConnection ); 00848 } 00849 } 00850 00851 function createDatabase( $dbName ) 00852 { 00853 if ( $this->IsConnected ) 00854 { 00855 $this->query( "CREATE DATABASE $dbName" ); 00856 $this->setError(); 00857 } 00858 } 00859 00860 function removeDatabase( $dbName ) 00861 { 00862 if ( $this->IsConnected ) 00863 { 00864 $this->query( "DROP DATABASE $dbName" ); 00865 $this->setError(); 00866 } 00867 } 00868 00869 /** 00870 * Sets the internal error messages & number 00871 * @param MySQLi $connection database connection handle, overrides the current one if given 00872 */ 00873 function setError( $connection = false ) 00874 { 00875 if ( $this->IsConnected ) 00876 { 00877 if ( $connection === false ) 00878 $connection = $this->DBConnection; 00879 00880 if ( $connection instanceof MySQLi ) 00881 { 00882 $this->ErrorMessage = mysqli_error( $connection ); 00883 $this->ErrorNumber = mysqli_errno( $connection ); 00884 } 00885 } 00886 else 00887 { 00888 $this->ErrorMessage = mysqli_connect_error(); 00889 $this->ErrorNumber = mysqli_connect_errno(); 00890 } 00891 } 00892 00893 function availableDatabases() 00894 { 00895 $databaseArray = mysqli_query( $this->DBConnection, 'SHOW DATABASES' ); 00896 00897 if ( $this->errorNumber() != 0 ) 00898 { 00899 return null; 00900 } 00901 00902 $databases = array(); 00903 00904 $numRows = mysqli_num_rows( $databaseArray ); 00905 if ( count( $numRows ) == 0 ) 00906 { 00907 return false; 00908 } 00909 00910 while ( $row = mysqli_fetch_row( $databaseArray ) ) 00911 { 00912 // we don't allow "mysql" or "information_schema" database to be shown anywhere 00913 $curDB = $row[0]; 00914 if ( strcasecmp( $curDB, 'mysql' ) != 0 && strcasecmp( $curDB, 'information_schema' ) != 0 ) 00915 { 00916 $databases[] = $curDB; 00917 } 00918 } 00919 return $databases; 00920 } 00921 00922 function databaseServerVersion() 00923 { 00924 if ( $this->IsConnected ) 00925 { 00926 $versionInfo = mysqli_get_server_info( $this->DBConnection ); 00927 00928 $versionArray = explode( '.', $versionInfo ); 00929 00930 return array( 'string' => $versionInfo, 00931 'values' => $versionArray ); 00932 } 00933 00934 return false; 00935 } 00936 00937 function databaseClientVersion() 00938 { 00939 $versionInfo = mysqli_get_client_info(); 00940 00941 $versionArray = explode( '.', $versionInfo ); 00942 00943 return array( 'string' => $versionInfo, 00944 'values' => $versionArray ); 00945 } 00946 00947 function isCharsetSupported( $charset ) 00948 { 00949 return true; 00950 } 00951 00952 function supportsDefaultValuesInsertion() 00953 { 00954 return false; 00955 } 00956 00957 function dropTempTable( $dropTableQuery = '', $server = self::SERVER_SLAVE ) 00958 { 00959 $dropTableQuery = str_ireplace( 'DROP TABLE', 'DROP TEMPORARY TABLE', $dropTableQuery ); 00960 parent::dropTempTable( $dropTableQuery, $server ); 00961 } 00962 00963 protected $TempTableList; 00964 00965 /// \privatesection 00966 } 00967 00968 ?>