Skip to content

Active Record parallel queries

Lev-Zabudko edited this page Jan 28, 2014 · 24 revisions

Category:Core | Category:Core::Database | Category:Core::Community

This is a replacement of the system/database/DB_active_rec.php file, allowing you to use several CI Active Record threads at the same time without overlapping. See the relevant forum thread.

Sample code:

$this->db->set_identifier('my_first_query');
$this->db->from('blabla');
$this->db->where('blibli');

$this->db->set_identifier('my_second_query');
$this->db->from('tatata');
$this->db->where('tititi');

$this->db->set_identifier('my_first_query');
$first_query = $this->db->get();

$this->db->set_identifier('my_second_query');
$second_query = $this->db->get();

It's fully backward compatible, and you could use it also this way (adding two lines around the code to protect from other AR calls):

$this->db->set_identifier("my unique string that I won't use twice unless it's really clean");
$query = $this->db->getwhere('a_table', array('id' => $id));
$result = $query->first_row();
$this->db->set_identifier('default');

Some apps (like Rapyd in dataset.php around line 253) tweak some AR variables without using the public functions. You'll have to hack them if you want to use the new DB_active_rec.php. For example in Rapyd, the lines

// ...
$this->db->ar_limit = FALSE;
// ...
$this->db->ar_select = array('COUNT(*) AS totalrows');  
// ...
$this->db->ar_orderby = array();
// ...
$this->db->ar_order = FALSE;

becomes

// ...
$this->db->ar_limit['default'] = FALSE;
// ...
$this->db->ar_select['default'] = array('COUNT(*) AS totalrows');  
// ...
$this->db->ar_orderby['default'] = array();
// ...
$this->db->ar_order['default'] = FALSE;

Here is a piece of the code of this new file.

<?php
/**
 * Active Record Class
 *
 * This is the platform-independent base Active Record implementation class.
 *
 * Changes by Christophe Gragnic on 2007/06/14 are:
 *         vars now arrays of old vars
 *         added var $ar_identifier and its setter
 *         everywhere you had $this->ar_somevar, you now have
 *         $this->ar_somevar[$this->ar_identifier]
 *
 * Those changes allow us to use Active Record parallel queries.
 * The "identifier" is a simple string (default:'default').
 * Backward compatible, set_identifier('some_identifier') is optional.
 *
 * Sample code:
 * $this->db->set_identifier('my_first_query');
 * $this->db->from('blabla');
 * $this->db->where('blibli');
 *
 * $this->db->set_identifier('my_second_query');
 * $this->db->from('tatata');
 * $this->db->where('tititi');
 *
 * $this->db->set_identifier('my_first_query');
 * $first_query = $this->db->get();
 *
 * $this->db->set_identifier('my_second_query');
 * $second_query = $this->db->get();
 *
 * @package        CodeIgniter
 * @subpackage    Drivers
 * @category    Database
 * @author        Rick Ellis
 * @author        Christophe Gragnic (grahack) for the identifier stuff only
 * @link        http://www.codeigniter.com/user_guide/database/
 */
class CI_DB_active_record extends CI_DB_driver {
    
    var $ar_identifier = 'default';
    
    var $ar_select    = array('default' => array());
    var $ar_distinct  = array('default' => FALSE);
    var $ar_from      = array('default' => array());
    var $ar_join      = array('default' => array());
    var $ar_where     = array('default' => array());
    var $ar_like      = array('default' => array());
    var $ar_groupby   = array('default' => array());
    var $ar_having    = array('default' => array());
    var $ar_limit     = array('default' => FALSE);
    var $ar_offset    = array('default' => FALSE);
    var $ar_order     = array('default' => FALSE);
    var $ar_orderby   = array('default' => array());
    var $ar_set       = array('default' => array());

    /**
     * Set_identifier
     *
     * Chooses the channel to use
     *
     * @access    public
     * @param    string
     * @return    object
     */
    function set_identifier($identifier = 'default')
    {
        if ( ! is_string($identifier))
        {
            $identifier = 'default';
        }
        
        $this->ar_identifier = $identifier;
        
        // we have to init the values only if it is a new key
        // let's check on the first array
        if ( ! array_key_exists($identifier, $this->ar_select))
        {
            $this->ar_select        [$identifier] = array();
            $this->ar_distinct      [$identifier] = FALSE;
            $this->ar_from          [$identifier] = array();
            $this->ar_join          [$identifier] = array();
            $this->ar_where         [$identifier] = array();
            $this->ar_like          [$identifier] = array();
            $this->ar_groupby       [$identifier] = array();
            $this->ar_having        [$identifier] = array();
            $this->ar_limit         [$identifier] = FALSE;
            $this->ar_offset        [$identifier] = FALSE;
            $this->ar_order         [$identifier] = FALSE;
            $this->ar_orderby       [$identifier] = array();
            $this->ar_set           [$identifier] = array();
        }
        return $this;
    }

Download

File:DB_active_rec.zip

version 1.7.2 edited by Afro Radio Head Loved this added feature, so I had to update it File:DB_active_rec_v1.7.2.zip

updated by LevZabudko - modified this file for 2.1.3 version.

Here is the new source. Hope this helps smbdy:

don't forget to change (seems it's a Codeigniter bug in line 712 in 2.1.3) in _delete function in all db_driver files like database/driver/mysql/mysql_driver.php to:

$conditions .= implode("\n", $where);

or delete function will not work.

Main code:

<?php

if (!defined('BASEPATH'))
	exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team, Lev Zabudko
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html, AS IS
 * @link		http://codeigniter.com, https://www.facebook.com/LevZabudko
 * @since		Version 1.0u
 * @filesource
 */
// ------------------------------------------------------------------------

/**
 * Active Record Class
 *
 * This is the platform-independent base Active Record implementation class.
 *
 * @package		CodeIgniter
 * @subpackage	Drivers
 * @category	Database
 * @author		ExpressionEngine Dev Team
 * @link		http://codeigniter.com/user_guide/database/
 */
class CI_DB_active_record extends CI_DB_driver
{

