|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // $Id$ 00004 // 00005 // Definition of eZDBInterface class 00006 // 00007 // Created on: <12-Feb-2002 15:54:17 bf> 00008 // 00009 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00010 // SOFTWARE NAME: eZ Publish 00011 // SOFTWARE RELEASE: 4.0.x 00012 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS 00013 // SOFTWARE LICENSE: GNU General Public License v2.0 00014 // NOTICE: > 00015 // This program is free software; you can redistribute it and/or 00016 // modify it under the terms of version 2.0 of the GNU General 00017 // Public License as published by the Free Software Foundation. 00018 // 00019 // This program is distributed in the hope that it will be useful, 00020 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 // GNU General Public License for more details. 00023 // 00024 // You should have received a copy of version 2.0 of the GNU General 00025 // Public License along with this program; if not, write to the Free 00026 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00027 // MA 02110-1301, USA. 00028 // 00029 // 00030 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00031 // 00032 00033 /*! 00034 \class eZDBInterface ezdbinterface.php 00035 \ingroup eZDB 00036 \brief The eZDBInterface defines the interface for all database implementations 00037 00038 \sa eZDB 00039 */ 00040 00041 require_once( "lib/ezutils/classes/ezdebug.php" ); 00042 //include_once( "lib/ezutils/classes/ezini.php" ); 00043 00044 class eZDBInterface 00045 { 00046 const BINDING_NO = 0; 00047 const BINDING_NAME = 1; 00048 const BINDING_ORDERED = 2; 00049 00050 const RELATION_TABLE = 0; 00051 const RELATION_SEQUENCE = 1; 00052 const RELATION_TRIGGER = 2; 00053 const RELATION_VIEW = 3; 00054 const RELATION_INDEX = 4; 00055 00056 const RELATION_TABLE_BIT = 1; 00057 const RELATION_SEQUENCE_BIT = 2; 00058 const RELATION_TRIGGER_BIT = 4; 00059 const RELATION_VIEW_BIT = 8; 00060 const RELATION_INDEX_BIT = 16; 00061 00062 const RELATION_NONE = 0; 00063 const RELATION_MASK = 31; 00064 00065 const ERROR_MISSING_EXTENSION = 1; 00066 00067 const SERVER_MASTER = 1; 00068 const SERVER_SLAVE = 2; 00069 00070 /*! 00071 Create a new eZDBInterface object and connects to the database backend. 00072 */ 00073 function eZDBInterface( $parameters ) 00074 { 00075 $server = $parameters['server']; 00076 $port = $parameters['port']; 00077 $user = $parameters['user']; 00078 $password = $parameters['password']; 00079 $db = $parameters['database']; 00080 $useSlaveServer = $parameters['use_slave_server']; 00081 $slaveServer = $parameters['slave_server']; 00082 $slavePort = $parameters['slave_port']; 00083 $slaveUser = $parameters['slave_user']; 00084 $slavePassword = $parameters['slave_password']; 00085 $slaveDB = $parameters['slave_database']; 00086 $socketPath = $parameters['socket']; 00087 $charset = $parameters['charset']; 00088 $isInternalCharset = $parameters['is_internal_charset']; 00089 $builtinEncoding = $parameters['builtin_encoding']; 00090 $connectRetries = $parameters['connect_retries']; 00091 00092 if ( $parameters['use_persistent_connection'] == 'enabled' ) 00093 { 00094 $this->UsePersistentConnection = true; 00095 } 00096 00097 $this->DB = $db; 00098 $this->Server = $server; 00099 $this->Port = $port; 00100 $this->SocketPath = $socketPath; 00101 $this->User = $user; 00102 $this->Password = $password; 00103 $this->UseSlaveServer = $useSlaveServer; 00104 $this->SlaveDB = $slaveDB; 00105 $this->SlaveServer = $slaveServer; 00106 $this->SlavePort = $slavePort; 00107 $this->SlaveUser = $slaveUser; 00108 $this->SlavePassword = $slavePassword; 00109 $this->Charset = $charset; 00110 $this->IsInternalCharset = $isInternalCharset; 00111 $this->UseBuiltinEncoding = $builtinEncoding; 00112 $this->ConnectRetries = $connectRetries; 00113 $this->DBConnection = false; 00114 $this->DBWriteConnection = false; 00115 $this->TransactionCounter = 0; 00116 $this->TransactionIsValid = false; 00117 $this->TransactionStackTree = false; 00118 00119 $this->OutputTextCodec = null; 00120 $this->InputTextCodec = null; 00121 /* 00122 This is pseudocode, there is no such function as 00123 mysql_supports_charset() of course 00124 if ( $this->UseBuiltinEncoding and mysql_supports_charset( $charset ) ) 00125 { 00126 mysql_session_set_charset( $charset ); 00127 } 00128 else 00129 */ 00130 { 00131 //include_once( "lib/ezi18n/classes/eztextcodec.php" ); 00132 $tmpOutputTextCodec = eZTextCodec::instance( $charset, false, false ); 00133 $tmpInputTextCodec = eZTextCodec::instance( false, $charset, false ); 00134 unset( $this->OutputTextCodec ); 00135 unset( $this->InputTextCodec ); 00136 $this->OutputTextCodec = null; 00137 $this->InputTextCodec = null; 00138 00139 if ( $tmpOutputTextCodec && $tmpInputTextCodec ) 00140 { 00141 if ( $tmpOutputTextCodec->conversionRequired() && $tmpInputTextCodec->conversionRequired() ) 00142 { 00143 $this->OutputTextCodec =& $tmpOutputTextCodec; 00144 $this->InputTextCodec =& $tmpInputTextCodec; 00145 } 00146 } 00147 } 00148 00149 $this->OutputSQL = false; 00150 $this->SlowSQLTimeout = 0; 00151 $ini = eZINI::instance(); 00152 if ( ( $ini->variable( "DatabaseSettings", "SQLOutput" ) == "enabled" ) and 00153 ( $ini->variable( "DebugSettings", "DebugOutput" ) == "enabled" ) ) 00154 { 00155 $this->OutputSQL = true; 00156 00157 $this->SlowSQLTimeout = (int) $ini->variable( "DatabaseSettings", "SlowQueriesOutput" ); 00158 } 00159 if ( $ini->variable( "DatabaseSettings", "DebugTransactions" ) == "enabled" ) 00160 { 00161 // Setting it to an array turns on the debugging 00162 $this->TransactionStackTree = array(); 00163 } 00164 00165 $this->QueryAnalysisOutput = false; 00166 if ( $ini->variable( "DatabaseSettings", "QueryAnalysisOutput" ) == "enabled" ) 00167 { 00168 $this->QueryAnalysisOutput = true; 00169 } 00170 00171 $this->IsConnected = false; 00172 $this->NumQueries = 0; 00173 $this->StartTime = false; 00174 $this->EndTime = false; 00175 $this->TimeTaken = false; 00176 00177 $this->AttributeVariableMap = 00178 array( 00179 'database_name' => 'DB', 00180 'database_server' => 'Server', 00181 'database_port' => 'Port', 00182 'database_socket_path' => 'SocketPath', 00183 'database_user' => 'User', 00184 'use_slave_server' => 'UseSlaveServer', 00185 'slave_database_name' => 'SlaveDB', 00186 'slave_database_server' => 'SlaveServer', 00187 'slave_database_port' => 'SlavePort', 00188 'slave_database_user' => 'SlaveUser', 00189 'charset' => 'Charset', 00190 'is_internal_charset' => 'IsInternalCharset', 00191 'use_builting_encoding' => 'UseBuiltinEncoding', 00192 'retry_count' => 'ConnectRetries' ); 00193 } 00194 00195 /*! 00196 \return the available attributes for this database handler. 00197 */ 00198 function attributes() 00199 { 00200 return array_keys( $this->AttributeVariableMap ); 00201 } 00202 00203 /*! 00204 \return \c true if the attribute \a $name exists for this database handler. 00205 */ 00206 function hasAttribute( $name ) 00207 { 00208 if ( isset( $this->AttributeVariableMap[$name] ) ) 00209 { 00210 return true; 00211 } 00212 return false; 00213 } 00214 00215 /*! 00216 \return the value of the attribute \a $name if it exists, otherwise \c null. 00217 */ 00218 function attribute( $name ) 00219 { 00220 if ( isset( $this->AttributeVariableMap[$name] ) ) 00221 { 00222 $memberVariable = $this->AttributeVariableMap[$name]; 00223 return $this->$memberVariable; 00224 } 00225 else 00226 { 00227 eZDebug::writeError( "Attribute '$name' does not exist", 'eZDBInterface::attribute' ); 00228 return null; 00229 } 00230 } 00231 00232 /*! 00233 Checks if the requested character set matches the one used in the database. 00234 00235 \return \c true if it matches or \c false if it differs. 00236 \param[out] $currentCharset The charset that the database uses, 00237 will only be set if the match fails. 00238 Note: This will be specific to the database. 00239 00240 \note The default is to always return \c true, see the specific database handler 00241 for more information. 00242 */ 00243 function checkCharset( $charset, &$currentCharset ) 00244 { 00245 return true; 00246 } 00247 00248 /*! 00249 \private 00250 Prepare the sql file so we can create the database. 00251 \param $fd The file descriptor 00252 \param $buffer Reference to string buffer for SQL queries. 00253 */ 00254 function prepareSqlQuery( &$fd, &$buffer ) 00255 { 00256 00257 $sqlQueryArray = array(); 00258 while( count( $sqlQueryArray ) == 0 && !feof( $fd ) ) 00259 { 00260 $buffer .= fread( $fd, 4096 ); 00261 if ( $buffer ) 00262 { 00263 // Fix SQL file by deleting all comments and newlines 00264 // eZDebug::writeDebug( $buffer, "read data" ); 00265 $sqlQuery = preg_replace( array( "/^#.*\n" . "/m", 00266 "#^/\*.*\*/\n" . "#m", 00267 "/^--.*\n" . "/m", 00268 "/\n|\r\n|\r/m" ), 00269 array( "", 00270 "", 00271 "", 00272 "\n" ), 00273 $buffer ); 00274 // eZDebug::writeDebug( $sqlQuery, "read data" ); 00275 00276 // Split the query into an array 00277 $sqlQueryArray = preg_split( "/;\n/m", $sqlQuery ); 00278 00279 if ( preg_match( '/;\n/m', $sqlQueryArray[ count( $sqlQueryArray ) -1 ] ) ) 00280 { 00281 $buffer = ''; 00282 } 00283 else 00284 { 00285 $buffer = $sqlQueryArray[ count( $sqlQueryArray ) -1 ]; 00286 array_splice( $sqlQueryArray, count( $sqlQueryArray ) -1 , 1 ); 00287 } 00288 } 00289 else 00290 { 00291 return $sqlQueryArray; 00292 00293 } 00294 } 00295 return $sqlQueryArray; 00296 } 00297 00298 /*! 00299 Inserts the SQL file \a $sqlFile found in the path \a $path into 00300 the currently connected database. 00301 \return \c true if succesful. 00302 */ 00303 function insertFile( $path, $sqlFile, $usePathType = true ) 00304 { 00305 $type = $this->databaseName(); 00306 00307 //include_once( 'lib/ezfile/classes/ezdir.php' ); 00308 if ( $usePathType ) 00309 $sqlFileName = eZDir::path( array( $path, $type, $sqlFile ) ); 00310 else 00311 $sqlFileName = eZDir::path( array( $path, $sqlFile ) ); 00312 $sqlFileHandler = fopen( $sqlFileName, 'rb' ); 00313 $buffer = ''; 00314 $done = false; 00315 while ( count( ( $sqlArray = $this->prepareSqlQuery( $sqlFileHandler, $buffer ) ) ) > 0 ) 00316 { 00317 // Turn unneccessary SQL debug output off 00318 $oldOutputSQL = $this->OutputSQL; 00319 $this->OutputSQL = false; 00320 if ( $sqlArray && is_array( $sqlArray ) ) 00321 { 00322 $done = true; 00323 foreach( $sqlArray as $singleQuery ) 00324 { 00325 $singleQuery = preg_replace( "/\n|\r\n|\r/", " ", $singleQuery ); 00326 if ( preg_match( "#^ */(.+)$#", $singleQuery, $matches ) ) 00327 { 00328 $singleQuery = $matches[1]; 00329 } 00330 if ( trim( $singleQuery ) != "" ) 00331 { 00332 // eZDebug::writeDebug( $singleQuery ); 00333 $this->query( trim( $singleQuery ) ); 00334 if ( $this->errorNumber() ) 00335 { 00336 return false; 00337 } 00338 } 00339 } 00340 00341 } 00342 $this->OutputSQL = $oldOutputSQL; 00343 } 00344 return $done; 00345 00346 } 00347 00348 /*! 00349 \private 00350 Writes a debug notice with query information. 00351 */ 00352 function reportQuery( $class, $sql, $numRows, $timeTaken ) 00353 { 00354 $rowText = ''; 00355 if ( $numRows !== false ) $rowText = "$numRows rows, "; 00356 00357 $backgroundClass = ($this->TransactionCounter > 0 ? "debugtransaction transactionlevel-$this->TransactionCounter" : ""); 00358 eZDebug::writeNotice( "$sql", "$class::query($rowText" . number_format( $timeTaken, 3 ) . " ms) query number per page:" . $this->NumQueries++, $backgroundClass ); 00359 } 00360 00361 /*! 00362 Enabled or disables sql output. 00363 */ 00364 function setIsSQLOutputEnabled( $enabled ) 00365 { 00366 $this->OutputSQL = $enabled; 00367 } 00368 00369 /*! 00370 \private 00371 Records the current micro time. End the timer with endTimer() and 00372 fetch the result with timeTaken(); 00373 */ 00374 function startTimer() 00375 { 00376 $this->StartTime = microtime( true ); 00377 } 00378 00379 /*! 00380 \private 00381 Stops the current timer and calculates the time taken. 00382 \sa startTimer, timeTaken 00383 */ 00384 function endTimer() 00385 { 00386 $this->EndTime = microtime( true ); 00387 // Calculate time taken in ms 00388 $this->TimeTaken = $this->EndTime - $this->StartTime; 00389 $this->TimeTaken *= 1000.0; 00390 } 00391 00392 /*! 00393 \private 00394 \return the micro time when the timer was start or false if no timer. 00395 */ 00396 function startTime() 00397 { 00398 return $this->StartTime; 00399 } 00400 00401 /*! 00402 \private 00403 \return the micro time when the timer was ended or false if no timer. 00404 */ 00405 function endTime() 00406 { 00407 return $this->EndTime; 00408 } 00409 00410 /*! 00411 \private 00412 \return the number of milliseconds the last operation took or false if no value. 00413 */ 00414 function timeTaken() 00415 { 00416 return $this->TimeTaken; 00417 } 00418 00419 /*! 00420 \pure 00421 Returns the name of driver, this is used to determine the name of the database type. 00422 For instance multiple implementations of the MySQL database will all return \c 'mysql'. 00423 */ 00424 function databaseName() 00425 { 00426 return ''; 00427 } 00428 00429 /*! 00430 \return the socket path for the database or \c false if no socket path was defined. 00431 */ 00432 function socketPath() 00433 { 00434 return $this->SocketPath; 00435 } 00436 00437 /*! 00438 \return the number of times the db handler should try to reconnect if it fails. 00439 */ 00440 function connectRetryCount() 00441 { 00442 return $this->ConnectRetries; 00443 } 00444 00445 /*! 00446 \return the number of seconds the db handler should wait before rereconnecting. 00447 \note Currently returns 3 seconds. 00448 */ 00449 function connectRetryWaitTime() 00450 { 00451 return 3; 00452 } 00453 00454 /*! 00455 \pure 00456 \return a mask of the relation type it supports. 00457 */ 00458 function supportedRelationTypeMask() 00459 { 00460 return eZDBInterface::RELATION_NONE; 00461 } 00462 00463 /*! 00464 \pure 00465 \return if the short column names should be used insted of default ones 00466 */ 00467 function useShortNames() 00468 { 00469 return false; 00470 } 00471 00472 /*! 00473 \pure 00474 \return an array of the relation types. 00475 */ 00476 function supportedRelationTypes() 00477 { 00478 return array(); 00479 } 00480 00481 /*! 00482 \pure 00483 \return a sql-expression(string) to get substring. 00484 */ 00485 function subString( $string, $from, $len = null ) 00486 { 00487 return ''; 00488 } 00489 00490 /*! 00491 \pure 00492 \return a sql-expression(string) to concatenate strings. 00493 */ 00494 function concatString( $strings = array() ) 00495 { 00496 return ''; 00497 } 00498 00499 /*! 00500 \pure 00501 \return a sql-expression(string) to generate a md5 sum of the string. 00502 */ 00503 function md5( $str ) 00504 { 00505 return ''; 00506 } 00507 00508 /*! 00509 \pure 00510 \return a sql-expression(string) to generate a bit and of the argument. 00511 */ 00512 function bitAnd( $arg1, $arg2 ) 00513 { 00514 return '(' . $arg1 . ' & ' . $arg2 . ' ) '; 00515 } 00516 00517 /*! 00518 \pure 00519 \return a sql-expression(string) to generate a bit and of the argument. 00520 */ 00521 function bitOr( $arg1, $arg2 ) 00522 { 00523 return '( ' . $arg1 . ' | ' . $arg2 . ' ) '; 00524 } 00525 00526 /*! 00527 Checks if the version number of the server is equal or larger than \a $minVersion. 00528 Will also check if the database type is correct if \a $name is set. 00529 00530 \param $minVersion A string denoting the min. required version. 00531 \param $name The name of the database type it requires or \c false if it does not matter. 00532 \return \c true if the server fulfills the requirements. 00533 */ 00534 function hasRequiredServerVersion( $minVersion, $name = false ) 00535 { 00536 if ( $name !== false and 00537 $name != $this->databaseName() ) 00538 return false; 00539 00540 $version = $this->databaseServerVersion(); 00541 $version = $version['string']; 00542 return version_compare( $version, $minVersion ) >= 0; 00543 } 00544 00545 /*! 00546 \virtual 00547 \return the version of the database server or \c false if no version could be retrieved/ 00548 */ 00549 function databaseServerVersion() 00550 { 00551 } 00552 00553 /*! 00554 \pure 00555 \return the version of the database client or \c false if no version could be retrieved/ 00556 */ 00557 function databaseClientVersion() 00558 { 00559 } 00560 00561 /*! 00562 \return \c true if the charset \a $charset is supported by the connected database. 00563 */ 00564 function isCharsetSupported( $charset ) 00565 { 00566 return false; 00567 } 00568 00569 /*! 00570 Returns the charset which the database is encoded in. 00571 \sa usesBuiltinEncoding 00572 */ 00573 function charset() 00574 { 00575 return $this->Charset; 00576 } 00577 00578 /*! 00579 Returns true if the database handles encoding itself, if not 00580 all queries and returned data must be decoded yourselves. 00581 \note This functionality might be removed in the future 00582 */ 00583 function usesBuiltinEncoding() 00584 { 00585 return $this->UseBuiltinEncoding; 00586 } 00587 00588 /*! 00589 \pure 00590 Returns type of binding used in database plugin. 00591 */ 00592 function bindingType( ) 00593 { 00594 } 00595 00596 /*! 00597 \pure 00598 Binds variable. 00599 */ 00600 function bindVariable( $value, $fieldDef = false ) 00601 { 00602 } 00603 00604 /*! 00605 \pure 00606 Execute a query on the global MySQL database link. If it returns an error, 00607 the script is halted and the attempted SQL query and MySQL error message are printed. 00608 00609 \param $sql SQL query to execute. 00610 */ 00611 function query( $sql, $server = false ) 00612 { 00613 } 00614 00615 /*! 00616 \pure 00617 Executes an SQL query and returns the result as an array of accociative arrays. 00618 00619 \param $sql SQL query to execute. 00620 \param $params Associative array containing extra parameters, can contain: 00621 - offset - The offset of the query. 00622 - limit - The limit of the query. 00623 - column - Limit returned row arrays to only contain this column. 00624 \param $server Which server to execute the query on, either eZDBInterface::SERVER_MASTER or eZDBInterface::SERVER_SLAVE 00625 00626 An example would be: 00627 \code 00628 $db->arrayQuery( 'SELECT * FROM eztable', array( 'limit' => 10, 'offset' => 5 ) ); 00629 \endcode 00630 */ 00631 function arrayQuery( $sql, $params = array(), $server = false ) 00632 { 00633 } 00634 00635 /*! 00636 \pure 00637 Locks a table 00638 */ 00639 function lock( $table ) 00640 { 00641 } 00642 00643 /*! 00644 \pure 00645 Releases table locks. 00646 */ 00647 function unlock() 00648 { 00649 } 00650 00651 /*! 00652 Begin a new transaction. If we are already in transaction then we omit 00653 this new transaction and its matching commit or rollback. 00654 */ 00655 function begin() 00656 { 00657 $ini = eZINI::instance(); 00658 if ($ini->variable( "DatabaseSettings", "Transactions" ) == "enabled") 00659 { 00660 if ( $this->TransactionCounter > 0 ) 00661 { 00662 if ( is_array( $this->TransactionStackTree ) ) 00663 { 00664 // Make a new sub-level for debugging 00665 $bt = debug_backtrace(); 00666 $subLevels =& $this->TransactionStackTree['sub_levels']; 00667 for ( $i = 1; $i < $this->TransactionCounter; ++$i ) 00668 { 00669 $subLevels =& $subLevels[count( $subLevels ) - 1]['sub_levels']; 00670 } 00671 // Next entry will be at the end 00672 $subLevels[count( $subLevels )] = array( 'level' => $this->TransactionCounter, 00673 'trace' => $bt, 00674 'sub_levels' => array() ); 00675 } 00676 ++$this->TransactionCounter; 00677 return false; 00678 } 00679 else 00680 { 00681 if ( is_array( $this->TransactionStackTree ) ) 00682 { 00683 // Start new stack tree for debugging 00684 $bt = debug_backtrace(); 00685 $this->TransactionStackTree = array( 'level' => $this->TransactionCounter, 00686 'trace' => $bt, 00687 'sub_levels' => array() ); 00688 } 00689 } 00690 $this->TransactionIsValid = true; 00691 00692 if ( $this->isConnected() ) 00693 { 00694 $oldRecordError = $this->RecordError; 00695 // Turn off error handling while we begin 00696 $this->RecordError = false; 00697 $this->beginQuery(); 00698 $this->RecordError = $oldRecordError; 00699 00700 // We update the transaction counter after the query, otherwise we 00701 // mess up the debug background highlighting. 00702 ++$this->TransactionCounter; 00703 } 00704 } 00705 return true; 00706 } 00707 00708 /*! 00709 \virtual 00710 The query to start a transaction. 00711 This function must be reimplemented in the subclasses. 00712 */ 00713 function beginQuery() 00714 { 00715 return false; 00716 } 00717 00718 /*! 00719 Commits the current transaction. If this is not the outermost it will not commit 00720 to the database immediately but instead decrease the transaction counter. 00721 00722 If the current transaction had any errors in it the transaction will be rollbacked 00723 instead of commited. This ensures that the database is in a valid state. 00724 Also the PHP execution will be stopped. 00725 00726 \return \c true if the transaction was successful, \c false otherwise. 00727 */ 00728 function commit() 00729 { 00730 $ini = eZINI::instance(); 00731 if ($ini->variable( "DatabaseSettings", "Transactions" ) == "enabled") 00732 { 00733 if ( $this->TransactionCounter <= 0 ) 00734 { 00735 eZDebug::writeError( 'No transaction in progress, cannot commit', 'eZDBInterface::commit' ); 00736 return false; 00737 } 00738 00739 --$this->TransactionCounter; 00740 if ( $this->TransactionCounter == 0 ) 00741 { 00742 if ( is_array( $this->TransactionStackTree ) ) 00743 { 00744 // Reset the stack debug tree since the top commit was done 00745 $this->TransactionStackTree = array(); 00746 } 00747 if ( $this->isConnected() ) 00748 { 00749 // Check if we have encountered any problems, if so we have to rollback 00750 if ( !$this->TransactionIsValid ) 00751 { 00752 $oldRecordError = $this->RecordError; 00753 // Turn off error handling while we rollback 00754 $this->RecordError = false; 00755 $this->rollbackQuery(); 00756 $this->RecordError = $oldRecordError; 00757 00758 return false; 00759 } 00760 else 00761 { 00762 $oldRecordError = $this->RecordError; 00763 // Turn off error handling while we commit 00764 $this->RecordError = false; 00765 $this->commitQuery(); 00766 $this->RecordError = $oldRecordError; 00767 } 00768 } 00769 } 00770 else 00771 { 00772 if ( is_array( $this->TransactionStackTree ) ) 00773 { 00774 // Close the last open nested transaction 00775 $bt = debug_backtrace(); 00776 // Store commit trace 00777 $subLevels =& $this->TransactionStackTree['sub_levels']; 00778 for ( $i = 1; $i < $this->TransactionCounter; ++$i ) 00779 { 00780 $subLevels =& $subLevels[count( $subLevels ) - 1]['sub_levels']; 00781 } 00782 // Find last entry and add the commit trace 00783 $subLevels[count( $subLevels ) - 1]['commit_trace'] = $bt; 00784 } 00785 } 00786 } 00787 return true; 00788 } 00789 00790 /*! 00791 \virtual 00792 The query to commit the transaction. 00793 This function must be reimplemented in the subclasses. 00794 */ 00795 function commitQuery() 00796 { 00797 return false; 00798 } 00799 00800 /*! 00801 Cancels the transaction. 00802 */ 00803 function rollback() 00804 { 00805 if ( is_array( $this->TransactionStackTree ) ) 00806 { 00807 // All transactions were rollbacked, reset the tree. 00808 $this->TransactionStackTree = array(); 00809 } 00810 $ini = eZINI::instance(); 00811 if ($ini->variable( "DatabaseSettings", "Transactions" ) == "enabled") 00812 { 00813 if ( $this->TransactionCounter <= 0 ) 00814 { 00815 eZDebug::writeError( 'No transaction in progress, cannot rollback', 'eZDBInterface::rollback' ); 00816 return false; 00817 } 00818 // Reset the transaction counter 00819 $this->TransactionCounter = 0; 00820 if ( $this->isConnected() ) 00821 { 00822 $oldRecordError = $this->RecordError; 00823 // Turn off error handling while we rollback 00824 $this->RecordError = false; 00825 $this->rollbackQuery(); 00826 $this->RecordError = $oldRecordError; 00827 } 00828 } 00829 return true; 00830 } 00831 00832 /*! 00833 Goes through the transaction stack tree $this->TransactionStackTree and 00834 generates the text output for it and returns it. 00835 \returns The generated string or false if it is disabled. 00836 */ 00837 function generateFailedTransactionStack() 00838 { 00839 if ( !$this->TransactionStackTree ) 00840 { 00841 return false; 00842 } 00843 return $this->generateFailedTransactionStackEntry( $this->TransactionStackTree, 0 ); 00844 } 00845 00846 /*! 00847 \private 00848 Recursive helper function for generating stack tree output. 00849 \returns The generated string 00850 */ 00851 function generateFailedTransactionStackEntry( $stack, $indentCount ) 00852 { 00853 $stackText = ''; 00854 $indent = ''; 00855 if ( $indentCount > 0 ) 00856 { 00857 $indent = str_repeat( " ", $indentCount*4 ); 00858 } 00859 $stackText .= $indent . "Level " . $stack['level'] . "\n" . $indent . "{" . $indent . "\n"; 00860 $stackText .= $indent . " Began at:\n"; 00861 for ( $i = 0; $i < 2 && $i < count( $stack['trace'] ); ++$i ) 00862 { 00863 $indent2 = str_repeat( " ", $i + 1 ); 00864 if ( $i > 0 ) 00865 { 00866 $indent2 .= "->"; 00867 } 00868 $stackText .= $indent . $indent2 . $this->generateTraceEntry( $stack['trace'][$i] ); 00869 $stackText .= "\n"; 00870 } 00871 foreach ( $stack['sub_levels'] as $subStack ) 00872 { 00873 $stackText .= $this->generateFailedTransactionStackEntry( $subStack, $indentCount + 1 ); 00874 } 00875 if ( isset( $stack['commit_trace'] ) ) 00876 { 00877 $stackText .= $indent . " And commited at:\n"; 00878 for ( $i = 0; $i < 2 && $i < count( $stack['commit_trace'] ); ++$i ) 00879 { 00880 $indent2 = str_repeat( " ", $i + 1 ); 00881 if ( $i > 0 ) 00882 { 00883 $indent2 .= "->"; 00884 } 00885 $stackText .= $indent . $indent2 . $this->generateTraceEntry( $stack['commit_trace'][$i] ); 00886 $stackText .= "\n"; 00887 } 00888 } 00889 $stackText .= $indent . "}" . "\n"; 00890 return $stackText; 00891 } 00892 00893 /*! 00894 \private 00895 Helper function for generating output for one stack-trace entry. 00896 \returns The generated string 00897 */ 00898 function generateTraceEntry( $entry ) 00899 { 00900 if ( isset( $entry['file'] ) ) 00901 { 00902 $stackText = $entry['file']; 00903 } 00904 else 00905 { 00906 $stackText = "???"; 00907 } 00908 $stackText .= ":"; 00909 if ( isset( $entry['line'] ) ) 00910 { 00911 $stackText .= $entry['line']; 00912 } 00913 else 00914 { 00915 $stackText .= "???"; 00916 } 00917 $stackText .= " "; 00918 if ( isset( $entry['class'] ) ) 00919 { 00920 $stackText .= $entry['class']; 00921 } 00922 else 00923 { 00924 $stackText .= "???"; 00925 } 00926 $stackText .= "::"; 00927 if ( isset( $entry['function'] ) ) 00928 { 00929 $stackText .= $entry['function']; 00930 } 00931 else 00932 { 00933 $stackText .= "???"; 00934 } 00935 return $stackText; 00936 } 00937 00938 /*! 00939 \virtual 00940 The query to cancel the transaction. 00941 This function must be reimplemented in the subclasses. 00942 */ 00943 function rollbackQuery() 00944 { 00945 return false; 00946 } 00947 00948 /*! 00949 Invalidates the current transaction, see commit() for more details on this. 00950 \return \c true if it was invalidated or \c false if there is no transaction to invalidate. 00951 00952 \sa isTransactionValid() 00953 */ 00954 function invalidateTransaction() 00955 { 00956 if ( $this->TransactionCounter <= 0 ) 00957 return false; 00958 $this->TransactionIsValid = false; 00959 return true; 00960 } 00961 00962 /*! 00963 \protected 00964 This is called whenever an error occurs in one of the database handlers. 00965 If a transaction is active it will be invalidated as well. 00966 */ 00967 function reportError() 00968 { 00969 // If we have a running transaction we must mark as invalid 00970 // in which case a call to commit() will perform a rollback 00971 if ( $this->TransactionCounter > 0 ) 00972 { 00973 $this->invalidateTransaction(); 00974 00975 // This is the unique ID for this incidence which will also be placed in the error logs. 00976 $transID = 'TRANSID-' . md5( time() . mt_rand() ); 00977 00978 eZDebug::writeError( 'Transaction in progress failed due to DB error, transaction was rollbacked. Transaction ID is ' . $transID . '.', 'eZDBInterface::commit ' . $transID ); 00979 00980 $oldRecordError = $this->RecordError; 00981 // Turn off error handling while we rollback 00982 $this->RecordError = false; 00983 $this->rollbackQuery(); 00984 $this->RecordError = $oldRecordError; 00985 00986 // Stop execution immediately while allowing other systems (session etc.) to cleanup 00987 require_once( 'lib/ezutils/classes/ezexecution.php' ); 00988 eZExecution::cleanup(); 00989 eZExecution::setCleanExit(); 00990 00991 // Give some feedback, and also possibly show the debug output 00992 eZDebug::setHandleType( eZDebug::HANDLE_NONE ); 00993 00994 $ini = eZINI::instance(); 00995 $adminEmail = $ini->variable( 'MailSettings', 'AdminEmail' ); 00996 //include_once( 'lib/ezutils/classes/ezsys.php' ); 00997 00998 if ( !eZSys::isShellExecution() ) 00999 { 01000 $site = eZSys::serverVariable( 'HTTP_HOST' ); 01001 $uri = eZSys::serverVariable( 'REQUEST_URI' ); 01002 01003 print( "<div class=\"fatal-error\" style=\"" ); 01004 print( 'margin: 0.5em 0 1em 0; ' . 01005 'padding: 0.25em 1em 0.75em 1em;' . 01006 'border: 4px solid #000000;' . 01007 'background-color: #f8f8f4;' . 01008 'border-color: #f95038;" >' ); 01009 print( "<b>Fatal error</b>: A database transaction in eZ Publish failed.<br/>" ); 01010 print( "<p>" ); 01011 print( "The current execution was stopped to prevent further problems.<br/>\n" . 01012 "You should contact the <a href=\"mailto:$adminEmail?subject=Transaction failed on $site and URI $uri with ID $transID\">System Administrator</a> of this site with the information on this page.<br/>\n" . 01013 "The current transaction ID is <b>$transID</b> and has been logged.<br/>\n" . 01014 "Please include the transaction ID and the current URL when contacting the system administrator.<br/>\n" ); 01015 print( "</p>" ); 01016 print( "</div>" ); 01017 01018 $templateResult = null; 01019 if ( function_exists( 'eZDisplayResult' ) ) 01020 { 01021 eZDisplayResult( $templateResult ); 01022 } 01023 } 01024 else 01025 { 01026 fputs( STDERR,"Fatal error: A database transaction in eZ Publish failed.\n" ); 01027 fputs( STDERR, "\n" ); 01028 fputs( STDERR, "The current execution was stopped to prevent further problems.\n" . 01029 "You should contact the System Administrator ($adminEmail) of this site.\n" . 01030 "The current transaction ID is $transID and has been logged.\n" . 01031 "Please include the transaction ID and the name of the current script when contacting the system administrator.\n" ); 01032 fputs( STDERR, "\n" ); 01033 01034 fputs( STDERR, eZDebug::printReport( false, false, true ) ); 01035 } 01036 01037 // PHP execution stops here 01038 exit( 1 ); 01039 } 01040 } 01041 01042 /*! 01043 \return \c true if the current or last running transaction was valid, 01044 \c false otherwise. 01045 \sa invalidateTransaction() 01046 */ 01047 function isTransactionValid() 01048 { 01049 return $this->TransactionIsValid; 01050 } 01051 01052 /*! 01053 \return The current transaction counter. 01054 01055 0 means no transactions, 1 or higher means 1 or more transactions 01056 are running and a negative value means something is wrong. 01057 */ 01058 function transactionCounter() 01059 { 01060 return $this->TransactionCounter; 01061 } 01062 01063 /*! 01064 \pure 01065 \return the relation count for all relation types in the mask \a $relationMask. 01066 */ 01067 function relationCounts( $relationMask ) 01068 { 01069 } 01070 01071 /*! 01072 \pure 01073 \return the number of relation objects in the database for the relation type \a $relationType. 01074 */ 01075 function relationCount( $relationType = eZDBInterface::RELATION_TABLE ) 01076 { 01077 } 01078 01079 /*! 01080 \pure 01081 \return existing ez publish tables in database 01082 */ 01083 function eZTableList( $server = self::SERVER_MASTER ) 01084 { 01085 } 01086 01087 /*! 01088 \pure 01089 \return the relation names in the database as an array for the relation type \a $relationType. 01090 */ 01091 function relationList( $relationType = eZDBInterface::RELATION_TABLE ) 01092 { 01093 } 01094 01095 /*! 01096 \pure 01097 Tries to remove the relation type \a $relationType named \a $relationName 01098 \return \c true if successful 01099 */ 01100 function removeRelation( $relationName, $relationType ) 01101 { 01102 return false; 01103 } 01104 01105 /*! 01106 \protected 01107 \return the name of the relation type which is usable in SQL or false if unknown type. 01108 \note This function can be used by som database handlers which can operate on relation types using SQL. 01109 */ 01110 function relationName( $relationType ) 01111 { 01112 $names = array( eZDBInterface::RELATION_TABLE => 'TABLE', 01113 eZDBInterface::RELATION_SEQUENCE => 'SEQUENCE', 01114 eZDBInterface::RELATION_TRIGGER => 'TRIGGER', 01115 eZDBInterface::RELATION_VIEW => 'VIEW', 01116 eZDBInterface::RELATION_INDEX => 'INDEX' ); 01117 if ( !isset( $names[$relationType] ) ) 01118 return false; 01119 return $names[$relationType]; 01120 } 01121 01122 /*! 01123 \pure 01124 \return A regexp (PCRE) that can be used to filter out certain relation elements. 01125 If no special regexp is provided it will return \c false. 01126 \param $relationType The type which needs to be filtered, this allows one regexp per type. 01127 01128 An example, will only match tables that start with 'ez'. 01129 \code 01130 return "#^ez#"; 01131 \endcode 01132 01133 \note This function is currently used by the eZDBTool class to remove relation elements 01134 of a specific kind (Most likely eZ Publish related elements). 01135 */ 01136 function relationMatchRegexp( $relationType ) 01137 { 01138 return false; 01139 } 01140 01141 /*! 01142 Casts elements of \a $pieces to type \a $type and returns them as string separated by \a $glue. 01143 01144 \param $glue The separator. 01145 $pieces The array containing the elements. 01146 $type The type to cast to. 01147 01148 Example: 01149 \code 01150 implodeWithTypeCast( ',', $myArray, 'int' ) 01151 \endcode 01152 01153 */ 01154 function implodeWithTypeCast( $glue, &$pieces, $type ) 01155 { 01156 $str = ''; 01157 if ( !is_array( $pieces ) ) 01158 return $str; 01159 01160 foreach( $pieces as $piece ) 01161 { 01162 settype( $piece, $type ); 01163 $str .= $piece.$glue; 01164 } 01165 $str = rtrim( $str, $glue ); 01166 return $str; 01167 } 01168 01169 /*! 01170 \pure 01171 Returns the last serial ID generated with an auto increment field. 01172 */ 01173 function lastSerialID( $table = false, $column = false ) 01174 { 01175 } 01176 01177 /*! 01178 \pure 01179 Will escape a string so it's ready to be inserted in the database. 01180 */ 01181 function escapeString( $str ) 01182 { 01183 return $str; 01184 } 01185 01186 /*! 01187 \pure 01188 Will close the database connection. 01189 */ 01190 function close() 01191 { 01192 } 01193 01194 /*! 01195 \protected 01196 Returns true if we're connected to the database backend. 01197 */ 01198 function isConnected() 01199 { 01200 return $this->IsConnected; 01201 } 01202 01203 /*! 01204 \pure 01205 Create a new database 01206 */ 01207 function createDatabase( $dbName ) 01208 { 01209 } 01210 01211 /*! 01212 Create a new temporary table 01213 */ 01214 function createTempTable( $createTableQuery = '', $server = self::SERVER_SLAVE ) 01215 { 01216 $this->query( $createTableQuery, $server ); 01217 } 01218 01219 /*! 01220 Drop temporary table 01221 */ 01222 function dropTempTable( $dropTableQuery = '', $server = self::SERVER_SLAVE ) 01223 { 01224 $this->query( $dropTableQuery, $server ); 01225 } 01226 01227 /*! 01228 Drop temporary table list 01229 */ 01230 function dropTempTableList( $tableList, $server = self::SERVER_SLAVE ) 01231 { 01232 foreach( $tableList as $tableName ) 01233 $this->dropTempTable( "DROP TABLE $tableName", $server ); 01234 } 01235 01236 /*! 01237 \pure 01238 Sets the error message and error message number 01239 */ 01240 function setError() 01241 { 01242 } 01243 01244 /*! 01245 Returns the error message 01246 */ 01247 function errorMessage() 01248 { 01249 return $this->ErrorMessage; 01250 } 01251 01252 /*! 01253 Returns the error number 01254 */ 01255 function errorNumber() 01256 { 01257 return $this->ErrorNumber; 01258 } 01259 01260 /*! 01261 Return alvailable databases in database. 01262 01263 \return array of available databases, 01264 null of none available 01265 false if listing databases not supported by database 01266 */ 01267 function availableDatabases() 01268 { 01269 return false; 01270 } 01271 01272 /*! 01273 Generate unique table name basing on the given pattern. 01274 If the pattern contains a (%) character then the character 01275 is replaced with a part providing uniqueness (e.g. random number). 01276 */ 01277 function generateUniqueTempTableName( $pattern, $randomizeIndex = false, $server = self::SERVER_SLAVE ) 01278 { 01279 $tableList = array_keys( $this->eZTableList( $server ) ); 01280 if ( $randomizeIndex === false ) 01281 { 01282 $randomizeIndex = rand( 10, 1000 ); 01283 } 01284 do 01285 { 01286 $uniqueTempTableName = str_replace( '%', $randomizeIndex, $pattern ); 01287 $randomizeIndex++; 01288 } while ( in_array( $uniqueTempTableName, $tableList ) ); 01289 01290 return $uniqueTempTableName; 01291 } 01292 01293 /*! 01294 Get database version number 01295 01296 \return version number 01297 false if not supported 01298 */ 01299 function version() 01300 { 01301 return false; 01302 } 01303 01304 /*! 01305 \static 01306 01307 This function can be used to create a SQL IN statement to be used in a WHERE clause: 01308 01309 WHERE columnName IN ( element1, element2, ... ) 01310 01311 By default, the elements that can be submitted as an anonymous array (or an integer value 01312 in case of a single element) will just be imploded. Drivers for databases with a limitation 01313 of the elements within an IN statement have to reimplement this function. It is also possible 01314 to negate the "IN" to a "NOT IN" by using the last parameter of this function. 01315 01316 Usage: 01317 01318 $db =& eZDb::instance(); 01319 $db->generateSQLINStatement( array( 2, 5, 43, ) ); 01320 01321 \param $elements Elements that should (not) be matched by the IN statment as an integer or anonymous array 01322 \param $columnName Column name of the database table the IN statement should be created for 01323 \param $not Will generate a "NOT IN" ( if set to \c true ) statement instead of an "IN" ( if set to 01324 \c false , default ) 01325 \param $unique 01326 \param $type The type to cast the array elements to 01327 01328 \return A string with the correct IN statement like for example 01329 "columnName IN ( element1, element2 )" 01330 */ 01331 function generateSQLINStatement( $elements, $columnName = '', $not = false, $unique = true, $type = false ) 01332 { 01333 $result = ''; 01334 $statement = $columnName . ' IN'; 01335 if ( $not === true ) 01336 { 01337 $statement = $columnName . ' NOT IN'; 01338 } 01339 01340 if ( !is_array( $elements ) ) 01341 { 01342 $elements = array( $elements ); 01343 } 01344 01345 $impString = $type ? $this->implodeWithTypeCast( ', ', $elements, $type ) : implode( ', ', $elements ); 01346 $result = $statement . ' ( ' . $impString . ' )'; 01347 01348 return $result; 01349 } 01350 01351 function supportsDefaultValuesInsertion() 01352 { 01353 return true; 01354 } 01355 01356 /// \protectedsection 01357 /// Contains the current server 01358 public $Server; 01359 /// Contains the current port 01360 public $Port; 01361 /// The socket path, used by MySQL 01362 public $SocketPath; 01363 /// The current database name 01364 public $DB; 01365 /// The current connection, \c false if not connection has been made 01366 public $DBConnection; 01367 /// Contains the write database connection if used 01368 public $DBWriteConnection; 01369 /// Stores the database connection user 01370 public $User; 01371 /// Stores the database connection password 01372 public $Password; 01373 /// The charset used for the current database 01374 public $Charset; 01375 /// The number of times to retry a connection if it fails 01376 public $ConnectRetries; 01377 /// Instance of a textcodec which handles text conversion, may not be set if no builtin encoding is used 01378 public $OutputTextCodec; 01379 public $InputTextCodec; 01380 01381 /// True if a builtin encoder is to be used, this means that all input/output text is converted 01382 public $UseBuiltinEncoding; 01383 /// Setting if SQL queries should be sent to debug output 01384 public $OutputSQL; 01385 /// Contains true if we're connected to the database backend 01386 public $IsConnected = false; 01387 /// Contains number of queries sended to DB 01388 public $NumQueries = 0; 01389 /// The start time of the timer 01390 public $StartTime; 01391 /// The end time of the tiemr 01392 public $EndTime; 01393 /// The total number of milliseconds the timer took 01394 public $TimeTaken; 01395 /// The database error message of the last executed function 01396 public $ErrorMessage; 01397 /// The database error message number of the last executed function 01398 public $ErrorNumber = 0; 01399 /// If true then ErrorMessage and ErrorNumber get filled 01400 public $RecordError = true; 01401 /// If true then the database connection should be persistent 01402 public $UsePersistentConnection = false; 01403 /// Contains true if slave servers are enabled 01404 public $UserSlaveServer; 01405 /// The slave database name 01406 public $SlaveDB; 01407 /// The slave server name 01408 public $SlaveServer; 01409 /// The slave server port 01410 public $SlavePort; 01411 /// The slave database user 01412 public $SlaveUser; 01413 /// The slave database user password 01414 public $SlavePassword; 01415 /// The transaction counter, 0 means no transaction 01416 public $TransactionCounter; 01417 /// Flag which tells if a transaction is considered valid or not 01418 /// A transaction will be made invalid if SQL errors occur 01419 public $TransactionIsValid; 01420 } 01421 01422 ?>