Logo Search packages:      
Sourcecode: b2evolution version File versions  Download package

_class_db.php

<?php
/**
 * This file implements the DB class.
 *
 * Based on ezSQL - Class to make it very easy to deal with MySQL database connections.
 * b2evo Additions:
 * - nested transactions
 * - symbolic table names
 * - query log
 * - get_list
 * - dynamic extension loading
 * - Debug features (EXPLAIN...)
 * and more...
 *
 * This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
 * See also {@link http://sourceforge.net/projects/evocms/}.
 *
 * @copyright (c)2003-2005 by Francois PLANQUE - {@link http://fplanque.net/}.
 * Parts of this file are copyright (c)2004 by Justin Vincent - {@link http://php.justinvincent.com}
 * Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
 *
 * @license http://b2evolution.net/about/license.html GNU General Public License (GPL)
 * {@internal
 * b2evolution is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * b2evolution is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with b2evolution; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * }}
 *
 * {@internal
 * This file is based on the following package (excerpt from ezSQL's readme.txt):
 * =======================================================================
 * Author:  Justin Vincent (justin@visunet.ie)
 * Web:      http://php.justinvincent.com
 * Name:     ezSQL
 * Desc:     Class to make it very easy to deal with database connections.
 * License: FREE / Donation (LGPL - You may do what you like with ezSQL - no exceptions.)
 * =======================================================================
 * A $10 donation has been made to Justin VINCENT on behalf of the b2evolution team.
 * The package has been relicensed as GPL based on
 * "You may do what you like with ezSQL - no exceptions."
 * 2004-10-14 (email): Justin VINCENT grants François PLANQUE the right to relicense
 * this modified class under other licenses. "Just include a link to where you got it from."
 * }}
 *
 * {@internal
 * Daniel HAHLER grants François PLANQUE the right to license
 * Daniel HAHLER's contributions to this file and the b2evolution project
 * under any OSI approved OSS license (http://www.opensource.org/licenses/).
 * }}
 *
 * @package evocore
 *
 * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
 * @author blueyed: Daniel HAHLER
 * @author fplanque: François PLANQUE
 * @author Justin VINCENT
 *
 * @version $Id: _class_db.php,v 1.11.2.9 2005/09/02 21:31:34 fplanque Exp $
 * @todo transaction support
 */
if( !defined('DB_USER') ) die( 'Please, do not access this page directly.' );

/**
 * ezSQL Constants
 */
define( 'EZSQL_VERSION', '1.25' );
define( 'OBJECT', 'OBJECT', true );
define( 'ARRAY_A', 'ARRAY_A', true);
define( 'ARRAY_N', 'ARRAY_N', true);

if( ! function_exists( 'mysql_real_escape_string' ) )
{ // Function only available since PHP 4.3.0
      function mysql_real_escape_string( $unescaped_string ) 
      {
            return mysql_escape_string( $unescaped_string ); 
      }
}

/**
 * The Main Class
 *
 * @package evocore
 */
class DB
{
      /**
       * Do we want to explain joins?
       */
      var $debug_explain_joins = false;

  /**
       * Number of rows we want to dump in debug output:
       */
      var $debug_dump_rows = 0;

      var $show_errors = true;
      var $halt_on_error = true;
      var $error = false;           // no error yet
      var $num_queries = 0;
      var $last_query = '';         // last query SQL string
      var $last_error = '';               // last DB error string
      var $col_info;

      var $vardump_called;
      var $insert_id = 0;
      var $num_rows = 0;
      var $rows_affected = 0;
      var $last_result;
      /**
       * Log of queries:
       */
      var $queries = array();
      /**
       * Aliases that will be replaced in queries:
       */
      var $dbaliases = array();
      /**
       * Strings that will replace the aliases in queries:
       */
      var $dbreplaces = array();

      /**
       * CREATE TABLE options.
       */
      var $dbtableoptions;

      /**
       * Do we want to use transactions:
       */
      var $use_transactions;

      /**
       * How many transactions are currently nested?
       */
      var $transaction_nesting_level = 0;

      /**
       * Rememeber if we have to rollback at the end of a nested transaction construct
       */
      var $rollback_nested_transaction = false;