	var $ar_identifier = 'default';
	var $ar_select = array('default' => array());
	var $ar_distinct = array('default' => FALSE);
	var $ar_from = array('default' => array());
	var $ar_join = array('default' => array());
	var $ar_where = array('default' => array());
	var $ar_like = array('default' => array());
	var $ar_groupby = array('default' => array());
	var $ar_having = array('default' => array());
	var $ar_keys = array('default' => array());
	var $ar_limit = array('default' => FALSE);
	var $ar_offset = array('default' => FALSE);
	var $ar_order = array('default' => FALSE);
	var $ar_orderby = array('default' => array());
	var $ar_set = array('default' => array());
	var $ar_wherein = array('default' => array());
	var $ar_aliased_tables = array('default' => array());
	var $ar_store_array = array('default' => array());
	var $ar_no_escape =  array('default' => array());
	// Active Record Caching variables
	var $ar_caching = FALSE;
	var $ar_cache_exists = array();
	var $ar_cache_select = array();
	var $ar_cache_from = array();
	var $ar_cache_join = array();
	var $ar_cache_where = array();
	var $ar_cache_like = array();
	var $ar_cache_groupby = array();
	var $ar_cache_having = array();
	var $ar_cache_orderby = array();
	var $ar_cache_set = array();
	var $ar_cache_no_escape = array();

