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