      /**
       * DB Constructor
       *
       * connects to the server and selects a database
       *
       * blueyed> Note: Too many parameters (and without default). Should be accessed through members. $halt_on_error is relevant to the connect procedure and should be put after $dbhost.
       */
      function DB( $dbuser, $dbpassword, $dbname, $dbhost, $dbaliases, $halt_on_error = true, $db_use_transactions = false, $dbtableoptions = '' )
      {
            $this->halt_on_error = $halt_on_error;

            if( !extension_loaded('mysql') )
            {     // The mysql extension is not loaded, try to dynamically load it:
                  if (strtoupper(substr(PHP_OS, 0, 3) == 'WIN'))
                  {
                        @dl('php_mysql.dll');
                  }
                  else
                  {
                        @dl('mysql.so');
                  }

                  if( !extension_loaded('mysql') )
                  { // Still not loaded:
                        $this->print_error( '<p><strong>The PHP MySQL module could not be loaded.</strong></p>
                              <p>You must edit your php configuration (php.ini) and enable this module.</p>
                              <p>Do not forget to restart your webserver (if necessary) after editing the PHP conf.</p>' );
                        return;
                  }
            }

            // Connect to the Database:
            $this->dbh = @mysql_connect($dbhost,$dbuser,$dbpassword);

            if( ! $this->dbh )
            {
                  $this->print_error( '<p><strong>Error establishing a database connection!</strong></p>
                        <p>('.mysql_error().')</p>
                        <ol>
                              <li>Are you sure you have typed the correct user/password?</li>
                              <li>Are you sure that you have typed the correct hostname?</li>
                              <li>Are you sure that the database server is running?</li>
                        </ol>' );
            }
            else
            {
                  $this->select($dbname);
            }

            // Prepare aliases for replacements:
            foreach( $dbaliases as $dbalias => $dbreplace )
            {
                  $this->dbaliases[] = '#\b'.$dbalias.'\b#'; // \b = word boundary
                  $this->dbreplaces[] = $dbreplace;
                  // echo '<br />'.'#\b'.$dbalias.'\b#';
            }
            // echo count($this->dbaliases);

            $this->use_transactions = $db_use_transactions;
            $this->dbtableoptions = $dbtableoptions;
      }


      /**
       * Select a DB (if another one needs to be selected)
       */
      function select($db)
      {
            if ( !@mysql_select_db($db,$this->dbh))
            {
                  $this->print_error( '<strong>Error selecting database ['.$db.']!</strong>
                        <ol>
                              <li>Are you sure the database exists?</li>
                              <li>Are you sure there is a valid database connection?</li>
                        </ol>' );
            }
      }


      /**
       * Format a string correctly for safe insert under all PHP conditions
       */
      function escape($str)
      {
            return mysql_real_escape_string($str);                      
      }


      function quote($str)
      {
            if( $str === NULL )
                  return 'NULL';
            else
                  return "'".mysql_real_escape_string($str)."'";                    
      }


      function null($val)
      {
            if( $val === NULL )
                  return 'NULL';
            else
                  return $val;                        
      }


      /**
       * Returns the correct WEEK() function to get the week number for the given date.
       *
       * {@see http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html}
       *
       * @todo disable when MySQL < 4
       * @param string will be used as is
       * @param integer 0 for sunday, 1 for monday
       */
      function week( $date, $startofweek )
      {
            if( $startofweek == 1 )
            {     // Week starts on Monday:
                  return ' WEEK( '.$date.', 5 ) ';
            }

            // Week starts on Sunday:
            return ' WEEK( '.$date.', 0 ) ';
      }

      /**
       * Returns the appropriate string to compare $val in a WHERE clause.
       *
       * @param mixed Value to create a "compare-String" for
       * @return string Either 'IS NULL', 'IN ("a", "b", "c")' or " = 'a'".
       */
      function compString( $val )
      {
            if( $val === NULL )
            {
                  return 'IS NULL';
            }
            elseif( is_array($val) )
            {
                  return 'IN ("'.implode('","', $val).'")';
            }
            else
            {
                  return " = '$root'";
            }
      }


      /**
       * Print SQL/DB error.
       */
      function print_error( $str = '', $query_title = '' )
      {
            // All errors go to the global error array $EZSQL_ERROR..
            global $EZSQL_ERROR;

            $this->error = true;

            // If no special error string then use mysql default..
            $this->last_error = empty($str) ? ( mysql_error().'(Errno='.mysql_errno().')' ) : $str;
            
            // Log this error to the global array..
            $EZSQL_ERROR[] = array 
                                    (
                                          'query' => $this->last_query,
                                          'error_str'  => $this->last_error
                                    );

            // Is error output turned on or not..
            if ( $this->show_errors )
            {
                  // If there is an error then take note of it
                  echo '<div class="error">';
                  echo '<p class="error">MySQL error!</p>';
                  echo '<p>', $this->last_error, '</p>';
                  if( !empty($this->last_query) ) echo '<p class="error">Your query: '.$query_title.'<br /><pre>'.htmlspecialchars( str_replace("\t", '  ', $this->last_query) ).'</pre></p>';

                  if( function_exists( 'xdebug_is_enabled' ) && xdebug_is_enabled() )
                  {
                        ?>
                        <table class="grouped">
                              <thead>
                                    <tr>
                                          <th>Function / Include</th>
                                          <th>File</th>
                                          <th>Line</th>
                                    </tr>
                              </thead>

                        <?php
                        foreach( xdebug_get_function_stack() as $lStack )
                        {
                              ?>
                              <tr>
                                    <td>
                                          <?php
                                          if( isset( $lStack['include_filename'] ) )
                                          {
                                                echo '<strong>=&gt;</strong> '.$lStack['include_filename'];
                                          }
                                          else
                                          {
                                                if( isset( $lStack['class'] ) )
                                                {
                                                      echo $lStack['class'].'::';
                                                }
                                                echo $lStack['function'].'()';
                                          }
                                          ?>
                                    </td>
                                    <td><?php echo $lStack['file'] ?></td>
                                    <td><?php echo $lStack['line'] ?></td>
                              </tr>
                              <?php
                        }
                        echo '</table>';
                  }

                  echo '</div>';
            }

            if( $this->halt_on_error ) die();
      }


      /**
       * Kill cached query results
       */
      function flush()
      {
            // Get rid of these
            $this->last_result = NULL;
            $this->col_info = NULL;
            $this->last_query = NULL;
            $this->current_idx = 0;
      }


      /** 
       * Basic Query
       *
       * {@internal DB::query(-) }}
       *
       * @param string SQL query
       * @param string title for debugging
       * @return mixed # of rows affected or false if error
       */
      function query( $query, $title = '' )
      {
            global $Timer;

            // initialise return
            $return_val = 0;

            // Flush cached values..
            $this->flush();

            // Log how the function was called
            $this->func_call = '$db->query("'.$query.'")';
            // echo $this->func_call, '<br />';

            // Replace aliases:
            $query = preg_replace( $this->dbaliases, $this->dbreplaces, $query );

            if( preg_match( '#^ \s* create \s* table \s #ix', $query) )
            {     // Query is a table creation, we add table options:
                  $query .= $this->dbtableoptions;
            }

            // Keep track of the last query for debug..
            $this->last_query = $query;

            // Perform the query via std mysql_query function..
            $this->num_queries++;
            $this->queries[ $this->num_queries - 1 ] = array(
                                                                                                                                                      'title' => $title,
                                                                                                                                                      'sql' => $query,
                                                                                                                                                      'rows' => -1,
                                                                                                                                                      'time' => 'unknown',
                                                                                                                                                      'results' => 'unknown' );

            if( is_object( $Timer ) )
            {
                  // Resume global query timer
                  $Timer->resume( 'sql_queries' );
                  // Start a timer fot this paritcular query:
                  $Timer->start( 'query' );
                  // Run query:
                  $this->result = @mysql_query( $query, $this->dbh );
                  // Get duration fpor last query:
                  $this->queries[ $this->num_queries - 1 ]['time'] = $Timer->get_duration( 'query' );
                  // Pause global query timer:
                  $Timer->pause( 'sql_queries' );
            }
            else
            {
                  $this->result = @mysql_query($query,$this->dbh);
            }

            // If there is an error then take note of it..
            if ( mysql_error() )
            {
                  $this->print_error( '', $title );
                  return false;
            }

            if( preg_match( '#^ \s* (insert|delete|update|replace) \s #ix', $query) )
            { // Query was an insert, delete, update, replace:

                  $this->rows_affected = mysql_affected_rows();
                  $this->queries[ $this->num_queries - 1 ]['rows'] = $this->rows_affected;

                  // Take note of the insert_id
                  if ( preg_match("/^\\s*(insert|replace) /i",$query) )
                  {
                        $this->insert_id = mysql_insert_id($this->dbh);
                  }

                  // Return number fo rows affected
                  $return_val = $this->rows_affected;
            }
            else
            { // Query was a select, alter, etc...:

                  // Take note of column info
                  $i = 0;
                  while( $i < @mysql_num_fields($this->result) )
                  {
                        $this->col_info[$i] = @mysql_fetch_field($this->result);
                        $i++;
                  }

                  // Store Query Results
                  $num_rows = 0;
                  while( $row = @mysql_fetch_object($this->result) )
                  {
                        // Store relults as an objects within main array
                        $this->last_result[$num_rows] = $row;
                        $num_rows++;
                  }

                  @mysql_free_result($this->result);

                  // Log number of rows the query returned
                  $this->num_rows = $num_rows;
                  $this->queries[ $this->num_queries - 1 ]['rows'] = $this->num_rows;

                  // Return number of rows selected
                  $return_val = $this->num_rows;
            }


            // EXPLAIN JOINS ??
            if( $this->debug_explain_joins && preg_match( '#^ \s* select \s #ix', $query) )
            { // Query was a select, let's try to explain joins...

                  // save values:
                  $saved_last_result = $this->last_result;
                  $saved_col_info = $this->col_info;
                  $saved_num_rows = $this->num_rows;

                  $this->last_result = NULL;
                  $this->col_info = NULL;
                  $this->num_rows = 0;

                  $this->result = @mysql_query( 'EXPLAIN '.$query, $this->dbh );
                  // Take note of column info
                  $i = 0;
                  while( $i < @mysql_num_fields($this->result) )
                  {
                        $this->col_info[$i] = @mysql_fetch_field($this->result);
                        $i++;
                  }

                  // Store Query Results
                  $num_rows = 0;
                  while( $row = @mysql_fetch_object($this->result) )
                  {
                        // Store results as an objects within main array
                        $this->last_result[$num_rows] = $row;
                        $num_rows++;
                  }

                  @mysql_free_result($this->result);

                  // Log number of rows the query returned
                  $this->num_rows = $num_rows;

                  $this->queries[ $this->num_queries - 1 ]['explain'] = $this->debug_dump_rows( 100, true );

                  // Retsore:
                  $this->last_result = $saved_last_result;
                  $this->col_info = $saved_col_info;
                  $this->num_rows = $saved_num_rows;
            }


            // If debug ALL queries
            if( $this->debug_dump_rows )
            {
                  $this->queries[ $this->num_queries - 1 ]['results'] = $this->debug_dump_rows( $this->debug_dump_rows );
            }

            return $return_val;
      }


      /**
       * Get one variable from the DB - see docs for more detail
       *
       * Note: To be sure that you received NULL from the DB and not "no rows" check
       *       for {@link $num_rows}.
       *
       * @return mixed NULL if not found, the value otherwise (which may also be NULL).
       */
      function get_var( $query = NULL, $x = 0, $y = 0, $title = '' )
      {
            // Log how the function was called
            $this->func_call = "\$db->get_var(\"$query\",$x,$y)";

            // If there is a query then perform it if not then use cached results..
            if( $query )
            {
                  $this->query($query, $title);
            }

            // Extract var out of cached results based x,y vals
            if( $this->last_result[$y] )
            {
                  $values = array_values(get_object_vars($this->last_result[$y]));
            }

            if( isset($values[$x]) )
            {
                  return $values[$x];
            }

            return NULL;
      }


      /**
       * Get one row from the DB - see docs for more detail
       *
       * @return array|object
       */
      function get_row( $query = NULL, $output = OBJECT, $y = 0, $title = '' )
      {
            // Log how the function was called
            $this->func_call = "\$db->get_row(\"$query\",$output,$y)";
            // echo $this->func_call, '<br />';

            // If there is a query then perform it if not then use cached results..
            if ( $query )
            {
                  $this->query($query, $title);
            }

            // If the output is an object then return object using the row offset..
            if ( $output == OBJECT )
            {
                  return $this->last_result[$y]
                                          ? $this->last_result[$y]
                                          : array();
            }
            // If the output is an associative array then return row as such..
            elseif ( $output == ARRAY_A )
            {
                  return $this->last_result[$y]
                                          ? get_object_vars( $this->last_result[$y] )
                                          : array();
            }
            // If the output is an numerical array then return row as such..
            elseif ( $output == ARRAY_N )
            {
                  return $this->last_result[$y]
                                          ? array_values( get_object_vars($this->last_result[$y]) )
                                          : array();
            }
            // If invalid output type was specified..
            else
            {
                  $this->print_error(" \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N");
            }
      }


      /**
       * Function to get 1 column from the cached result set based in X index
       * see docs for usage and info
       *
       * @return array
       */
      function get_col( $query = NULL, $x = 0, $title = '' )
      {
            // If there is a query then perform it if not then use cached results..
            if( $query )
            {
                  $this->query( $query, $title );
            }

            // Extract the column values
            $new_array = array();
            for( $i = 0, $count = count($this->last_result); $i < $count; $i++ )
            {
                  $new_array[$i] = $this->get_var( NULL, $x, $i );
            }

            return $new_array;
      }


      /**
       * Get a column as comma-seperated list.
       *
       * @param string|NULL Query to execute
       * @param integer Column of the result set
       * @return string
       */
      function get_list( $query = NULL, $x = 0, $title = '' )
      {
            return implode( ',', $this->get_col( $query, $x, $title ) );
      }
      

      /**
       * Return the the query as a result set - see docs for more details
       *
       * @return array
       */
      function get_results( $query = NULL, $output = OBJECT, $title = '' )
      {
            // Log how the function was called
            $this->func_call = "\$db->get_results(\"$query\", $output)";

            // If there is a query then perform it if not then use cached results..
            if( $query )
            {
                  $this->query($query, $title);
            }

            // Send back array of objects. Each row is an object
            if ( $output == OBJECT )
            {
                  return $this->last_result ? $this->last_result : array();
            }
            elseif ( $output == ARRAY_A || $output == ARRAY_N )
            {
                  $new_array = array();

                  if( $this->last_result )
                  {
                        $i = 0;

                        foreach( $this->last_result as $row )
                        {
                              $new_array[$i] = get_object_vars($row);

                              if ( $output == ARRAY_N )
                              {
                                    $new_array[$i] = array_values($new_array[$i]);
                              }

                              $i++;
                        }

                        return $new_array;
                  }
                  else
                  {
                        return array();
                  }
            }
      }


      /**
       * Function to get column meta data info pertaining to the last query
       * see docs for more info and usage
       */
      function get_col_info( $info_type = 'name', $col_offset = -1 )
      {
            if( $this->col_info )
            {
                  if( $col_offset == -1 )
                  {
                        $i = 0;
                        foreach($this->col_info as $col )
                        {
                              $new_array[$i] = $col->{$info_type};
                              $i++;
                        }
                        return $new_array;
                  }
                  else
                  {
                        return $this->col_info[$col_offset]->{$info_type};
                  }
            }
      }


      /**
       * Dumps the contents of any input variable to screen in a nicely
       * formatted and easy to understand way - any type: Object, Var or Array
       *
       * @param mixed Variable to dump
       */
      function vardump( $mixed = '' )
      {
            echo '<p><table><tr><td bgcolor="fff"><blockquote style="color:#000090">';
            echo '<pre style="font-family:Arial">';

            if ( ! $this->vardump_called )
            {
                  echo '<span style="color:#800080"><strong>ezSQL</strong> (v'.EZSQL_VERSION.") <strong>Variable Dump..</strong></span>\n\n";
            }

            $var_type = gettype ($mixed);
            print_r( ( $mixed ? $mixed : '<span style="color:#f00">No Value / False</span>') );
            echo "\n\n<strong>Type:</strong> ".ucfirst( $var_type )."\n"
                        ."<strong>Last Query</strong> [$this->num_queries]<strong>:</strong> "
                        .( $this->last_query ? $this->last_query : "NULL" )."\n"
                        .'<strong>Last Function Call:</strong> '.( $this->func_call ? $this->func_call : 'None' )."\n"
                        .'<strong>Last Rows Returned:</strong> '.count( $this->last_result )."\n"
                        .'</pre></blockquote></td></tr></table>';
            echo "\n<hr size=1 noshade color=dddddd>";

            $this->vardump_called = true;
      }


      /**
       * Alias for {@link vardump()}
       *
       * @param mixed Variable to dump
       */
      function dumpvar( $mixed )
      {
            $this->vardump( $mixed );
      }


      /**
       * Displays the last query string that was sent to the database & a
       * table listing results (if there were any).
       * (abstracted into a seperate file to save server overhead).
       */
      function debug_dump_rows( $max_lines, $break_at_comma = false )
      {
            $r = '';

            if ( $this->col_info )
            {
                  // =====================================================
                  // Results top rows
                  $r .= '<table cellspacing="0">';
                  for( $i = 0, $count = count($this->col_info); $i < $count; $i++ )
                  {
                        $r .= '<th><span class="type">'.$this->col_info[$i]->type.' '.$this->col_info[$i]->max_length.'</span><br />'
                                          .$this->col_info[$i]->name.'</th>';
                  }
                  $r .= '</tr>';

                  $i=0;

                  // ======================================================
                  // print main results
                  if( $this->last_result )
                  {
                        foreach( $this->get_results(NULL,ARRAY_N) as $one_row )
                        {
                              $i++;
                              if( $i >= $max_lines )
                              {
                                    break;
                              }
                              foreach( $one_row as $item )
                              {
                                    if( $i % 2 )
                                    {
                                          $r .= '<td class="odd">';
                                    }
                                    else
                                    {
                                          $r .= '<td>';
                                    }

                                    if( $break_at_comma )
                                    {
                                          $item = str_replace( ',', '<br />', $item );
                                          $item = str_replace( ';', '<br />', $item );
                                          $r .= $item;
                                    }
                                    else
                                    {
                                          if( strlen( $item ) > 50 )
                                          {
                                                $item = substr( $item, 0, 50 ).'...';
                                          }
                                          $r .= htmlspecialchars($item);
                                    }
                                    $r .= '</td>';
                              }

                              $r .= '</tr>';
                        }

                  } // if last result
                  else
                  {
                        $r .= '<tr><td colspan="'.(count($this->col_info)+1).'">No Results</td></tr>';
                  }
                  if( $i >= $max_lines )
                  {
                        $r .= '<tr><td colspan="'.(count($this->col_info)+1).'">Max number of dumped rows has been reached.</td></tr>';
                  }

                  $r .= '</table>';

            } // if col_info
            else
            {
                  $r .= 'No Results';
            }


            return $r;
      }


      /** 
       * Displays all queries that have been exectuted
       *
       * {@internal DB::dump_queries(-) }}
       */
      function dump_queries()
      {
            global $Timer;
            if( is_object( $Timer ) )
            {
                  $time_queries = $Timer->get_duration( 'sql_queries' );
            }
            else
            {
                  $time_queries = 0;
            }

            foreach( $this->queries as $query )
            {
                  echo '<h4>Query: '.$query['title'].'</h4>';
                  echo '<code>';
                  $sql = str_replace( 'FROM', '<br />FROM', htmlspecialchars($query['sql']) );
                  $sql = str_replace( 'WHERE', '<br />WHERE', $sql );
                  $sql = str_replace( 'GROUP BY', '<br />GROUP BY', $sql );
                  $sql = str_replace( 'ORDER BY', '<br />ORDER BY', $sql );
                  $sql = str_replace( 'LIMIT', '<br />LIMIT', $sql );
                  $sql = str_replace( 'AND ', '<br />&nbsp; AND ', $sql );
                  $sql = str_replace( 'OR ', '<br />&nbsp; OR ', $sql );
                  $sql = str_replace( 'VALUES', '<br />VALUES', $sql );
                  echo $sql;
                  echo '</code>';
                  echo '<p class="rows">Rows: '.$query['rows'].' - Time: '.$query['time'].'s';
                  if( $time_queries > 0 )
                  {     // We have a total time we can use to calculate percentage:
                        echo ' ('.number_format( 100/$time_queries * $query['time'], 2 ).'%)';
                  }
                  echo '</p>';

                  // Explain:
                  if( isset($query['explain']) )
                  {
                        echo $query['explain'];
                  }

                  // Results:
                  if( $query['results'] != 'unknown' )
                  {
                        echo $query['results'];
                  }
            }
      }


      /**
       * BEGIN A TRANSCATION
       *
       * Note:  By default, MySQL runs with autocommit mode enabled.
       * This means that as soon as you execute a statement that updates (modifies)
       * a table, MySQL stores the update on disk.
       * Once you execute a BEGIN, the updates are "pending" until you execute a
       * COMMIT {@see DB::commit()} or a ROLLBACK {@see DB:rollback()}
       *
       * Note 2: standard syntax would be START TRANSACTION but it's not supported by older
       * MySQL versions whereas BEGIN is...
       *
       * Note 3: The default isolation level is REPEATABLE READ.
       */
      function begin()
      {
            if( $this->use_transactions )
            {
                  $this->query( 'BEGIN', 'BEGIN transaction' );

                  $this->transaction_nesting_level++;
            }
      }


      /**
       * Commit current transaction
       */
      function commit()
      {
            if( $this->use_transactions )
            {
                  if( $this->transaction_nesting_level == 1 )
                  {     // Only COMMIT if there are no remaining nested transactions:
                        if( $this->rollback_nested_transaction )
                        {
                              $this->query( 'ROLLBACK', 'ROLLBACK transaction because there was a failure somewhere in the nesting of transactions' );
                        }
                        else
                        {
                              $this->query( 'COMMIT', 'COMMIT transaction' );
                        }
                        $this->rollback_nested_transaction = false;
                  }
                  $this->transaction_nesting_level--;
            }
      }


      /**
       * @todo implement transactions!
       */
      function rollback()
      {
            if( $this->use_transactions )
            {
                  if( $this->transaction_nesting_level == 1 )
                  {     // Only ROLLBACK if there are no remaining nested transactions:
                        $this->query( 'ROLLBACK', 'ROLLBACK transaction' );
                        $this->rollback_nested_transaction = false;
                  }
                  else
                  {     // Remember we'll have to roll back at the end!
                        $this->rollback_nested_transaction = true;
                  }
                  $this->transaction_nesting_level--;
            }
      }

}

/*
 * $Log: _class_db.php,v $
 * Revision 1.11.2.9  2005/09/02 21:31:34  fplanque
 * enhanced query debugging features
 *
 * Revision 1.23  2005/08/22 18:30:29  fplanque
 * minor
 *
 * Revision 1.22  2005/08/21 22:54:18  blueyed
 * Updated documentation for get_var().
 *
 * Revision 1.21  2005/08/21 22:44:32  blueyed
 * Removed dependencies on T_() and $Timer.
 *
 * Revision 1.20  2005/08/17 16:20:54  fplanque
 * rollback! I can't see a damn good reason to break existing code just because it happens that MySQL does not have a real boolean type!
 *
 * Revision 1.19  2005/07/22 13:54:45  blueyed
 * Better format for queries in print_error(); return value of get_var() is false, if nothing found (a DB cannot return false [boolean], but NULL?!)
 *
 * Revision 1.18  2005/07/15 18:11:16  fplanque
 * output debug context
 *
 * Revision 1.17  2005/07/12 23:05:36  blueyed
 * Added Timer class with categories 'main' and 'sql_queries' for now.
 *
 * Revision 1.16  2005/06/02 18:50:52  fplanque
 * no message
 *
 * Revision 1.15  2005/04/21 12:10:50  blueyed
 * minor
 *
 * Revision 1.14  2005/04/20 18:37:59  fplanque
 * Relocation of javascripts and CSS files to their proper places...
 *
 * Revision 1.13  2005/04/19 18:04:38  fplanque
 * implemented nested transactions for MySQL
 *
 * Revision 1.12  2005/03/08 20:32:07  fplanque
 * small fixes; slightly enhanced WEEK() handling
 *
 * Revision 1.11  2005/02/28 09:06:32  blueyed
 * removed constants for DB config (allows to override it from _config_TEST.php), introduced EVO_CONFIG_LOADED
 *
 * Revision 1.10  2005/02/23 22:32:44  blueyed
 * output xdebug function stack in case of error
 *
 * Revision 1.6  2005/02/08 00:43:15  blueyed
 * doc, whitespace, html, get_results() and get_row() now always return array, get_var() return false in case of error
 *
 * Revision 1.5  2004/11/09 00:25:11  blueyed
 * minor translation changes (+MySQL spelling :/)
 *
 * Revision 1.3  2004/10/28 11:11:09  fplanque
 * MySQL table options handling
 *
 * Revision 1.2  2004/10/14 16:28:41  fplanque
 * minor changes
 *
 * Revision 1.1  2004/10/13 22:46:32  fplanque
 * renamed [b2]evocore/*
 *
 * Revision 1.21  2004/10/12 10:27:18  fplanque
 * Edited code documentation.
 *
 */
?>

Generated by  Doxygen 1.6.0   Back to index