	/**
	 * Set_identifier
	 *
	 * Chooses the channel to use
	 *
	 * @access    public
	 * @param    string
	 * @return    object
	 */
	function set_identifier($identifier = 'default')
	{
		if (!is_string($identifier))
		{
			$identifier = 'default';
		}

		$this->ar_identifier = $identifier;
		// we have to init the values only if it is a new key
		// let's check on the first array
		if (!array_key_exists($identifier, $this->ar_select))
		{
			$this->ar_select [$identifier] = array();
			$this->ar_distinct [$identifier] = FALSE;
			$this->ar_from [$identifier] = array();
			$this->ar_join [$identifier] = array();
			$this->ar_where [$identifier] = array();
			$this->ar_like [$identifier] = array();
			$this->ar_groupby [$identifier] = array();
			$this->ar_having [$identifier] = array();
			$this->ar_limit [$identifier] = FALSE;
			$this->ar_offset [$identifier] = FALSE;
			$this->ar_order [$identifier] = FALSE;
			$this->ar_orderby [$identifier] = array();
			$this->ar_set [$identifier] = array();
			$this->ar_keys [$identifier] = array();
			$this->ar_wherein [$identifier] = array();
			$this->ar_aliased_tables [$identifier] = array();
			$this->ar_store_array [$identifier] = array();
		}
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Select
	 *
	 * Generates the SELECT portion of the query
	 *
	 * @param	string
	 * @return	object
	 */
	public function select($select = '*', $escape = NULL)
	{
		if (is_string($select))
		{
			$select = explode(',', $select);
		}

		foreach ($select as $val)
		{
			$val = trim($val);

			if ($val != '')
			{
				$this->ar_select[$this->ar_identifier][] = $val;
				$this->ar_no_escape[$this->ar_identifier][] = $escape;

				if ($this->ar_caching === TRUE)
				{
					$this->ar_cache_select[] = $val;
					$this->ar_cache_exists[] = 'select';
					$this->ar_cache_no_escape[] = $escape;
				}
			}
		}
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Select Max
	 *
	 * Generates a SELECT MAX(field) portion of a query
	 *
	 * @param	string	the field
	 * @param	string	an alias
	 * @return	object
	 */
	public function select_max($select = '', $alias = '')
	{
		return $this->_max_min_avg_sum($select, $alias, 'MAX');
	}

	// --------------------------------------------------------------------

	/**
	 * Select Min
	 *
	 * Generates a SELECT MIN(field) portion of a query
	 *
	 * @param	string	the field
	 * @param	string	an alias
	 * @return	object
	 */
	public function select_min($select = '', $alias = '')
	{
		return $this->_max_min_avg_sum($select, $alias, 'MIN');
	}

	// --------------------------------------------------------------------

	/**
	 * Select Average
	 *
	 * Generates a SELECT AVG(field) portion of a query
	 *
	 * @param	string	the field
	 * @param	string	an alias
	 * @return	object
	 */
	public function select_avg($select = '', $alias = '')
	{
		return $this->_max_min_avg_sum($select, $alias, 'AVG');
	}

	// --------------------------------------------------------------------

	/**
	 * Select Sum
	 *
	 * Generates a SELECT SUM(field) portion of a query
	 *
	 * @param	string	the field
	 * @param	string	an alias
	 * @return	object
	 */
	public function select_sum($select = '', $alias = '')
	{
		return $this->_max_min_avg_sum($select, $alias, 'SUM');
	}

	// --------------------------------------------------------------------

	/**
	 * Processing Function for the four functions above:
	 *
	 * 	select_max()
	 * 	select_min()
	 * 	select_avg()
	 *  select_sum()
	 *
	 * @param	string	the field
	 * @param	string	an alias
	 * @return	object
	 */
	protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')
	{
		if (!is_string($select) OR $select == '')
		{
			$this->display_error('db_invalid_query');
		}

		$type = strtoupper($type);

		if (!in_array($type, array('MAX', 'MIN', 'AVG', 'SUM')))
		{
			show_error('Invalid function type: ' . $type);
		}

		if ($alias == '')
		{
			$alias = $this->_create_alias_from_table(trim($select));
		}

		$sql = $type . '(' . $this->_protect_identifiers(trim($select)) . ') AS ' . $alias;

		$this->ar_select[$this->ar_identifier][] = $sql;

		if ($this->ar_caching === TRUE)
		{
			$this->ar_cache_select[] = $sql;
			$this->ar_cache_exists[] = 'select';
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Determines the alias name based on the table
	 *
	 * @param	string
	 * @return	string
	 */
	protected function _create_alias_from_table($item)
	{
		if (strpos($item, '.') !== FALSE)
		{
			return end(explode('.', $item));
		}

		return $item;
	}

	// --------------------------------------------------------------------

	/**
	 * DISTINCT
	 *
	 * Sets a flag which tells the query string compiler to add DISTINCT
	 *
	 * @param	bool
	 * @return	object
	 */
	public function distinct($val = TRUE)
	{
		$this->ar_distinct[$this->ar_identifier] = (is_bool($val)) ? $val : TRUE;
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * From
	 *
	 * Generates the FROM portion of the query
	 *
	 * @param	mixed	can be a string or array
	 * @return	object
	 */
	public function from($from)
	{
		foreach ((array) $from as $val)
		{
			if (strpos($val, ',') !== FALSE)
			{
				foreach (explode(',', $val) as $v)
				{
					$v = trim($v);
					$this->_track_aliases($v);

					$this->ar_from[$this->ar_identifier][] = $this->_protect_identifiers($v, TRUE, NULL, FALSE);

					if ($this->ar_caching === TRUE)
					{
						$this->ar_cache_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE);
						$this->ar_cache_exists[] = 'from';
					}
				}
			} else
			{
				$val = trim($val);

				// Extract any aliases that might exist.  We use this information
				// in the _protect_identifiers to know whether to add a table prefix
				$this->_track_aliases($val);

				$this->ar_from[$this->ar_identifier][] = $this->_protect_identifiers($val, TRUE, NULL, FALSE);

				if ($this->ar_caching === TRUE)
				{
					$this->ar_cache_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE);
					$this->ar_cache_exists[] = 'from';
				}
			}
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Join
	 *
	 * Generates the JOIN portion of the query
	 *
	 * @param	string
	 * @param	string	the join condition
	 * @param	string	the type of join
	 * @return	object
	 */
	public function join($table, $cond, $type = '')
	{
		if ($type != '')
		{
			$type = strtoupper(trim($type));

			if (!in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER')))
			{
				$type = '';
			} else
			{
				$type .= ' ';
			}
		}

		// Extract any aliases that might exist.  We use this information
		// in the _protect_identifiers to know whether to add a table prefix
		$this->_track_aliases($table);

		// Strip apart the condition and protect the identifiers
		if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $cond, $match))
		{
			$match[1] = $this->_protect_identifiers($match[1]);
			$match[3] = $this->_protect_identifiers($match[3]);

			$cond = $match[1] . $match[2] . $match[3];
		}

		// Assemble the JOIN statement
		$join = $type . 'JOIN ' . $this->_protect_identifiers($table, TRUE, NULL, FALSE) . ' ON ' . $cond;

		$this->ar_join[$this->ar_identifier][] = $join;
		if ($this->ar_caching === TRUE)
		{
			$this->ar_cache_join[] = $join;
			$this->ar_cache_exists[] = 'join';
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Where
	 *
	 * Generates the WHERE portion of the query. Separates
	 * multiple calls with AND
	 *
	 * @param	mixed
	 * @param	mixed
	 * @return	object
	 */
	public function where($key, $value = NULL, $escape = TRUE)
	{
		return $this->_where($key, $value, 'AND ', $escape);
	}

	// --------------------------------------------------------------------

	/**
	 * OR Where
	 *
	 * Generates the WHERE portion of the query. Separates
	 * multiple calls with OR
	 *
	 * @param	mixed
	 * @param	mixed
	 * @return	object
	 */
	public function or_where($key, $value = NULL, $escape = TRUE)
	{
		return $this->_where($key, $value, 'OR ', $escape);
	}

	// --------------------------------------------------------------------

	/**
	 * Where
	 *
	 * Called by where() or or_where()
	 *
	 * @param	mixed
	 * @param	mixed
	 * @param	string
	 * @return	object
	 */
	protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL)
	{
		
		if (!is_array($key))
		{
			$key = array($key => $value);
		}

		// If the escape value was not set will will base it on the global setting
		if (!is_bool($escape))
		{
			$escape = $this->_protect_identifiers;
		}

		foreach ($key as $k => $v)
		{
			$prefix = (count($this->ar_where[$this->ar_identifier]) == 0 AND count($this->ar_cache_where) == 0) ? '' : $type;

			if (is_null($v) && !$this->_has_operator($k))
			{
				// value appears not to have been set, assign the test to IS NULL
				$k .= ' IS NULL';
			}

			if (!is_null($v))
			{
				if ($escape === TRUE)
				{
					$k = $this->_protect_identifiers($k, FALSE, $escape);

					$v = ' ' . $this->escape($v);
				}

				if (!$this->_has_operator($k))
				{
					$k .= ' = ';
				}
			} else
			{
				$k = $this->_protect_identifiers($k, FALSE, $escape);
			}
			
			$this->ar_where[$this->ar_identifier][] = $prefix . $k . $v;
			
			if ($this->ar_caching === TRUE)
			{
				$this->ar_cache_where[] = $prefix . $k . $v;
				$this->ar_cache_exists[] = 'where';
			}
		}
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Where_in
	 *
	 * Generates a WHERE field IN ('item', 'item') SQL query joined with
	 * AND if appropriate
	 *
	 * @param	string	The field to search
	 * @param	array	The values searched on
	 * @return	object
	 */
	public function where_in($key = NULL, $values = NULL)
	{
		return $this->_where_in($key, $values);
	}

	// --------------------------------------------------------------------

	/**
	 * Where_in_or
	 *
	 * Generates a WHERE field IN ('item', 'item') SQL query joined with
	 * OR if appropriate
	 *
	 * @param	string	The field to search
	 * @param	array	The values searched on
	 * @return	object
	 */
	public function or_where_in($key = NULL, $values = NULL)
	{
		return $this->_where_in($key, $values, FALSE, 'OR ');
	}

	// --------------------------------------------------------------------

	/**
	 * Where_not_in
	 *
	 * Generates a WHERE field NOT IN ('item', 'item') SQL query joined
	 * with AND if appropriate
	 *
	 * @param	string	The field to search
	 * @param	array	The values searched on
	 * @return	object
	 */
	public function where_not_in($key = NULL, $values = NULL)
	{
		return $this->_where_in($key, $values, TRUE);
	}

	// --------------------------------------------------------------------

	/**
	 * Where_not_in_or
	 *
	 * Generates a WHERE field NOT IN ('item', 'item') SQL query joined
	 * with OR if appropriate
	 *
	 * @param	string	The field to search
	 * @param	array	The values searched on
	 * @return	object
	 */
	public function or_where_not_in($key = NULL, $values = NULL)
	{
		return $this->_where_in($key, $values, TRUE, 'OR ');
	}

	// --------------------------------------------------------------------

	/**
	 * Where_in
	 *
	 * Called by where_in, where_in_or, where_not_in, where_not_in_or
	 *
	 * @param	string	The field to search
	 * @param	array	The values searched on
	 * @param	boolean	If the statement would be IN or NOT IN
	 * @param	string
	 * @return	object
	 */
	protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ')
	{
		if ($key === NULL OR $values === NULL)
		{
			return;
		}

		if (!is_array($values))
		{
			$values = array($values);
		}

		$not = ($not) ? ' NOT' : '';

		foreach ($values as $value)
		{
			$this->ar_wherein[$this->ar_identifier][] = $this->escape($value);
		}

		$prefix = (count($this->ar_where[$this->ar_identifier]) == 0) ? '' : $type;

		$where_in = $prefix . $this->_protect_identifiers($key) . $not . " IN (" . implode(", ", $this->ar_wherein[$this->ar_identifier]) . ") ";

		$this->ar_where[$this->ar_identifier][] = $where_in;
		if ($this->ar_caching === TRUE)
		{
			$this->ar_cache_where[] = $where_in;
			$this->ar_cache_exists[] = 'where';
		}

		// reset the array for multiple calls
		$this->ar_wherein[$this->ar_identifier] = array();
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Like
	 *
	 * Generates a %LIKE% portion of the query. Separates
	 * multiple calls with AND
	 *
	 * @param	mixed
	 * @param	mixed
	 * @return	object
	 */
	public function like($field, $match = '', $side = 'both')
	{
		return $this->_like($field, $match, 'AND ', $side);
	}

	// --------------------------------------------------------------------

	/**
	 * Not Like
	 *
	 * Generates a NOT LIKE portion of the query. Separates
	 * multiple calls with AND
	 *
	 * @param	mixed
	 * @param	mixed
	 * @return	object
	 */
	public function not_like($field, $match = '', $side = 'both')
	{
		return $this->_like($field, $match, 'AND ', $side, 'NOT');
	}

	// --------------------------------------------------------------------

	/**
	 * OR Like
	 *
	 * Generates a %LIKE% portion of the query. Separates
	 * multiple calls with OR
	 *
	 * @param	mixed
	 * @param	mixed
	 * @return	object
	 */
	public function or_like($field, $match = '', $side = 'both')
	{
		return $this->_like($field, $match, 'OR ', $side);
	}

	// --------------------------------------------------------------------

	/**
	 * OR Not Like
	 *
	 * Generates a NOT LIKE portion of the query. Separates
	 * multiple calls with OR
	 *
	 * @param	mixed
	 * @param	mixed
	 * @return	object
	 */
	public function or_not_like($field, $match = '', $side = 'both')
	{
		return $this->_like($field, $match, 'OR ', $side, 'NOT');
	}

	// --------------------------------------------------------------------

	/**
	 * Like
	 *
	 * Called by like() or orlike()
	 *
	 * @param	mixed
	 * @param	mixed
	 * @param	string
	 * @return	object
	 */
	protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '')
	{
		if (!is_array($field))
		{
			$field = array($field => $match);
		}

		foreach ($field as $k => $v)
		{
			$k = $this->_protect_identifiers($k);

			$prefix = (count($this->ar_like[$this->ar_identifier]) == 0) ? '' : $type;

			$v = $this->escape_like_str($v);

			if ($side == 'none')
			{
				$like_statement = $prefix . " $k $not LIKE '{$v}'";
			} elseif ($side == 'before')
			{
				$like_statement = $prefix . " $k $not LIKE '%{$v}'";
			} elseif ($side == 'after')
			{
				$like_statement = $prefix . " $k $not LIKE '{$v}%'";
			} else
			{
				$like_statement = $prefix . " $k $not LIKE '%{$v}%'";
			}

			// some platforms require an escape sequence definition for LIKE wildcards
			if ($this->_like_escape_str != '')
			{
				$like_statement = $like_statement . sprintf($this->_like_escape_str, $this->_like_escape_chr);
			}

			$this->ar_like[$this->ar_identifier][] = $like_statement;
			if ($this->ar_caching === TRUE)
			{
				$this->ar_cache_like[] = $like_statement;
				$this->ar_cache_exists[] = 'like';
			}
		}
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * GROUP BY
	 *
	 * @param	string
	 * @return	object
	 */
	public function group_by($by)
	{
		if (is_string($by))
		{
			$by = explode(',', $by);
		}

		foreach ($by as $val)
		{
			$val = trim($val);

			if ($val != '')
			{
				$this->ar_groupby[$this->ar_identifier][] = $this->_protect_identifiers($val);

				if ($this->ar_caching === TRUE)
				{
					$this->ar_cache_groupby[] = $this->_protect_identifiers($val);
					$this->ar_cache_exists[] = 'groupby';
				}
			}
		}
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Sets the HAVING value
	 *
	 * Separates multiple calls with AND
	 *
	 * @param	string
	 * @param	string
	 * @return	object
	 */
	public function having($key, $value = '', $escape = TRUE)
	{
		return $this->_having($key, $value, 'AND ', $escape);
	}

	// --------------------------------------------------------------------

	/**
	 * Sets the OR HAVING value
	 *
	 * Separates multiple calls with OR
	 *
	 * @param	string
	 * @param	string
	 * @return	object
	 */
	public function or_having($key, $value = '', $escape = TRUE)
	{
		return $this->_having($key, $value, 'OR ', $escape);
	}

	// --------------------------------------------------------------------

	/**
	 * Sets the HAVING values
	 *
	 * Called by having() or or_having()
	 *
	 * @param	string
	 * @param	string
	 * @return	object
	 */
	protected function _having($key, $value = '', $type = 'AND ', $escape = TRUE)
	{
		if (!is_array($key))
		{
			$key = array($key => $value);
		}

		foreach ($key as $k => $v)
		{
			$prefix = (count($this->ar_having[$this->ar_identifier]) == 0) ? '' : $type;

			if ($escape === TRUE)
			{
				$k = $this->_protect_identifiers($k);
			}

			if (!$this->_has_operator($k))
			{
				$k .= ' = ';
			}

			if ($v != '')
			{
				$v = ' ' . $this->escape($v);
			}

			$this->ar_having[$this->ar_identifier][] = $prefix . $k . $v;
			if ($this->ar_caching === TRUE)
			{
				$this->ar_cache_having[] = $prefix . $k . $v;
				$this->ar_cache_exists[] = 'having';
			}
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Sets the ORDER BY value
	 *
	 * @param	string
	 * @param	string	direction: asc or desc
	 * @return	object
	 */
	public function order_by($orderby, $direction = '')
	{
		if (strtolower($direction) == 'random')
		{
			$orderby = ''; // Random results want or don't need a field name
			$direction = $this->_random_keyword;
		} elseif (trim($direction) != '')
		{
			$direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE)) ? ' ' . $direction : ' ASC';
		}


		if (strpos($orderby, ',') !== FALSE)
		{
			$temp = array();
			foreach (explode(',', $orderby) as $part)
			{
				$part = trim($part);
				if (!in_array($part, $this->ar_aliased_tables))
				{
					$part = $this->_protect_identifiers(trim($part));
				}

				$temp[] = $part;
			}

			$orderby = implode(', ', $temp);
		} else if ($direction != $this->_random_keyword)
		{
			$orderby = $this->_protect_identifiers($orderby);
		}

		$orderby_statement = $orderby . $direction;

		$this->ar_orderby[$this->ar_identifier][] = $orderby_statement;
		if ($this->ar_caching === TRUE)
		{
			$this->ar_cache_orderby[] = $orderby_statement;
			$this->ar_cache_exists[] = 'orderby';
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Sets the LIMIT value
	 *
	 * @param	integer	the limit value
	 * @param	integer	the offset value
	 * @return	object
	 */
	public function limit($value, $offset = '')
	{
		$this->ar_limit[$this->ar_identifier] = (int) $value;

		if ($offset != '')
		{
			$this->ar_offset[$this->ar_identifier] = (int) $offset;
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Sets the OFFSET value
	 *
	 * @param	integer	the offset value
	 * @return	object
	 */
	public function offset($offset)
	{
		$this->ar_offset[$this->ar_identifier] = $offset;
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * The "set" function.  Allows key/value pairs to be set for inserting or updating
	 *
	 * @param	mixed
	 * @param	string
	 * @param	boolean
	 * @return	object
	 */
	public function set($key, $value = '', $escape = TRUE)
	{
		$key = $this->_object_to_array($key);

		if (!is_array($key))
		{
			$key = array($key => $value);
		}

		foreach ($key as $k => $v)
		{
			if ($escape === FALSE)
			{
				$this->ar_set[$this->ar_identifier][$this->_protect_identifiers($k)] = $v;
			} else
			{
				$this->ar_set[$this->ar_identifier][$this->_protect_identifiers($k, FALSE, TRUE)] = $this->escape($v);
			}
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Get
	 *
	 * Compiles the select statement based on the other functions called
	 * and runs the query
	 *
	 * @param	string	the table
	 * @param	string	the limit clause
	 * @param	string	the offset clause
	 * @return	object
	 */
	public function get($table = '', $limit = null, $offset = null)
	{
		if ($table != '')
		{
			$this->_track_aliases($table);
			$this->from($table);
		}

		if (!is_null($limit))
		{
			$this->limit($limit, $offset);
		}

		$sql = $this->_compile_select();

		$result = $this->query($sql);
		$this->_reset_select();
		return $result;
	}

	/**
	 * "Count All Results" query
	 *
	 * Generates a platform-specific query string that counts all records
	 * returned by an Active Record query.
	 *
	 * @param	string
	 * @return	string
	 */
	public function count_all_results($table = '')
	{
		if ($table != '')
		{
			$this->_track_aliases($table);
			$this->from($table);
		}

		$sql = $this->_compile_select($this->_count_string . $this->_protect_identifiers('numrows'));

		$query = $this->query($sql);
		$this->_reset_select();

		if ($query->num_rows() == 0)
		{
			return 0;
		}

		$row = $query->row();
		return (int) $row->numrows;
	}

	// --------------------------------------------------------------------

	/**
	 * Get_Where
	 *
	 * Allows the where clause, limit and offset to be added directly
	 *
	 * @param	string	the where clause
	 * @param	string	the limit clause
	 * @param	string	the offset clause
	 * @return	object
	 */
	public function get_where($table = '', $where = null, $limit = null, $offset = null)
	{
		if ($table != '')
		{
			$this->from($table);
		}

		if (!is_null($where))
		{
			$this->where($where);
		}

		if (!is_null($limit))
		{
			$this->limit($limit, $offset);
		}

		$sql = $this->_compile_select();

		$result = $this->query($sql);
		$this->_reset_select();
		return $result;
	}

	// --------------------------------------------------------------------

	/**
	 * Insert_Batch
	 *
	 * Compiles batch insert strings and runs the queries
	 *
	 * @param	string	the table to retrieve the results from
	 * @param	array	an associative array of insert values
	 * @return	object
	 */
	public function insert_batch($table = '', $set = NULL)
	{
		if (!is_null($set))
		{
			$this->set_insert_batch($set);
		}

		if (count($this->ar_set[$this->ar_identifier]) == 0)
		{
			if ($this->db_debug)
			{
				//No valid data array.  Folds in cases where keys and values did not match up
				return $this->display_error('db_must_use_set');
			}
			return FALSE;
		}

		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		}

		// Batch this baby
		for ($i = 0, $total = count($this->ar_set[$this->ar_identifier]); $i < $total; $i = $i + 100)
		{

			$sql = $this->_insert_batch($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_keys[$this->ar_identifier], array_slice($this->ar_set[$this->ar_identifier], $i, 100));

			//echo $sql;

			$this->query($sql);
		}

		$this->_reset_write();


		return TRUE;
	}

	// --------------------------------------------------------------------

	/**
	 * The "set_insert_batch" function.  Allows key/value pairs to be set for batch inserts
	 *
	 * @param	mixed
	 * @param	string
	 * @param	boolean
	 * @return	object
	 */
	public function set_insert_batch($key, $value = '', $escape = TRUE)
	{
		$key = $this->_object_to_array_batch($key);

		if (!is_array($key))
		{
			$key = array($key => $value);
		}

		$keys = array_keys(current($key));
		sort($keys);

		foreach ($key as $row)
		{
			if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
			{
				// batch function above returns an error on an empty array
				$this->ar_set[$this->ar_identifier][] = array();
				return;
			}

			ksort($row); // puts $row in the same order as our keys

			if ($escape === FALSE)
			{
				$this->ar_set[$this->ar_identifier][] = '(' . implode(',', $row) . ')';
			} else
			{
				$clean = array();

				foreach ($row as $value)
				{
					$clean[] = $this->escape($value);
				}

				$this->ar_set[$this->ar_identifier][] = '(' . implode(',', $clean) . ')';
			}
		}

		foreach ($keys as $k)
		{
			$this->ar_keys[$this->ar_identifier][] = $this->_protect_identifiers($k);
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Insert
	 *
	 * Compiles an insert string and runs the query
	 *
	 * @param	string	the table to insert data into
	 * @param	array	an associative array of insert values
	 * @return	object
	 */
	function insert($table = '', $set = NULL)
	{
		if (!is_null($set))
		{
			$this->set($set);
		}

		if (count($this->ar_set[$this->ar_identifier]) == 0)
		{
			if ($this->db_debug)
			{
				return $this->display_error('db_must_use_set');
			}
			return FALSE;
		}

		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		}

		$sql = $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set[$this->ar_identifier]), array_values($this->ar_set[$this->ar_identifier]));

		$this->_reset_write();
		return $this->query($sql);
	}

	// --------------------------------------------------------------------

	/**
	 * Replace
	 *
	 * Compiles an replace into string and runs the query
	 *
	 * @param	string	the table to replace data into
	 * @param	array	an associative array of insert values
	 * @return	object
	 */
	public function replace($table = '', $set = NULL)
	{
		if (!is_null($set))
		{
			$this->set($set);
		}

		if (count($this->ar_set[$this->ar_identifier]) == 0)
		{
			if ($this->db_debug)
			{
				return $this->display_error('db_must_use_set');
			}
			return FALSE;
		}

		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		}

		$sql = $this->_replace($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set[$this->ar_identifier]), array_values($this->ar_set[$this->ar_identifier]));

		$this->_reset_write();
		return $this->query($sql);
	}

	// --------------------------------------------------------------------

	/**
	 * Update
	 *
	 * Compiles an update string and runs the query
	 *
	 * @param	string	the table to retrieve the results from
	 * @param	array	an associative array of update values
	 * @param	mixed	the where clause
	 * @return	object
	 */
	public function update($table = '', $set = NULL, $where = NULL, $limit = NULL)
	{
		// Combine any cached components with the current statements
		$this->_merge_cache();

		if (!is_null($set))
		{
			$this->set($set);
		}

		if (count($this->ar_set[$this->ar_identifier]) == 0)
		{
			if ($this->db_debug)
			{
				return $this->display_error('db_must_use_set');
			}
			return FALSE;
		}

		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		}

		if ($where != NULL)
		{
			$this->where($where);
		}

		if ($limit != NULL)
		{
			$this->limit($limit);
		}

		$sql = $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_set[$this->ar_identifier], $this->ar_where[$this->ar_identifier], $this->ar_orderby[$this->ar_identifier], $this->ar_limit[$this->ar_identifier]);

		$this->_reset_write();
		return $this->query($sql);
	}

	// --------------------------------------------------------------------

	/**
	 * Update_Batch
	 *
	 * Compiles an update string and runs the query
	 *
	 * @param	string	the table to retrieve the results from
	 * @param	array	an associative array of update values
	 * @param	string	the where key
	 * @return	object
	 */
	public function update_batch($table = '', $set = NULL, $index = NULL)
	{
		// Combine any cached components with the current statements
		$this->_merge_cache();

		if (is_null($index))
		{
			if ($this->db_debug)
			{
				return $this->display_error('db_must_use_index');
			}

			return FALSE;
		}

		if (!is_null($set))
		{
			$this->set_update_batch($set, $index);
		}

		if (count($this->ar_set[$this->ar_identifier]) == 0)
		{
			if ($this->db_debug)
			{
				return $this->display_error('db_must_use_set');
			}

			return FALSE;
		}

		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		}

		// Batch this baby
		for ($i = 0, $total = count($this->ar_set[$this->ar_identifier]); $i < $total; $i = $i + 100)
		{
			$sql = $this->_update_batch($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->ar_set[$this->ar_identifier], $i, 100), $this->_protect_identifiers($index), $this->ar_where[$this->ar_identifier]);

			$this->query($sql);
		}

		$this->_reset_write();
	}

	// --------------------------------------------------------------------

	/**
	 * The "set_update_batch" function.  Allows key/value pairs to be set for batch updating
	 *
	 * @param	array
	 * @param	string
	 * @param	boolean
	 * @return	object
	 */
	public function set_update_batch($key, $index = '', $escape = TRUE)
	{
		$key = $this->_object_to_array_batch($key);

		if (!is_array($key))
		{
			// @todo error
		}

		foreach ($key as $k => $v)
		{
			$index_set = FALSE;
			$clean = array();

			foreach ($v as $k2 => $v2)
			{
				if ($k2 == $index)
				{
					$index_set = TRUE;
				} else
				{
					$not[] = $k . '-' . $v;
				}

				if ($escape === FALSE)
				{
					$clean[$this->_protect_identifiers($k2)] = $v2;
				} else
				{
					$clean[$this->_protect_identifiers($k2)] = $this->escape($v2);
				}
			}

			if ($index_set == FALSE)
			{
				return $this->display_error('db_batch_missing_index');
			}

			$this->ar_set[$this->ar_identifier][] = $clean;
		}

		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Empty Table
	 *
	 * Compiles a delete string and runs "DELETE FROM table"
	 *
	 * @param	string	the table to empty
	 * @return	object
	 */
	public function empty_table($table = '')
	{
		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		} else
		{
			$table = $this->_protect_identifiers($table, TRUE, NULL, FALSE);
		}

		$sql = $this->_delete($table);

		$this->_reset_write();

		return $this->query($sql);
	}

	// --------------------------------------------------------------------

	/**
	 * Truncate
	 *
	 * Compiles a truncate string and runs the query
	 * If the database does not support the truncate() command
	 * This function maps to "DELETE FROM table"
	 *
	 * @param	string	the table to truncate
	 * @return	object
	 */
	public function truncate($table = '')
	{
		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		} else
		{
			$table = $this->_protect_identifiers($table, TRUE, NULL, FALSE);
		}

		$sql = $this->_truncate($table);

		$this->_reset_write();

		return $this->query($sql);
	}

	// --------------------------------------------------------------------

	/**
	 * Delete
	 *
	 * Compiles a delete string and runs the query
	 *
	 * @param	mixed	the table(s) to delete from. String or array
	 * @param	mixed	the where clause
	 * @param	mixed	the limit clause
	 * @param	boolean
	 * @return	object
	 */
	public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)
	{
		// Combine any cached components with the current statements
		$this->_merge_cache();

		if ($table == '')
		{
			if (!isset($this->ar_from[$this->ar_identifier][0]))
			{
				if ($this->db_debug)
				{
					return $this->display_error('db_must_set_table');
				}
				return FALSE;
			}

			$table = $this->ar_from[$this->ar_identifier][0];
		} elseif (is_array($table))
		{
			foreach ($table as $single_table)
			{
				$this->delete($single_table, $where, $limit, FALSE);
			}

			$this->_reset_write();
			return;
		} else
		{
			$table = $this->_protect_identifiers($table, TRUE, NULL, FALSE);
		}

		if ($where != '')
		{
			$this->where($where);
		}

		if ($limit != NULL)
		{
			$this->limit($limit);
		}
		
		if (count($this->ar_where[$this->ar_identifier]) == 0 && count($this->ar_wherein[$this->ar_identifier]) == 0 && count($this->ar_like[$this->ar_identifier]) == 0)
		{
			if ($this->db_debug)
			{
				return $this->display_error('db_del_must_use_where');
			}

			return FALSE;
		}
		$sql = $this->_delete($table, $this->ar_where[$this->ar_identifier], $this->ar_like[$this->ar_identifier], $this->ar_limit[$this->ar_identifier]);

		if ($reset_data)
		{
			$this->_reset_write();
		}

		return $this->query($sql);
	}

	// --------------------------------------------------------------------

	/**
	 * DB Prefix
	 *
	 * Prepends a database prefix if one exists in configuration
	 *
	 * @param	string	the table
	 * @return	string
	 */
	public function dbprefix($table = '')
	{
		if ($table == '')
		{
			$this->display_error('db_table_name_required');
		}

		return $this->dbprefix . $table;
	}

	// --------------------------------------------------------------------

	/**
	 * Set DB Prefix
	 *
	 * Set's the DB Prefix to something new without needing to reconnect
	 *
	 * @param	string	the prefix
	 * @return	string
	 */
	public function set_dbprefix($prefix = '')
	{
		return $this->dbprefix = $prefix;
	}

	// --------------------------------------------------------------------

	/**
	 * Track Aliases
	 *
	 * Used to track SQL statements written with aliased tables.
	 *
	 * @param	string	The table to inspect
	 * @return	string
	 */
	protected function _track_aliases($table)
	{
		if (is_array($table))
		{
			foreach ($table as $t)
			{
				$this->_track_aliases($t);
			}
			return;
		}

		// Does the string contain a comma?  If so, we need to separate
		// the string into discreet statements
		if (strpos($table, ',') !== FALSE)
		{
			return $this->_track_aliases(explode(',', $table));
		}

		// if a table alias is used we can recognize it by a space
		if (strpos($table, " ") !== FALSE)
		{
			// if the alias is written with the AS keyword, remove it
			$table = preg_replace('/\s+AS\s+/i', ' ', $table);

			// Grab the alias
			$table = trim(strrchr($table, " "));

			// Store the alias, if it doesn't already exist
			if (!in_array($table, $this->ar_aliased_tables[$this->ar_identifier]))
			{
				$this->ar_aliased_tables[$this->ar_identifier][] = $table;
			}
		}
	}

	// --------------------------------------------------------------------

	/**
	 * Compile the SELECT statement
	 *
	 * Generates a query string based on which functions were used.
	 * Should not be called directly.  The get() function calls it.
	 *
	 * @return	string
	 */
	protected function _compile_select($select_override = FALSE)
	{
		// Combine any cached components with the current statements
		$this->_merge_cache();

		// ----------------------------------------------------------------
		// Write the "select" portion of the query

		if ($select_override !== FALSE)
		{
			$sql = $select_override;
		} else
		{
			$sql = (!$this->ar_distinct[$this->ar_identifier]) ? 'SELECT ' : 'SELECT DISTINCT ';

			if (count($this->ar_select[$this->ar_identifier]) == 0)
			{
				$sql .= '*';
			} else
			{
				// Cycle through the "select" portion of the query and prep each column name.
				// The reason we protect identifiers here rather then in the select() function
				// is because until the user calls the from() function we don't know if there are aliases
				foreach ($this->ar_select[$this->ar_identifier] as $key => $val)
				{
					$no_escape = isset($this->ar_no_escape[$this->ar_identifier][$key]) ? $this->ar_no_escape[$this->ar_identifier][$key] : NULL;
					$this->ar_select[$this->ar_identifier][$key] = $this->_protect_identifiers($val, FALSE, $no_escape);
				}

				$sql .= implode(', ', $this->ar_select[$this->ar_identifier]);
			}
		}

		// ----------------------------------------------------------------
		// Write the "FROM" portion of the query

		if (count($this->ar_from[$this->ar_identifier]) > 0)
		{
			$sql .= "\nFROM ";

			$sql .= $this->_from_tables($this->ar_from[$this->ar_identifier]);
		}

		// ----------------------------------------------------------------
		// Write the "JOIN" portion of the query

		if (count($this->ar_join[$this->ar_identifier]) > 0)
		{
			$sql .= "\n";

			$sql .= implode("\n", $this->ar_join[$this->ar_identifier]);
		}

		// ----------------------------------------------------------------
		// Write the "WHERE" portion of the query

		if (count($this->ar_where[$this->ar_identifier]) > 0 OR count($this->ar_like[$this->ar_identifier]) > 0)
		{
			$sql .= "\nWHERE ";
		}

		$sql .= implode("\n", $this->ar_where[$this->ar_identifier]);

		// ----------------------------------------------------------------
		// Write the "LIKE" portion of the query

		if (count($this->ar_like[$this->ar_identifier]) > 0)
		{
			if (count($this->ar_where[$this->ar_identifier]) > 0)
			{
				$sql .= "\nAND ";
			}

			$sql .= implode("\n", $this->ar_like[$this->ar_identifier]);
		}

		// ----------------------------------------------------------------
		// Write the "GROUP BY" portion of the query

		if (count($this->ar_groupby[$this->ar_identifier]) > 0)
		{
			$sql .= "\nGROUP BY ";

			$sql .= implode(', ', $this->ar_groupby[$this->ar_identifier]);
		}

		// ----------------------------------------------------------------
		// Write the "HAVING" portion of the query

		if (count($this->ar_having[$this->ar_identifier]) > 0)
		{
			$sql .= "\nHAVING ";
			$sql .= implode("\n", $this->ar_having[$this->ar_identifier]);
		}

		// ----------------------------------------------------------------
		// Write the "ORDER BY" portion of the query

		if (count($this->ar_orderby[$this->ar_identifier]) > 0)
		{
			$sql .= "\nORDER BY ";
			$sql .= implode(', ', $this->ar_orderby[$this->ar_identifier]);

			if ($this->ar_order[$this->ar_identifier] !== FALSE)
			{
				$sql .= ($this->ar_order[$this->ar_identifier] == 'desc') ? ' DESC' : ' ASC';
			}
		}

		// ----------------------------------------------------------------
		// Write the "LIMIT" portion of the query

		if (is_numeric($this->ar_limit[$this->ar_identifier]))
		{
			$sql .= "\n";
			$sql = $this->_limit($sql, $this->ar_limit[$this->ar_identifier], $this->ar_offset[$this->ar_identifier]);
		}

		return $sql;
	}

	// --------------------------------------------------------------------

	/**
	 * Object to Array
	 *
	 * Takes an object as input and converts the class variables to array key/vals
	 *
	 * @param	object
	 * @return	array
	 */
	public function _object_to_array($object)
	{
		if (!is_object($object))
		{
			return $object;
		}

		$array = array();
		foreach (get_object_vars($object) as $key => $val)
		{
			// There are some built in keys we need to ignore for this conversion
			if (!is_object($val) && !is_array($val) && $key != '_parent_name')
			{
				$array[$key] = $val;
			}
		}

		return $array;
	}

	// --------------------------------------------------------------------

	/**
	 * Object to Array
	 *
	 * Takes an object as input and converts the class variables to array key/vals
	 *
	 * @param	object
	 * @return	array
	 */
	public function _object_to_array_batch($object)
	{
		if (!is_object($object))
		{
			return $object;
		}

		$array = array();
		$out = get_object_vars($object);
		$fields = array_keys($out);

		foreach ($fields as $val)
		{
			// There are some built in keys we need to ignore for this conversion
			if ($val != '_parent_name')
			{

				$i = 0;
				foreach ($out[$val] as $data)
				{
					$array[$i][$val] = $data;
					$i++;
				}
			}
		}

		return $array;
	}

	// --------------------------------------------------------------------

	/**
	 * Start Cache
	 *
	 * Starts AR caching
	 *
	 * @return	void
	 */
	public function start_cache()
	{
		$this->ar_caching = TRUE;
	}

	// --------------------------------------------------------------------

	/**
	 * Stop Cache
	 *
	 * Stops AR caching
	 *
	 * @return	void
	 */
	public function stop_cache()
	{
		$this->ar_caching = FALSE;
	}

	// --------------------------------------------------------------------

	/**
	 * Flush Cache
	 *
	 * Empties the AR cache
	 *
	 * @access	public
	 * @return	void
	 */
	public function flush_cache()
	{
		$this->_reset_run(array(
			'ar_cache_select' => array(),
			'ar_cache_from' => array(),
			'ar_cache_join' => array(),
			'ar_cache_where' => array(),
			'ar_cache_like' => array(),
			'ar_cache_groupby' => array(),
			'ar_cache_having' => array(),
			'ar_cache_orderby' => array(),
			'ar_cache_set' => array(),
			'ar_cache_exists' => array(),
			'ar_cache_no_escape' => array()
		));
	}

	// --------------------------------------------------------------------

	/**
	 * Merge Cache
	 *
	 * When called, this function merges any cached AR arrays with
	 * locally called ones.
	 *
	 * @return	void
	 */
	protected function _merge_cache()
	{
		if (count($this->ar_cache_exists) == 0)
		{
			return;
		}

		foreach ($this->ar_cache_exists as $val)
		{
			$ar_variable = 'ar_' . $val;
			$ar_cache_var = 'ar_cache_' . $val;

			if (count($this->$ar_cache_var) == 0)
			{
				continue;
			}

			$this->$ar_variable = array_unique(array_merge($this->$ar_cache_var, $this->$ar_variable));
		}

		// If we are "protecting identifiers" we need to examine the "from"
		// portion of the query to determine if there are any aliases
		if ($this->_protect_identifiers === TRUE AND count($this->ar_cache_from) > 0)
		{
			$this->_track_aliases($this->ar_from[$this->ar_identifier]);
		}

		$this->ar_no_escape[$this->ar_identifier] = $this->ar_cache_no_escape;
	}

	// --------------------------------------------------------------------

	/**
	 * Resets the active record values.  Called by the get() function
	 *
	 * @param	array	An array of fields to reset
	 * @return	void
	 */
	protected function _reset_run($ar_reset_items)
	{
		foreach ($ar_reset_items as $item => $default_value)
		{
			if (!in_array($item, $this->ar_store_array))
			{
				$this->$item = $default_value;
			}
		}
	}

	// --------------------------------------------------------------------

	/**
	 * Resets the active record values.  Called by the get() function
	 *
	 * @return	void
	 */
	protected function _reset_select()
	{
		$ar_reset_items = array(
			'ar_select' => array($this->ar_identifier => array()),
			'ar_from' => array($this->ar_identifier => array()),
			'ar_join' => array($this->ar_identifier => array()),
			'ar_where' => array($this->ar_identifier => array()),
			'ar_like' => array($this->ar_identifier => array()),
			'ar_groupby' => array($this->ar_identifier => array()),
			'ar_having' => array($this->ar_identifier => array()),
			'ar_orderby' => array($this->ar_identifier => array()),
			'ar_wherein' => array($this->ar_identifier => array()),
			'ar_aliased_tables' => array($this->ar_identifier => array()),
			'ar_no_escape' => array($this->ar_identifier => array()),
			'ar_distinct' => array($this->ar_identifier => FALSE),
			'ar_limit' => array($this->ar_identifier => FALSE),
			'ar_offset' => array($this->ar_identifier => FALSE),
			'ar_order' => array($this->ar_identifier => FALSE),
		);

		foreach ($this->ar_select as $ident => $val)
		{
			if ($ident != $this->ar_identifier)
			{
				$ar_reset_items['ar_select'][$ident] = $this->ar_select[$ident];
				$ar_reset_items['ar_from'][$ident] = $this->ar_from[$ident];
				$ar_reset_items['ar_join'][$ident] = $this->ar_join[$ident];
				$ar_reset_items['ar_where'][$ident] = $this->ar_where[$ident];
				$ar_reset_items['ar_like'][$ident] = $this->ar_like[$ident];
				$ar_reset_items['ar_groupby'][$ident] = $this->ar_groupby[$ident];
				$ar_reset_items['ar_having'][$ident] = $this->ar_having[$ident];
				$ar_reset_items['ar_orderby'][$ident] = $this->ar_orderby[$ident];
				$ar_reset_items['ar_wherein'][$ident] = $this->ar_wherein[$ident];
				$ar_reset_items['ar_aliased_tables'][$ident] = $this->ar_aliased_tables[$ident];
				$ar_reset_items['ar_no_escape'][$ident] = $this->ar_no_escape[$ident];
				$ar_reset_items['ar_distinct'][$ident] = $this->ar_distinct[$ident];
				$ar_reset_items['ar_limit'][$ident] = $this->ar_limit[$ident];
				$ar_reset_items['ar_offset'][$ident] = $this->ar_offset[$ident];
				$ar_reset_items['ar_order'][$ident] = $this->ar_order[$ident];
			}
			
		}

		$this->_reset_run($ar_reset_items);
	}

	// --------------------------------------------------------------------

	/**
	 * Resets the active record "write" values.
	 *
	 * Called by the insert() update() insert_batch() update_batch() and delete() functions
	 *
	 * @return	void
	 */
	protected function _reset_write()
	{
		$ar_reset_items = array(
			'ar_set' => array($this->ar_identifier => array()),
			'ar_from' => array($this->ar_identifier => array()),
			'ar_where' => array($this->ar_identifier => array()),
			'ar_like' => array($this->ar_identifier => array()),
			'ar_orderby' => array($this->ar_identifier => array()),
			'ar_keys' => array($this->ar_identifier => array()),
			'ar_limit' => array($this->ar_identifier => FALSE),
			'ar_order' => array($this->ar_identifier => FALSE)
		);

		$this->_reset_run($ar_reset_items);
	}
}

/* End of file DB_active_rec.php */
/* Location: ./system/database/DB_active_rec.php */
Clone this wiki locally