Merge branch 'develop' of https://github.com/EllisLab/CodeIgniter into develop
diff --git a/DCO.txt b/DCO.txt
new file mode 100644
index 0000000..a404c0d
--- /dev/null
+++ b/DCO.txt
@@ -0,0 +1,25 @@
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(1)	The contribution was created in whole or in part by me and I
+	have the right to submit it under the open source license
+	indicated in the file; or
+
+(2)	The contribution is based upon previous work that, to the best
+	of my knowledge, is covered under an appropriate open source
+	license and I have the right under that license to submit that
+	work with modifications, whether created in whole or in part
+	by me, under the same open source license (unless I am
+	permitted to submit under a different license), as indicated
+	in the file; or
+
+(3)	The contribution was provided directly to me by some other
+	person who certified (1), (2) or (3) and I have not modified
+	it.
+
+(4)	I understand and agree that this project and the contribution
+	are public and that a record of the contribution (including all
+	personal information I submit with it, including my sign-off) is
+	maintained indefinitely and may be redistributed consistent with
+	this project or the open source license(s) involved.
diff --git a/application/config/database.php b/application/config/database.php
index cb6ebad..bb0d87b 100644
--- a/application/config/database.php
+++ b/application/config/database.php
@@ -42,8 +42,9 @@
 |	['username'] The username used to connect to the database
 |	['password'] The password used to connect to the database
 |	['database'] The name of the database you want to connect to
-|	['dbdriver'] The database type. e.g.: mysql.  Currently supported:
-|				 cubrid, interbase, mssql, mysql, mysqli, oci8,
+|	['dbdriver'] The database driver. e.g.: mysqli.
+			Currently supported:
+|				 cubrid, ibase, mssql, mysql, mysqli, oci8,
 |				 odbc, pdo, postgre, sqlite, sqlite3, sqlsrv
 |	['dbprefix'] You can add an optional prefix, which will be added
 |				 to the table name when using the  Query Builder class
diff --git a/index.php b/index.php
index ad98013..aa1df44 100644
--- a/index.php
+++ b/index.php
@@ -52,20 +52,22 @@
  * By default development will show errors but testing and live will hide them.
  */
 
-if (defined('ENVIRONMENT'))
+switch (ENVIRONMENT)
 {
-	switch (ENVIRONMENT)
-	{
-		case 'development':
-			error_reporting(-1);
-		break;
-		case 'testing':
-		case 'production':
-			error_reporting(0);
-		break;
-		default:
-			exit('The application environment is not set correctly.');
-	}
+	case 'development':
+		error_reporting(-1);
+		ini_set('display_errors', 1);
+	break;
+
+	case 'testing':
+	case 'production':
+		error_reporting(E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_STRICT);
+		ini_set('display_errors', 0);
+	break;
+
+	default:
+		header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+		exit('The application environment is not set correctly.');
 }
 
 /*
@@ -268,4 +270,4 @@
 require_once BASEPATH.'core/CodeIgniter.php';
 
 /* End of file index.php */
-/* Location: ./index.php */
\ No newline at end of file
+/* Location: ./index.php */
diff --git a/readme.rst b/readme.rst
index 7b718fe..b211ad7 100644
--- a/readme.rst
+++ b/readme.rst
@@ -115,6 +115,34 @@
 cannot merge the request. Using the Git-Flow branching model you can create
 new branches for both of these features and send two requests.
 
+Signing
+=======
+You must sign your work, certifying that you either wrote the work or
+otherwise have the right to pass it on to an open source project. git makes
+this trivial as you merely have to use `--signoff` on your commits to your
+CodeIgniter fork.
+
+::
+
+	git commit --signoff
+
+or simply::
+
+	git commit -s
+
+This will sign your commits with the information setup in your git config, e.g.
+
+	Signed-off-by: John Q Public <john.public@example.com>
+
+If you are using Tower there is a "Sign-Off" checkbox in the commit window. You 
+could even alias git commit to use the -s flag so you don’t have to think about 
+it.
+
+By signing your work in this manner, you certify to a "Developer's Certificate 
+or Origin". The current version of this certificate is in the `DCO.txt` file
+in the root of this repository.
+
+
 ************
 How-to Guide
 ************
diff --git a/system/core/Common.php b/system/core/Common.php
index c309d41..7e93ed4 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -526,7 +526,8 @@
 
 		// Should we display the error? We'll get the current error_reporting
 		// level and add its bits with the severity bits to find out.
-		if (($severity & error_reporting()) === $severity)
+		// And respect display_errors
+		if (($severity & error_reporting()) === $severity && (bool) ini_get('display_errors') === TRUE)
 		{
 			$_error->show_php_error($severity, $message, $filepath, $line);
 		}
diff --git a/system/core/Config.php b/system/core/Config.php
index 4b4e5a7..2f6a9e0 100644
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -43,7 +43,7 @@
 	 *
 	 * @var array
 	 */
-	public $config =	array();
+	public $config = array();
 
 	/**
 	 * List of all loaded config files
@@ -102,13 +102,13 @@
 	{
 		$file = ($file === '') ? 'config' : str_replace('.php', '', $file);
 		$found = $loaded = FALSE;
+		
+		$check_locations = defined('ENVIRONMENT')
+			? array(ENVIRONMENT.'/'.$file, $file)
+			: array($file);
 
 		foreach ($this->_config_paths as $path)
 		{
-			$check_locations = defined('ENVIRONMENT')
-				? array(ENVIRONMENT.'/'.$file, $file)
-				: array($file);
-
 			foreach ($check_locations as $location)
 			{
 				$file_path = $path.'config/'.$location.'.php';
@@ -172,7 +172,7 @@
 			{
 				return FALSE;
 			}
-			show_error('The configuration file '.$file.'.php'.' does not exist.');
+			show_error('The configuration file '.$file.'.php does not exist.');
 		}
 
 		return TRUE;
@@ -271,7 +271,7 @@
 	 */
 	public function base_url($uri = '')
 	{
-		return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/');
+		return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/');
 	}
 
 	// -------------------------------------------------------------
diff --git a/system/database/DB.php b/system/database/DB.php
index 00d14b4..d751325 100644
--- a/system/database/DB.php
+++ b/system/database/DB.php
@@ -29,8 +29,8 @@
  * Initialize the database
  *
  * @category	Database
- * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/database/
+ * @author	EllisLab Dev Team
+ * @link	http://codeigniter.com/user_guide/database/
  * @param 	string
  * @param 	bool	Determines if query builder should be used or not
  */
@@ -47,6 +47,21 @@
 		}
 
 		include($file_path);
+		//make packages contain database config files
+		foreach(get_instance()->load->get_package_paths() as $path)
+		{
+			if ($path !== APPPATH)
+			{
+				if (file_exists ($file_path = $path.'config/'.ENVIRONMENT.'/database.php'))
+				{
+					include ($file_path);
+				}
+				elseif ( file_exists ($file_path = $path.'config/database.php'))
+				{
+					include ($file_path);
+				}
+			}
+		}
 
 		if ( ! isset($db) OR count($db) === 0)
 		{
@@ -144,7 +159,10 @@
 	// Load the DB driver
 	$driver_file = BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php';
 
-	if ( ! file_exists($driver_file)) show_error('Invalid DB driver');
+	if ( ! file_exists($driver_file))
+	{
+		show_error('Invalid DB driver');
+	}
 
 	require_once($driver_file);
 
@@ -152,6 +170,19 @@
 	$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
 	$DB = new $driver($params);
 
+	// Check for a subdriver
+	if ( ! empty($DB->subdriver))
+	{
+		$driver_file = BASEPATH.'database/drivers/'.$DB->dbdriver.'/subdrivers/'.$DB->dbdriver.'_'.$DB->subdriver.'_driver.php';
+
+		if (file_exists($driver_file))
+		{
+			require_once($driver_file);
+			$driver = 'CI_DB_'.$DB->dbdriver.'_'.$DB->subdriver.'_driver';
+			$DB = new $driver($params);
+		}
+	}
+
 	if ($DB->autoinit === TRUE)
 	{
 		$DB->initialize();
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index 334bdbd..e9efc89 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -45,7 +45,8 @@
 	public $password;
 	public $hostname;
 	public $database;
-	public $dbdriver		= 'mysql';
+	public $dbdriver		= 'mysqli';
+	public $subdriver;
 	public $dbprefix		= '';
 	public $char_set		= 'utf8';
 	public $dbcollat		= 'utf8_general_ci';
@@ -77,6 +78,19 @@
 	protected $_protect_identifiers		= TRUE;
 	protected $_reserved_identifiers	= array('*'); // Identifiers that should NOT be escaped
 
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_count_string = 'SELECT COUNT(*) AS ';
+
+	/**
+	 * Constructor
+	 *
+	 * @param	array
+	 * @return	void
+	 */
 	public function __construct($params)
 	{
 		if (is_array($params))
@@ -855,7 +869,7 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Fetch MySQL Field Names
+	 * Fetch Field Names
 	 *
 	 * @param	string	the table name
 	 * @return	array
@@ -957,7 +971,7 @@
 	 */
 	public function escape_identifiers($item)
 	{
-		if ($this->_escape_char === '')
+		if ($this->_escape_char === '' OR empty($item))
 		{
 			return $item;
 		}
@@ -970,8 +984,8 @@
 
 			return $item;
 		}
-		// Avoid breaking functions inside queries
-		elseif (strpos($item, '(') !== FALSE)
+		// Avoid breaking functions and literal values inside queries
+		elseif (ctype_digit($item) OR $item[0] === "'" OR strpos($item, '(') !== FALSE)
 		{
 			return $item;
 		}
@@ -1030,6 +1044,23 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Insert statement
+	 *
+	 * Generates a platform-specific insert string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return	string
+	 */
+	protected function _insert($table, $keys, $values)
+	{
+		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Generate an update string
 	 *
 	 * @param	string	the table upon which the query will be performed
@@ -1082,6 +1113,41 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause
+	 * @param	array	the limit clause
+	 * @param	array	the like clause
+	 * @return	string
+	 */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
+			.$where
+			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '')
+			.($limit ? ' LIMIT '.$limit : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Tests whether the string has an SQL operator
 	 *
 	 * @param	string
@@ -1171,7 +1237,6 @@
 		return $this->cache_on = FALSE;
 	}
 
-
 	// --------------------------------------------------------------------
 
 	/**
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index 3982885..479b7f2 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -350,18 +350,18 @@
 		is_bool($escape) OR $escape = $this->_protect_identifiers;
 
 		// Split multiple conditions
-		if ($escape === TRUE && preg_match_all('/\sAND\s|\sOR\s/i', $cond, $m, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
+		if ($escape === TRUE && preg_match_all('/\sAND\s|\sOR\s/i', $cond, $m, PREG_OFFSET_CAPTURE))
 		{
 			$newcond = '';
 			$m[0][] = array('', strlen($cond));
 
 			for ($i = 0, $c = count($m[0]), $s = 0;
 				$i < $c;
-				$s += $m[0][$i][1] + strlen($m[0][$i][0]), $i++)
+				$s = $m[0][$i][1] + strlen($m[0][$i][0]), $i++)
 			{
-				$temp = substr($cond, $s, $m[0][$i][1]);
+				$temp = substr($cond, $s, ($m[0][$i][1] - $s));
 
-				$newcond .= preg_match('/([\[\w\.-]+)([\W\s]+)(.+)/i', $temp, $match)
+				$newcond .= preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $temp, $match)
 						? $this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3])
 						: $temp;
 
@@ -371,7 +371,7 @@
 			$cond = ' ON '.$newcond;
 		}
 		// Split apart the condition and protect the identifiers
-		elseif ($escape === TRUE && preg_match('/([\[\w\.-]+)([\W\s]+)(.+)/i', $cond, $match))
+		elseif ($escape === TRUE && preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $cond, $match))
 		{
 			$cond = ' ON '.$this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3]);
 		}
@@ -1435,23 +1435,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Insert statement
-	 *
-	 * Generates a platform-specific insert string from the supplied data
-	 *
-	 * @param	string	the table name
-	 * @param	array	the insert keys
-	 * @param	array	the insert values
-	 * @return	string
-	 */
-	protected function _insert($table, $keys, $values)
-	{
-		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Validate Insert
 	 *
 	 * This method is used by both insert() and get_compiled_insert() to
@@ -1631,41 +1614,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Update statement
-	 *
-	 * Generates a platform-specific update string from the supplied data
-	 *
-	 * @param	string	the table name
-	 * @param	array	the update data
-	 * @param	array	the where clause
-	 * @param	array	the orderby clause
-	 * @param	array	the limit clause
-	 * @param	array	the like clause
-	 * @return	string
-	 */
-	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
-	{
-		foreach ($values as $key => $val)
-		{
-			$valstr[] = $key.' = '.$val;
-		}
-
-		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
-
-		if ( ! empty($like))
-		{
-			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
-		}
-
-		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
-			.$where
-			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '')
-			.($limit ? ' LIMIT '.$limit : '');
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Validate Update
 	 *
 	 * This method is used by both update() and get_compiled_update() to
diff --git a/system/database/DB_result.php b/system/database/DB_result.php
index 991f6ba..d44df6c 100644
--- a/system/database/DB_result.php
+++ b/system/database/DB_result.php
@@ -38,32 +38,74 @@
  */
 class CI_DB_result {
 
-	public $conn_id				= NULL;
-	public $result_id			= NULL;
+	public $conn_id;
+	public $result_id;
 	public $result_array			= array();
 	public $result_object			= array();
 	public $custom_result_object		= array();
 	public $current_row			= 0;
-	public $num_rows			= 0;
-	public $row_data			= NULL;
+	public $num_rows;
+	public $row_data;
 
+	/**
+	 * Constructor
+	 *
+	 * @param	object
+	 * @return	void
+	 */
 	public function __construct(&$driver_object)
 	{
 		$this->conn_id = $driver_object->conn_id;
 		$this->result_id = $driver_object->result_id;
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
-	 * Query result.  Acts as a wrapper function for the following functions.
+	 * Number of rows in the result set
 	 *
-	 * @param	string	can be "object" or "array"
-	 * @return	object
+	 * @return	int
+	 */
+	public function num_rows()
+	{
+		if (is_int($this->num_rows))
+		{
+			return $this->num_rows;
+		}
+		elseif (count($this->result_array) > 0)
+		{
+			return $this->num_rows = count($this->result_array);
+		}
+		elseif (count($this->result_object) > 0)
+		{
+			return $this->num_rows = count($this->result_object);
+		}
+
+		return $this->num_rows = count($this->result_array());
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Query result. Acts as a wrapper function for the following functions.
+	 *
+	 * @param	string	'object', 'array' or a custom class name
+	 * @return	array
 	 */
 	public function result($type = 'object')
 	{
-		if ($type === 'array') return $this->result_array();
-		elseif ($type === 'object') return $this->result_object();
-		else return $this->custom_result_object($type);
+		if ($type === 'array')
+		{
+			return $this->result_array();
+		}
+		elseif ($type === 'object')
+		{
+			return $this->result_object();
+		}
+		else
+		{
+			return $this->custom_result_object($type);
+		}
 	}
 
 	// --------------------------------------------------------------------
@@ -76,33 +118,50 @@
 	 */
 	public function custom_result_object($class_name)
 	{
-		if (array_key_exists($class_name, $this->custom_result_object))
+		if (isset($this->custom_result_object[$class_name]))
 		{
 			return $this->custom_result_object[$class_name];
 		}
-
-		if ($this->result_id === FALSE OR $this->num_rows() === 0)
+		elseif ( ! $this->result_id OR $this->num_rows === 0)
 		{
 			return array();
 		}
 
-		// add the data to the object
-		$this->_data_seek(0);
-		$result_object = array();
-
-		while ($row = $this->_fetch_object())
+		// Don't fetch the result set again if we already have it
+		$_data = NULL;
+		if (($c = count($this->result_array)) > 0)
 		{
-			$object = new $class_name();
-			foreach ($row as $key => $value)
-			{
-				$object->$key = $value;
-			}
-
-			$result_object[] = $object;
+			$_data = 'result_array';
+		}
+		elseif (($c = count($this->result_object)) > 0)
+		{
+			$_data = 'result_object';
 		}
 
-		// return the array
-		return $this->custom_result_object[$class_name] = $result_object;
+		if ($_data !== NULL)
+		{
+			for ($i = 0; $i < $c; $i++)
+			{
+				$this->custom_result_object[$class_name][$i] = new $class_name();
+
+				foreach ($this->{$_data}[$i] as $key => $value)
+				{
+					$this->custom_result_object[$class_name][$i]->$key = $value;
+				}
+			}
+
+			return $this->custom_result_object[$class_name];
+		}
+
+		$this->_data_seek(0);
+		$this->custom_result_object[$class_name] = array();
+
+		while ($row = $this->_fetch_object($class_name))
+		{
+			$this->custom_result_object[$class_name][] = $row;
+		}
+
+		return $this->custom_result_object[$class_name];
 	}
 
 	// --------------------------------------------------------------------
@@ -119,14 +178,24 @@
 			return $this->result_object;
 		}
 
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE OR $this->num_rows() === 0)
+		// In the event that query caching is on, the result_id variable
+		// will not be a valid resource so we'll simply return an empty
+		// array.
+		if ( ! $this->result_id OR $this->num_rows === 0)
 		{
 			return array();
 		}
 
+		if (($c = count($this->result_array)) > 0)
+		{
+			for ($i = 0; $i < $c; $i++)
+			{
+				$this->result_object[$i] = (object) $this->result_array[$i];
+			}
+
+			return $this->result_object;
+		}
+
 		$this->_data_seek(0);
 		while ($row = $this->_fetch_object())
 		{
@@ -139,7 +208,7 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Query result.  "array" version.
+	 * Query result. "array" version.
 	 *
 	 * @return	array
 	 */
@@ -150,14 +219,24 @@
 			return $this->result_array;
 		}
 
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE OR $this->num_rows() === 0)
+		// In the event that query caching is on, the result_id variable
+		// will not be a valid resource so we'll simply return an empty
+		// array.
+		if ( ! $this->result_id OR $this->num_rows === 0)
 		{
 			return array();
 		}
 
+		if (($c = count($this->result_object)) > 0)
+		{
+			for ($i = 0; $i < $c; $i++)
+			{
+				$this->result_array[$i] = (array) $this->result_object[$i];
+			}
+
+			return $this->result_array;
+		}
+
 		$this->_data_seek(0);
 		while ($row = $this->_fetch_assoc())
 		{
@@ -239,18 +318,19 @@
 	 */
 	public function custom_row_object($n, $type)
 	{
-		$result = $this->custom_result_object($type);
-		if (count($result) === 0)
+		isset($this->custom_result_object[$type]) OR $this->custom_result_object($type);
+
+		if (count($this->custom_result_object[$type]) === 0)
 		{
 			return NULL;
 		}
 
-		if ($n !== $this->current_row && isset($result[$n]))
+		if ($n !== $this->current_row && isset($this->custom_result_object[$type][$n]))
 		{
 			$this->current_row = $n;
 		}
 
-		return $result[$this->current_row];
+		return $this->custom_result_object[$type][$this->current_row];
 	}
 
 	// --------------------------------------------------------------------
@@ -375,11 +455,21 @@
 	/**
 	 * Returns an unbuffered row and move pointer to next row
 	 *
+	 * @param	string	'array', 'object' or a custom class name
 	 * @return	mixed	either a result object or array
 	 */
 	public function unbuffered_row($type = 'object')
 	{
-		return ($type !== 'array') ? $this->_fetch_object() : $this->_fetch_assoc();
+		if ($type === 'array')
+		{
+			return $this->_fetch_assoc();
+		}
+		elseif ($type === 'object')
+		{
+			return $this->_fetch_object();
+		}
+
+		return $this->_fetch_object($type);
 	}
 
 	// --------------------------------------------------------------------
@@ -393,7 +483,6 @@
 	 * operational due to the unavailability of the database resource IDs with
 	 * cached results.
 	 */
-	public function num_rows() { return $this->num_rows; }
 	public function num_fields() { return 0; }
 	public function list_fields() { return array(); }
 	public function field_data() { return array(); }
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index 7496ee4..a3d0287 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = '';
 	protected $_like_escape_chr = '';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	// CUBRID-specific properties
diff --git a/system/database/drivers/cubrid/cubrid_result.php b/system/database/drivers/cubrid/cubrid_result.php
index 3eb9f7e..4a06a2d 100644
--- a/system/database/drivers/cubrid/cubrid_result.php
+++ b/system/database/drivers/cubrid/cubrid_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		Esen Sagynov
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	2.1
  */
 class CI_DB_cubrid_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @cubrid_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @cubrid_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -157,11 +160,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return cubrid_fetch_object($this->result_id);
+		return cubrid_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/interbase/interbase_driver.php b/system/database/drivers/ibase/ibase_driver.php
similarity index 95%
rename from system/database/drivers/interbase/interbase_driver.php
rename to system/database/drivers/ibase/ibase_driver.php
index 38d3096..c902767 100644
--- a/system/database/drivers/interbase/interbase_driver.php
+++ b/system/database/drivers/ibase/ibase_driver.php
@@ -38,9 +38,9 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
  */
-class CI_DB_interbase_driver extends CI_DB {
+class CI_DB_ibase_driver extends CI_DB {
 
-	public $dbdriver = 'interbase';
+	public $dbdriver = 'ibase';
 
 	// The character used to escape with
 	protected $_escape_char = '"';
@@ -49,13 +49,7 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string	= 'SELECT COUNT(*) AS ';
-	protected $_random_keyword	= ' Random()'; // database specific random keyword
+	protected $_random_keyword = ' Random()'; // database specific random keyword
 
 	// Keeps track of the resource for the current transaction
 	protected $trans;
@@ -443,5 +437,5 @@
 
 }
 
-/* End of file interbase_driver.php */
-/* Location: ./system/database/drivers/interbase/interbase_driver.php */
\ No newline at end of file
+/* End of file ibase_driver.php */
+/* Location: ./system/database/drivers/ibase/ibase_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/interbase/interbase_forge.php b/system/database/drivers/ibase/ibase_forge.php
similarity index 96%
rename from system/database/drivers/interbase/interbase_forge.php
rename to system/database/drivers/ibase/ibase_forge.php
index d1b006e..da75eb9 100644
--- a/system/database/drivers/interbase/interbase_forge.php
+++ b/system/database/drivers/ibase/ibase_forge.php
@@ -32,7 +32,7 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
  */
-class CI_DB_interbase_forge extends CI_DB_forge {
+class CI_DB_ibase_forge extends CI_DB_forge {
 
 	protected $_drop_table	= 'DROP TABLE %s';
 
@@ -190,5 +190,5 @@
 
 }
 
-/* End of file interbase_forge.php */
-/* Location: ./system/database/drivers/interbase/interbase_forge.php */
\ No newline at end of file
+/* End of file ibase_forge.php */
+/* Location: ./system/database/drivers/ibase/ibase_forge.php */
\ No newline at end of file
diff --git a/system/database/drivers/ibase/ibase_result.php b/system/database/drivers/ibase/ibase_result.php
new file mode 100644
index 0000000..95e5571
--- /dev/null
+++ b/system/database/drivers/ibase/ibase_result.php
@@ -0,0 +1,155 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+/**
+ * Interbase/Firebird Result Class
+ *
+ * This class extends the parent result class: CI_DB_result
+ *
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ * @since	3.0
+ */
+class CI_DB_ibase_result extends CI_DB_result {
+
+	/**
+	 * Number of fields in the result set
+	 *
+	 * @return	int
+	 */
+	public function num_fields()
+	{
+		return @ibase_num_fields($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch Field Names
+	 *
+	 * Generates an array of column names
+	 *
+	 * @return	array
+	 */
+	public function list_fields()
+	{
+		$field_names = array();
+		for ($i = 0, $num_fields = $this->num_fields(); $i < $num_fields; $i++)
+		{
+			$info = ibase_field_info($this->result_id, $i);
+			$field_names[] = $info['name'];
+		}
+
+		return $field_names;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data
+	 *
+	 * Generates an array of objects containing field meta-data
+	 *
+	 * @return	array
+	 */
+	public function field_data()
+	{
+		$retval = array();
+		for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
+		{
+			$info = ibase_field_info($this->result_id, $i);
+
+			$retval[$i]			= new stdClass();
+			$retval[$i]->name		= $info['name'];
+			$retval[$i]->type		= $info['type'];
+			$retval[$i]->max_length		= $info['length'];
+			$retval[$i]->primary_key	= 0;
+			$retval[$i]->default		= '';
+		}
+
+		return $retval;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Free the result
+	 *
+	 * @return	void
+	 */
+	public function free_result()
+	{
+		@ibase_free_result($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Result - associative array
+	 *
+	 * Returns the result set as an array
+	 *
+	 * @return	array
+	 */
+	protected function _fetch_assoc()
+	{
+		return @ibase_fetch_assoc($this->result_id, IBASE_FETCH_BLOBS);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Result - object
+	 *
+	 * Returns the result set as an object
+	 *
+	 * @param	string
+	 * @return	object
+	 */
+	protected function _fetch_object($class_name = 'stdClass')
+	{
+		$row = @ibase_fetch_object($this->result_id, IBASE_FETCH_BLOBS);
+
+		if ($class_name === 'stdClass' OR ! $row)
+		{
+			return $row;
+		}
+
+		$class_name = new $class_name();
+		foreach ($row as $key => $value)
+		{
+			$class_name->$key = $value;
+		}
+
+		return $class_name;
+	}
+
+}
+
+/* End of file ibase_result.php */
+/* Location: ./system/database/drivers/ibase/ibase_result.php */
\ No newline at end of file
diff --git a/system/database/drivers/interbase/interbase_utility.php b/system/database/drivers/ibase/ibase_utility.php
similarity index 90%
rename from system/database/drivers/interbase/interbase_utility.php
rename to system/database/drivers/ibase/ibase_utility.php
index 1642118..d0e84a7 100644
--- a/system/database/drivers/interbase/interbase_utility.php
+++ b/system/database/drivers/ibase/ibase_utility.php
@@ -32,7 +32,7 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
  */
-class CI_DB_interbase_utility extends CI_DB_utility {
+class CI_DB_ibase_utility extends CI_DB_utility {
 
 	protected $_list_databases	= FALSE;
 
@@ -58,5 +58,5 @@
 
 }
 
-/* End of file interbase_utility.php */
-/* Location: ./system/database/drivers/interbase/interbase_utility.php */
\ No newline at end of file
+/* End of file ibase_utility.php */
+/* Location: ./system/database/drivers/ibase/ibase_utility.php */
\ No newline at end of file
diff --git a/system/database/drivers/interbase/index.html b/system/database/drivers/ibase/index.html
similarity index 100%
rename from system/database/drivers/interbase/index.html
rename to system/database/drivers/ibase/index.html
diff --git a/system/database/drivers/interbase/interbase_result.php b/system/database/drivers/interbase/interbase_result.php
deleted file mode 100644
index 5ddb6fa..0000000
--- a/system/database/drivers/interbase/interbase_result.php
+++ /dev/null
@@ -1,260 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.2.4 or newer
- *
- * NOTICE OF LICENSE
- *
- * Licensed under the Open Software License version 3.0
- *
- * This source file is subject to the Open Software License (OSL 3.0) that is
- * bundled with this package in the files license.txt / license.rst.  It is
- * also available through the world wide web at this URL:
- * http://opensource.org/licenses/OSL-3.0
- * If you did not receive a copy of the license and are unable to obtain it
- * through the world wide web, please send an email to
- * licensing@ellislab.com so we can send you a copy immediately.
- *
- * @package		CodeIgniter
- * @author		EllisLab Dev Team
- * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
- * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
- * @link		http://codeigniter.com
- * @since		Version 3.0
- * @filesource
- */
-
-/**
- * Interbase/Firebird Result Class
- *
- * This class extends the parent result class: CI_DB_result
- *
- * @category	Database
- * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/database/
- */
-class CI_DB_interbase_result extends CI_DB_result {
-
-	public $num_rows;
-
-	/**
-	 * Number of rows in the result set
-	 *
-	 * @return	int
-	 */
-	public function num_rows()
-	{
-		if (is_int($this->num_rows))
-		{
-			return $this->num_rows;
-		}
-
-		// Get the results so that you can get an accurate rowcount
-		$this->result();
-
-		return $this->num_rows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Number of fields in the result set
-	 *
-	 * @return	int
-	 */
-	public function num_fields()
-	{
-		return @ibase_num_fields($this->result_id);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Fetch Field Names
-	 *
-	 * Generates an array of column names
-	 *
-	 * @return	array
-	 */
-	public function list_fields()
-	{
-		$field_names = array();
-		for ($i = 0, $num_fields = $this->num_fields(); $i < $num_fields; $i++)
-		{
-			$info = ibase_field_info($this->result_id, $i);
-			$field_names[] = $info['name'];
-		}
-
-		return $field_names;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Field data
-	 *
-	 * Generates an array of objects containing field meta-data
-	 *
-	 * @return	array
-	 */
-	public function field_data()
-	{
-		$retval = array();
-		for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
-		{
-			$info = ibase_field_info($this->result_id, $i);
-
-			$retval[$i]			= new stdClass();
-			$retval[$i]->name		= $info['name'];
-			$retval[$i]->type		= $info['type'];
-			$retval[$i]->max_length		= $info['length'];
-			$retval[$i]->primary_key	= 0;
-			$retval[$i]->default		= '';
-		}
-
-		return $retval;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Free the result
-	 *
-	 * @return	void
-	 */
-	public function free_result()
-	{
-		@ibase_free_result($this->result_id);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Result - associative array
-	 *
-	 * Returns the result set as an array
-	 *
-	 * @return	array
-	 */
-	protected function _fetch_assoc()
-	{
-		if (($row = @ibase_fetch_assoc($this->result_id, IBASE_FETCH_BLOBS)) !== FALSE)
-		{
-			//Increment row count
-			$this->num_rows++;
-		}
-
-		return $row;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Result - object
-	 *
-	 * Returns the result set as an object
-	 *
-	 * @return	object
-	 */
-	protected function _fetch_object()
-	{
-		if (($row = @ibase_fetch_object($this->result_id, IBASE_FETCH_BLOBS)) !== FALSE)
-		{
-			//Increment row count
-			$this->num_rows++;
-		}
-
-		return $row;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result.  "object" version.
-	 *
-	 * @return	object
-	 */
-	public function result_object()
-	{
-		if (count($this->result_object) === $this->num_rows)
-		{
-			return $this->result_object;
-		}
-
-		// Convert result array to object so that
-		// We don't have to get the result again
-		if (($c = count($this->result_array)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_object[$i] = (object) $this->result_array[$i];
-			}
-
-			return $this->result_object;
-		}
-
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE)
-		{
-			return array();
-		}
-
-		$this->num_rows = 0;
-		while ($row = $this->_fetch_object())
-		{
-			$this->result_object[] = $row;
-		}
-
-		return $this->result_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result.  "array" version.
-	 *
-	 * @return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) === $this->num_rows)
-		{
-			return $this->result_array;
-		}
-
-		// Since the object and array are really similar, just case
-		// the result object to an array  if need be
-		if (($c = count($this->result_object)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_array[$i] = (array) $this->result_object[$i];
-			}
-
-			return $this->result_array;
-		}
-
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE)
-		{
-			return array();
-		}
-
-		$this->num_rows = 0;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->result_array[] = $row;
-		}
-
-		return $this->result_array;
-	}
-
-}
-
-/* End of file interbase_result.php */
-/* Location: ./system/database/drivers/interbase/interbase_result.php */
\ No newline at end of file
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index 3026b36..1714704 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() methods.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' NEWID()';
 
 	// MSSQL-specific properties
diff --git a/system/database/drivers/mssql/mssql_result.php b/system/database/drivers/mssql/mssql_result.php
index 5929306..aeede3f 100644
--- a/system/database/drivers/mssql/mssql_result.php
+++ b/system/database/drivers/mssql/mssql_result.php
@@ -26,13 +26,14 @@
  */
 
 /**
- * MS SQL Result Class
+ * MSSQL Result Class
  *
  * This class extends the parent result class: CI_DB_result
  *
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_mssql_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @mssql_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @mssql_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -158,11 +161,25 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return mssql_fetch_object($this->result_id);
+		$row = @mssql_fetch_object($this->result_id);
+
+		if ($class_name === 'stdClass' OR ! $row)
+		{
+			return $row;
+		}
+
+		$class_name = new $class_name();
+		foreach ($row as $key => $value)
+		{
+			$class_name->$key = $value;
+		}
+
+		return $class_name;
 	}
 
 }
diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php
index d11f015..29db904 100644
--- a/system/database/drivers/mysql/mysql_driver.php
+++ b/system/database/drivers/mysql/mysql_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = '';
 	protected $_like_escape_chr = '\\';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	/**
diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php
index 14d6d07..7fbb654 100644
--- a/system/database/drivers/mysql/mysql_result.php
+++ b/system/database/drivers/mysql/mysql_result.php
@@ -33,17 +33,35 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.0
  */
 class CI_DB_mysql_result extends CI_DB_result {
 
 	/**
+	 * Constructor
+	 *
+	 * @param	object
+	 * @return	void
+	 */
+	public function __construct(&$driver_object)
+	{
+		parent::__construct($driver_object);
+
+		// Required, due to mysql_data_seek() causing nightmares
+		// with empty result sets
+		$this->num_rows = @mysql_num_rows($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Number of rows in the result set
 	 *
 	 * @return	int
 	 */
 	public function num_rows()
 	{
-		return @mysql_num_rows($this->result_id);
+		return $this->num_rows;
 	}
 
 	// --------------------------------------------------------------------
@@ -132,7 +150,9 @@
 	 */
 	protected function _data_seek($n = 0)
 	{
-		return mysql_data_seek($this->result_id, $n);
+		return $this->num_rows
+			? @mysql_data_seek($this->result_id, $n)
+			: FALSE;
 	}
 
 	// --------------------------------------------------------------------
@@ -156,11 +176,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return mysql_fetch_object($this->result_id);
+		return mysql_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php
index d1581bf..be61aab 100644
--- a/system/database/drivers/mysqli/mysqli_driver.php
+++ b/system/database/drivers/mysqli/mysqli_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = '';
 	protected $_like_escape_chr = '\\';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	/**
diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php
index 9b4d494..c1ec4da 100644
--- a/system/database/drivers/mysqli/mysqli_result.php
+++ b/system/database/drivers/mysqli/mysqli_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_mysqli_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return $this->result_id->num_rows;
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = $this->result_id->num_rows;
 	}
 
 	// --------------------------------------------------------------------
@@ -157,11 +160,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return $this->result_id->fetch_object();
+		return $this->result_id->fetch_object($class_name);
 	}
 
 }
diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php
index 6fb6c81..a2b600e 100644
--- a/system/database/drivers/oci8/oci8_result.php
+++ b/system/database/drivers/oci8/oci8_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.4.1
  */
 class CI_DB_oci8_result extends CI_DB_result {
 
@@ -41,14 +42,16 @@
 	public $limit_used;
 	public $commit_mode;
 
-	/* Overwriting the parent here, so we have a way to know if it's
-	 * already called or not:
+	/**
+	 * Constructor
+	 *
+	 * @param	object
+	 * @return	void
 	 */
-	public $num_rows;
-
 	public function __construct(&$driver_object)
 	{
 		parent::__construct($driver_object);
+
 		$this->stmt_id = $driver_object->stmt_id;
 		$this->curs_id = $driver_object->curs_id;
 		$this->limit_used = $driver_object->limit_used;
@@ -56,33 +59,6 @@
 		$driver_object->stmt_id = FALSE;
 	}
 
-	/**
-	 * Number of rows in the result set.
-	 *
-	 * Oracle doesn't have a graceful way to return the number of rows
-	 * so we have to use what amounts to a hack.
-	 *
-	 * @return	int
-	 */
-	public function num_rows()
-	{
-		if ( ! is_int($this->num_rows))
-		{
-			if (count($this->result_array) > 0)
-			{
-				return $this->num_rows = count($this->result_array);
-			}
-			elseif (count($this->result_object) > 0)
-			{
-				return $this->num_rows = count($this->result_object);
-			}
-
-			return $this->num_rows = count($this->result_array());
-		}
-
-		return $this->num_rows;
-	}
-
 	// --------------------------------------------------------------------
 
 	/**
@@ -191,450 +167,27 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		$id = ($this->curs_id) ? $this->curs_id : $this->stmt_id;
-		return oci_fetch_object($id);
-	}
+		$row = ($this->curs_id)
+			? oci_fetch_object($this->curs_id)
+			: oci_fetch_object($this->stmt_id);
 
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Array version.
-	 *
-	 * @return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) > 0)
+		if ($class_name === 'stdClass' OR ! $row)
 		{
-			return $this->result_array;
-		}
-		elseif (count($this->result_object) > 0)
-		{
-			for ($i = 0, $c = count($this->result_object); $i < $c; $i++)
-			{
-				$this->result_array[$i] = (array) $this->result_object[$i];
-			}
-
-			return $this->result_array;
-		}
-		elseif (is_array($this->row_data))
-		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_array;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
+			return $row;
 		}
 
-		$row = NULL;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->row_data[$row_index++] = $row;
-		}
-
-		return $this->result_array = $this->row_data;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. "object" version.
-	 *
-	 * @return	array
-	 */
-	public function result_object()
-	{
-		if (count($this->result_object) > 0)
-		{
-			return $this->result_object;
-		}
-		elseif (count($this->result_array) > 0)
-		{
-			for ($i = 0, $c = count($this->result_array); $i < $c; $i++)
-			{
-				$this->result_object[] = (object) $this->result_array[$i];
-			}
-
-			return $this->result_object;
-		}
-		elseif (is_array($this->row_data))
-		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_object;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-				for ($i = 0; $i < $row_index; $i++)
-				{
-					$this->result_object[$i] = (object) $this->row_data[$i];
-				}
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
-		}
-
-		$row = NULL;
-		while ($row = $this->_fetch_object())
-		{
-			$this->row_data[$row_index] = (array) $row;
-			$this->result_object[$row_index++] = $row;
-		}
-
-		return $this->result_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Custom object version.
-	 *
-	 * @param	string	class name used to instantiate rows to
-	 * @return	array
-	 */
-	public function custom_result_object($class_name)
-	{
-		if (isset($this->custom_result_object[$class_name]))
-		{
-			return $this->custom_result_object[$class_name];
-		}
-
-		if ( ! class_exists($class_name) OR $this->result_id === FALSE OR $this->num_rows() === 0)
-		{
-			return array();
-		}
-
-		/* Even if we didn't have result_array or result_object
-		 * set prior to custom_result_object() being called,
-		 * num_rows() has already done so.
-		 * Pass by reference, as we don't know how
-		 * large it might be and we don't want 1000 row
-		 * sets being copied.
-		 */
-		if (count($this->result_array) > 0)
-		{
-			$data = &$this->result_array;
-		}
-		elseif (count($this->result_object) > 0)
-		{
-			$data = &$this->result_object;
-		}
-
-		$this->custom_result_object[$class_name] = array();
-		for ($i = 0, $c = count($data); $i < $c; $i++)
-		{
-			$this->custom_result_object[$class_name][$i] = new $class_name();
-			foreach ($data[$i] as $key => $value)
-			{
-				$this->custom_result_object[$class_name][$i]->$key = $value;
-			}
-		}
-
-		return $this->custom_result_object[$class_name];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result.
-	 *
-	 * Acts as a wrapper for row_object(), row_array()
-	 * and custom_row_object(). Also used by first_row(), next_row()
-	 * and previous_row().
-	 *
-	 * @param	int	row index
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function row($n = 0, $type = 'object')
-	{
-		if ($type === 'object')
-		{
-			return $this->row_object($n);
-		}
-		elseif ($type === 'array')
-		{
-			return $this->row_array($n);
-		}
-
-		return $this->custom_row_object($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Array version.
-	 *
-	 * @param	int	row index
-	 * @return	array
-	 */
-	public function row_array($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		/* If row_data is initialized, it means that we've already tried
-		 * (at least) to fetch some data, so ... check if we already have
-		 * this row.
-		*/
-		if (is_array($this->row_data))
-		{
-			/* If we already have row_data[$n] - return it.
-			 *
-			 * If we enter the elseif, there's a number of reasons to
-			 * return an empty array:
-			 *
-			 *	- count($this->row_data) === 0 means there are no results
-			 *	- num_rows being set, result_array and/or result_object
-			 *	  having count() > 0 means that we've already fetched all
-			 *	  data and $n is greater than our highest row index available
-			 *	- $n < $this->current_row means that if such row existed,
-			 *	  we would've already returned it, therefore $n is an
-			 *	  invalid index
-			 */
-			if (isset($this->row_data[$n])) // We already have this row
-			{
-				$this->current_row = $n;
-				return $this->row_data[$n];
-			}
-			elseif (count($this->row_data) === 0 OR is_int($this->num_rows)
-				OR count($this->result_array) > 0 OR count($this->result_object) > 0
-				OR $n < $this->current_row)
-			{
-				// No such row exists
-				return NULL;
-			}
-
-			// Get the next row index that would actually need to be fetched
-			$current_row = ($this->current_row < count($this->row_data)) ? count($this->row_data) : $this->current_row + 1;
-		}
-		else
-		{
-			$current_row = $this->current_row = 0;
-			$this->row_data = array();
-		}
-
-		/* Fetch more data, if available
-		 *
-		 * NOTE: Operator precedence is important here, if you change
-		 *	 'AND' with '&&' - it WILL BREAK the results, as
-		 *	 $row will be assigned the scalar value of both
-		 *	 expressions!
-		 */
-		while ($row = $this->_fetch_assoc() AND $current_row <= $n)
-		{
-			$this->row_data[$current_row++] = $row;
-		}
-
-		// This would mean that there's no (more) data to fetch
-		if ( ! is_array($this->row_data) OR ! isset($this->row_data[$n]))
-		{
-			// Cache what we already have
-			if (is_array($this->row_data))
-			{
-				$this->num_rows = count($this->row_data);
-				/* Usually, row_data could have less elements than result_array,
-				 * but at this point - they should be exactly the same.
-				 */
-				$this->result_array = $this->row_data;
-			}
-			else
-			{
-				$this->num_rows = 0;
-			}
-
-			return NULL;
-		}
-
-		$this->current_row = $n;
-		return $this->row_data[$n];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Object version.
-	 *
-	 * @param	int	row index
-	 * @return	mixed	object if row found; empty array if not
-	 */
-	public function row_object($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-		/* Logic here is exactly the same as in row_array,
-		 * except we have to cast row_data[$n] to an object.
-		 *
-		 * If we already have result_object though - we can
-		 * directly return from it.
-		 */
-		if (isset($this->result_object[$n]))
-		{
-			$this->current_row = $n;
-			// Set this, if not already done.
-			if ( ! is_int($this->num_rows))
-			{
-				$this->num_rows = count($this->result_object);
-			}
-
-			return $this->result_object[$n];
-		}
-
-		$row = $this->row_array($n);
-		// Cast only if the row exists
-		if (count($row) > 0)
-		{
-			$this->current_row = $n;
-			return (object) $row;
-		}
-
-		return NULL;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Custom object version.
-	 *
-	 * @param	int	row index
-	 * @param	string	custom class name
-	 * @return	mixed	custom object if row found; empty array otherwise
-	 */
-	public function custom_row_object($n = 0, $class_name)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		if (array_key_exists($class_name, $this->custom_result_object))
-		{
-			/* We already have a the whole result set with this class_name,
-			 * return the specified row if it exists, and an empty array if
-			 * it doesn't.
-			 */
-			if (isset($this->custom_result_object[$class_name][$n]))
-			{
-				$this->current_row = $n;
-				return $this->custom_result_object[$class_name][$n];
-			}
-			else
-			{
-				return NULL;
-			}
-		}
-		elseif ( ! class_exists($class_name)) // No such class exists
-		{
-			return NULL;
-		}
-
-		$row = $this->row_array($n);
-		// A non-array would mean that the row doesn't exist
-		if ( ! is_array($row))
-		{
-			return NULL;
-		}
-
-		// Convert to the desired class and return
-		$row_object = new $class_name();
+		$class_name = new $class_name();
 		foreach ($row as $key => $value)
 		{
-			$row_object->$key = $value;
+			$class_name->$key = $value;
 		}
 
-		$this->current_row = $n;
-		return $row_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* First row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function first_row($type = 'object')
-	{
-		return $this->row(0, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Last row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function last_row($type = 'object')
-	{
-		$result = &$this->result($type);
-		if ( ! isset($this->num_rows))
-		{
-			$this->num_rows = count($result);
-		}
-		$this->current_row = $this->num_rows - 1;
-		return $result[$this->current_row];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Next row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function next_row($type = 'object')
-	{
-		if (is_array($this->row_data))
-		{
-			$count = count($this->row_data);
-			if ($this->current_row > $count OR ($this->current_row === 0 && $count === 0))
-			{
-				$n = $count;
-			}
-			else
-			{
-				$n = $this->current_row + 1;
-			}
-		}
-		else
-		{
-			$n = 0;
-		}
-
-		return $this->row($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Previous row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function previous_row($type = 'object')
-	{
-		$n = ($this->current_row !== 0) ? $this->current_row - 1 : 0;
-		return $this->row($n, $type);
+		return $class_name;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index bd57592..8f0a474 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = " {escape '%s'} ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword;
 
 	public function __construct($params)
diff --git a/system/database/drivers/odbc/odbc_forge.php b/system/database/drivers/odbc/odbc_forge.php
index b074c58..d17b046 100644
--- a/system/database/drivers/odbc/odbc_forge.php
+++ b/system/database/drivers/odbc/odbc_forge.php
@@ -34,7 +34,6 @@
  */
 class CI_DB_odbc_forge extends CI_DB_forge {
 
-	protected $_drop_database	= 'DROP DATABASE %s';
 	protected $_drop_table		= 'DROP TABLE %s';
 
 	/**
diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php
index 227fe4f..48dc48d 100644
--- a/system/database/drivers/odbc/odbc_result.php
+++ b/system/database/drivers/odbc/odbc_result.php
@@ -33,11 +33,10 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_odbc_result extends CI_DB_result {
 
-	public $num_rows;
-
 	/**
 	 * Number of rows in the result set
 	 *
@@ -49,16 +48,26 @@
 		{
 			return $this->num_rows;
 		}
-
-		// Work-around for ODBC subdrivers that don't support num_rows()
-		if (($this->num_rows = @odbc_num_rows($this->result_id)) === -1)
+		elseif (($this->num_rows = @odbc_num_rows($this->result_id)) !== -1)
 		{
-			$this->num_rows = count($this->result_array());
+			return $this->num_rows;
 		}
 
-		return $this->num_rows;
+		// Work-around for ODBC subdrivers that don't support num_rows()
+		if (count($this->result_array) > 0)
+		{
+			return $this->num_rows = count($this->result_array);
+		}
+		elseif (count($this->result_object) > 0)
+		{
+			return $this->num_rows = count($this->result_object);
+		}
+
+		return $this->num_rows = count($this->result_array());
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * Number of fields in the result set
 	 *
@@ -146,9 +155,7 @@
 	 */
 	protected function _fetch_assoc()
 	{
-		return function_exists('odbc_fetch_array')
-			? odbc_fetch_array($this->result_id)
-			: $this->_odbc_fetch_array($this->result_id);
+		return odbc_fetch_array($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -158,57 +165,47 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return function_exists('odbc_fetch_object')
-			? odbc_fetch_object($this->result_id)
-			: $this->_odbc_fetch_object($this->result_id);
-	}
+		$row = odbc_fetch_object($this->result_id);
 
-	// --------------------------------------------------------------------
-
-	/**
-	 * Result - object
-	 *
-	 * subsititutes the odbc_fetch_object function when
-	 * not available (odbc_fetch_object requires unixODBC)
-	 *
-	 * @return	object
-	 */
-	protected function _odbc_fetch_object(& $odbc_result)
-	{
-		$rs = array();
-		if ( ! odbc_fetch_into($odbc_result, $rs))
+		if ($class_name === 'stdClass' OR ! $row)
 		{
-			return FALSE;
+			return $row;
 		}
 
-		$rs_obj = new stdClass();
-		foreach ($rs as $k => $v)
+		$class_name = new $class_name();
+		foreach ($row as $key => $value)
 		{
-			$field_name = odbc_field_name($odbc_result, $k+1);
-			$rs_obj->$field_name = $v;
+			$class_name->$key = $value;
 		}
 
-		return $rs_obj;
+		return $class_name;
 	}
 
-	// --------------------------------------------------------------------
+}
 
+// --------------------------------------------------------------------
+
+if ( ! function_exists('odbc_fetch_array'))
+{
 	/**
-	 * Result - array
+	 * ODBC Fetch array
 	 *
-	 * subsititutes the odbc_fetch_array function when
-	 * not available (odbc_fetch_array requires unixODBC)
+	 * Emulates the native odbc_fetch_array() function when
+	 * it is not available (odbc_fetch_array() requires unixODBC)
 	 *
+	 * @param	resource
+	 * @param	int
 	 * @return	array
 	 */
-	protected function _odbc_fetch_array(& $odbc_result)
+	function odbc_fetch_array(& $result, $rownumber = 1)
 	{
 		$rs = array();
-		if ( ! odbc_fetch_into($odbc_result, $rs))
+		if ( ! odbc_fetch_into($result, $rs, $rownumber))
 		{
 			return FALSE;
 		}
@@ -216,83 +213,45 @@
 		$rs_assoc = array();
 		foreach ($rs as $k => $v)
 		{
-			$field_name = odbc_field_name($odbc_result, $k+1);
+			$field_name = odbc_field_name($result, $k+1);
 			$rs_assoc[$field_name] = $v;
 		}
 
 		return $rs_assoc;
 	}
+}
 
-	// --------------------------------------------------------------------
+// --------------------------------------------------------------------
 
+if ( ! function_exists('odbc_fetch_object'))
+{
 	/**
-	 * Query result. Array version.
+	 * ODBC Fetch object
 	 *
-	 * @return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) > 0)
-		{
-			return $this->result_array;
-		}
-		elseif (($c = count($this->result_object)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_array[$i] = (array) $this->result_object[$i];
-			}
-		}
-		elseif ($this->result_id === FALSE)
-		{
-			return array();
-		}
-		else
-		{
-			while ($row = $this->_fetch_assoc())
-			{
-				$this->result_array[] = $row;
-			}
-		}
-
-		return $this->result_array;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Object version.
+	 * Emulates the native odbc_fetch_object() function when
+	 * it is not available.
 	 *
-	 * @return	array
+	 * @param	resource
+	 * @param	int
+	 * @return	object
 	 */
-	public function result_object()
+	function odbc_fetch_object(& $result, $rownumber = 1)
 	{
-		if (count($this->result_object) > 0)
+		$rs = array();
+		if ( ! odbc_fetch_into($result, $rs, $rownumber))
 		{
-			return $this->result_object;
-		}
-		elseif (($c = count($this->result_array)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_object[$i] = (object) $this->result_array[$i];
-			}
-		}
-		elseif ($this->result_id === FALSE)
-		{
-			return array();
-		}
-		else
-		{
-			while ($row = $this->_fetch_object())
-			{
-				$this->result_object[] = $row;
-			}
+			return FALSE;
 		}
 
-		return $this->result_object;
+		$rs_object = new stdClass();
+		foreach ($rs as $k => $v)
+		{
+			$field_name = odbc_field_name($result, $k+1);
+			$rs_object->$field_name = $v;
+		}
+
+		return $rs_object;
 	}
-
 }
 
 /* End of file odbc_result.php */
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index a3ad469..b36a3d9 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -42,25 +42,28 @@
 
 	public $dbdriver = 'pdo';
 
-	// the character used to excape - not necessary for PDO
-	protected $_escape_char = '';
+	// The character used to escaping
+	protected $_escape_char = '"';
 
 	// clause and character used for LIKE escape sequences
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword;
 
-	// need to track the pdo driver and options
-	public $pdodriver;
+	public $trans_enabled = FALSE;
+
+	// need to track the PDO options
 	public $options = array();
 
+	/**
+	 * Constructor
+	 *
+	 * Validates the DSN string and/or detects the subdriver
+	 *
+	 * @param	array
+	 * @return	void
+	 */
 	public function __construct($params)
 	{
 		parent::__construct($params);
@@ -69,118 +72,36 @@
 		{
 			// If there is a minimum valid dsn string pattern found, we're done
 			// This is for general PDO users, who tend to have a full DSN string.
-			$this->pdodriver = end($match);
+			$this->subdriver = $match[1];
+			return;
 		}
-		else
+		// Legacy support for DSN specified in the hostname field
+		elseif (preg_match('/([^;]+):/', $this->hostname, $match) && count($match) === 2)
 		{
-			// Try to build a complete DSN string from params
-			$this->_connect_string($params);
+			$this->dsn = $this->hostname;
+			$this->hostname = NULL;
+			$this->subdriver = $match[1];
+			return;
 		}
+		elseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE))
+		{
+			$this->subdriver = 'dblib';
+		}
+		elseif ($this->subdriver === '4D')
+		{
+			$this->subdriver = '4d';
+		}
+		elseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'sqlite', 'sqlsrv'), TRUE))
+		{
+			log_message('error', 'PDO: Invalid or non-existent subdriver');
 
-		// clause and character used for LIKE escape sequences
-		// this one depends on the driver being used
-		if ($this->pdodriver === 'mysql')
-		{
-			$this->_escape_char = '`';
-			$this->_like_escape_str = '';
-			$this->_like_escape_chr = '\\';
-		}
-		elseif ($this->pdodriver === 'odbc')
-		{
-			$this->_like_escape_str = " {escape '%s'} ";
-		}
-		elseif ( ! in_array($this->pdodriver, array('sqlsrv', 'mssql', 'dblib', 'sybase')))
-		{
-			$this->_escape_char = '"';
-		}
-
-		$this->trans_enabled = FALSE;
-		$this->_random_keyword = ' RND('.time().')'; // database specific random keyword
-	}
-
-	/**
-	 * Connection String
-	 *
-	 * @param	array
-	 * @return	void
-	 */
-	protected function _connect_string($params)
-	{
-		if (strpos($this->hostname, ':'))
-		{
-			// hostname generally would have this prototype
-			// $db['hostname'] = 'pdodriver:host(/Server(/DSN))=hostname(/DSN);';
-			// We need to get the prefix (pdodriver used by PDO).
-			$dsnarray = explode(':', $this->hostname);
-			$this->pdodriver = $dsnarray[0];
-
-			// End dsn with a semicolon for extra backward compability
-			// if database property was not empty.
-			if ( ! empty($this->database))
+			if ($this->db_debug)
 			{
-				$this->dsn .= rtrim($this->hostname, ';').';';
-			}
-		}
-		else
-		{
-			// Invalid DSN, display an error
-			if ( ! array_key_exists('pdodriver', $params))
-			{
-				show_error('Invalid DB Connection String for PDO');
-			}
-
-			// Assuming that the following DSN string format is used:
-			// $dsn = 'pdo://username:password@hostname:port/database?pdodriver=pgsql';
-			$this->dsn = $this->pdodriver.':';
-
-			// Add hostname to the DSN for databases that need it
-			if ( ! empty($this->hostname)
-				&& strpos($this->hostname, ':') === FALSE
-				&& in_array($this->pdodriver, array('informix', 'mysql', 'pgsql', 'sybase', 'mssql', 'dblib', 'cubrid')))
-			{
-			    $this->dsn .= 'host='.$this->hostname.';';
-			}
-
-			// Add a port to the DSN for databases that can use it
-			if ( ! empty($this->port) && in_array($this->pdodriver, array('informix', 'mysql', 'pgsql', 'ibm', 'cubrid')))
-			{
-			    $this->dsn .= 'port='.$this->port.';';
+				show_error('Invalid or non-existent PDO subdriver');
 			}
 		}
 
-		// Add the database name to the DSN, if needed
-	    if (stripos($this->dsn, 'dbname') === FALSE
-	       && in_array($this->pdodriver, array('4D', 'pgsql', 'mysql', 'firebird', 'sybase', 'mssql', 'dblib', 'cubrid')))
-	    {
-	        $this->dsn .= 'dbname='.$this->database.';';
-	    }
-	    elseif (stripos($this->dsn, 'database') === FALSE && in_array($this->pdodriver, array('ibm', 'sqlsrv')))
-	    {
-	    	if (stripos($this->dsn, 'dsn') === FALSE)
-	    	{
-		        $this->dsn .= 'database='.$this->database.';';
-	    	}
-	    }
-	    elseif ($this->pdodriver === 'sqlite' && $this->dsn === 'sqlite:')
-	    {
-	        if ($this->database !== ':memory')
-	        {
-	            if ( ! file_exists($this->database))
-	            {
-	                show_error('Invalid DB Connection string for PDO SQLite');
-	            }
-
-	            $this->dsn .= (strpos($this->database, DIRECTORY_SEPARATOR) !== 0) ? DIRECTORY_SEPARATOR : '';
-	        }
-
-	        $this->dsn .= $this->database;
-	    }
-
-	    // Add charset to the DSN, if needed
-	    if ( ! empty($this->char_set) && in_array($this->pdodriver, array('4D', 'mysql', 'sybase', 'mssql', 'dblib', 'oci')))
-	    {
-	        $this->dsn .= 'charset='.$this->char_set.';';
-	    }
+		$this->dsn = NULL;
 	}
 
 	// --------------------------------------------------------------------
@@ -188,53 +109,17 @@
 	/**
 	 * Non-persistent database connection
 	 *
-	 * @return	object
-	 */
-	public function db_connect()
-	{
-		return $this->_pdo_connect();
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Persistent database connection
-	 *
-	 * @return	object
-	 */
-	public function db_pconnect()
-	{
-		return $this->_pdo_connect(TRUE);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * PDO connection
-	 *
 	 * @param	bool
 	 * @return	object
 	 */
-	protected function _pdo_connect($persistent = FALSE)
+	public function db_connect($persistent = FALSE)
 	{
-		$this->options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT;
-		$persistent === FALSE OR $this->options[PDO::ATTR_PERSISTENT] = TRUE;
-
-		/* Prior to PHP 5.3.6, even if the charset was supplied in the DSN
-		 * on connect - it was ignored. This is a work-around for the issue.
-		 *
-		 * Reference: http://www.php.net/manual/en/ref.pdo-mysql.connection.php
-		 */
-		if ($this->pdodriver === 'mysql' && ! is_php('5.3.6') && ! empty($this->char_set))
-		{
-			$this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set
-					.( ! empty($this->db_collat) ? " COLLATE '".$this->dbcollat."'" : '');
-		}
+		$this->options[PDO::ATTR_PERSISTENT] = $persistent;
 
 		// Connecting...
 		try
 		{
-			return new PDO($this->dsn, $this->username, $this->password, $this->options);
+			return @new PDO($this->dsn, $this->username, $this->password, $this->options);
 		}
 		catch (PDOException $e)
 		{
@@ -250,15 +135,38 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Persistent database connection
+	 *
+	 * @return	object
+	 */
+	public function db_pconnect()
+	{
+		return $this->db_connect(TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Database version number
 	 *
 	 * @return	string
 	 */
 	public function version()
 	{
-		return isset($this->data_cache['version'])
-			? $this->data_cache['version']
-			: $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
+		if (isset($this->data_cache['version']))
+		{
+			return $this->data_cache['version'];
+		}
+
+		// Not all subdrivers support the getAttribute() method
+		try
+		{
+			return $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
+		}
+		catch (PDOException $e)
+		{
+			return parent::version();
+		}
 	}
 
 	// --------------------------------------------------------------------
@@ -358,7 +266,7 @@
 		$str = $this->conn_id->quote($str);
 
 		// If there are duplicated quotes, trim them away
-		if (strpos($str, "'") === 0)
+		if ($str[0] === "'")
 		{
 			$str = substr($str, 1, -1);
 		}
@@ -396,69 +304,12 @@
 	 */
 	public function insert_id($name = NULL)
 	{
-		if ($this->pdodriver === 'pgsql' && $name === NULL && $this->version() >= '8.1')
-		{
-			$query = $this->query('SELECT LASTVAL() AS ins_id');
-			$query = $query->row();
-			return $query->ins_id;
-		}
-
 		return $this->conn_id->lastInsertId($name);
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * Show table query
-	 *
-	 * Generates a platform-specific query string so that the table names can be fetched
-	 *
-	 * @param	bool
-	 * @return	string
-	 */
-	protected function _list_tables($prefix_limit = FALSE)
-	{
-		if ($this->pdodriver === 'pgsql')
-		{
-			// Analog function to show all tables in postgre
-			$sql = "SELECT * FROM information_schema.tables WHERE table_schema = 'public'";
-		}
-		elseif ($this->pdodriver === 'sqlite')
-		{
-			// Analog function to show all tables in sqlite
-			$sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'";
-		}
-		else
-		{
-			$sql = 'SHOW TABLES FROM '.$this->escape_identifiers($this->database);
-		}
-
-		if ($prefix_limit !== FALSE AND $this->dbprefix !== '')
-		{
-			return FALSE;
-		}
-
-		return $sql;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Show column query
-	 *
-	 * Generates a platform-specific query string so that the column names can be fetched
-	 *
-	 * @param	string	the table name
-	 * @return	string
-	 */
-	protected function _list_columns($table = '')
-	{
-		return 'SHOW COLUMNS FROM '.$this->escape_identifiers($table);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Field data query
 	 *
 	 * Generates a platform-specific query so that the column data can be retrieved
@@ -468,23 +319,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		if ($this->pdodriver === 'mysql' or $this->pdodriver === 'pgsql')
-		{
-			// Analog function for mysql and postgre
-			return 'SELECT * FROM '.$this->escape_identifiers($table).' LIMIT 1';
-		}
-		elseif ($this->pdodriver === 'oci')
-		{
-			// Analog function for oci
-			return 'SELECT * FROM '.$this->escape_identifiers($table).' WHERE ROWNUM <= 1';
-		}
-		elseif ($this->pdodriver === 'sqlite')
-		{
-			// Analog function for sqlite
-			return 'PRAGMA table_info('.$this->escape_identifiers($table).')';
-		}
-
-		return 'SELECT TOP 1 FROM '.$this->escape_identifiers($table);
+		return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -530,7 +365,7 @@
 	 */
 	protected function _update_batch($table, $values, $index, $where = NULL)
 	{
-		$ids   = array();
+		$ids = array();
 		$where = ($where !== '' && count($where) >=1) ? implode(" ", $where).' AND ' : '';
 
 		foreach ($values as $key => $val)
@@ -582,32 +417,10 @@
 	 */
 	protected function _truncate($table)
 	{
-		return 'DELETE FROM '.$table;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		if ($this->pdodriver === 'pgsql')
-		{
-			return $sql.' LIMIT '.$limit.($offset ? ' OFFSET '.$offset : '');
-		}
-
-		return $sql.' LIMIT '.($offset ? $offset.', ' : '').$limit;
+		return 'TRUNCATE TABLE '.$table;
 	}
 
 }
 
 /* End of file pdo_driver.php */
-/* Location: ./system/database/drivers/pdo/pdo_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/pdo/pdo_driver.php */
diff --git a/system/database/drivers/pdo/pdo_forge.php b/system/database/drivers/pdo/pdo_forge.php
index 02ceb74..34a6ee4 100644
--- a/system/database/drivers/pdo/pdo_forge.php
+++ b/system/database/drivers/pdo/pdo_forge.php
@@ -34,7 +34,6 @@
  */
 class CI_DB_pdo_forge extends CI_DB_forge {
 
-	protected $_drop_database	= 'DROP DATABASE %s';
 	protected $_drop_table		= 'DROP TABLE %s';
 
 	/**
@@ -78,7 +77,7 @@
 				if ( ! empty($attributes['CONSTRAINT']))
 				{
 					// Exception for Postgre numeric which not too happy with constraint within those type
-					if ( ! ($this->db->pdodriver === 'pgsql' && in_array($attributes['TYPE'], $numeric)))
+					if ( ! ($this->db->subdriver === 'pgsql' && in_array($attributes['TYPE'], $numeric)))
 					{
 						$sql .= '('.$attributes['CONSTRAINT'].')';
 					}
diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php
index 0b8937c..4444069 100644
--- a/system/database/drivers/pdo/pdo_result.php
+++ b/system/database/drivers/pdo/pdo_result.php
@@ -21,7 +21,7 @@
  * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version 2.1.0
+ * @since		Version 1.0
  * @filesource
  */
 
@@ -33,72 +33,35 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	2.1
  */
 class CI_DB_pdo_result extends CI_DB_result {
 
 	/**
-	 * @var bool  Hold the flag whether a result handler already fetched before
-	 */
-	protected $is_fetched = FALSE;
-
-	/**
-	 * @var mixed Hold the fetched assoc array of a result handler
-	 */
-	protected $result_assoc;
-
-	/**
 	 * Number of rows in the result set
 	 *
 	 * @return	int
 	 */
 	public function num_rows()
 	{
-		if (empty($this->result_id) OR ! is_object($this->result_id))
+		if (is_int($this->num_rows))
 		{
-			// invalid result handler
-			return 0;
+			return $this->num_rows;
 		}
-		elseif (($num_rows = $this->result_id->rowCount()) && $num_rows > 0)
+		elseif (count($this->result_array) > 0)
 		{
-			// If rowCount return something, we're done.
-			return $num_rows;
+			return $this->num_rows = count($this->result_array);
+		}
+		elseif (count($this->result_object) > 0)
+		{
+			return $this->num_rows = count($this->result_object);
+		}
+		elseif (($num_rows = $this->result_id->rowCount()) > 0)
+		{
+			return $this->num_rows = $num_rows;
 		}
 
-		// Fetch the result, instead perform another extra query
-		return ($this->is_fetched && is_array($this->result_assoc)) ? count($this->result_assoc) : count($this->result_assoc());
-	}
-
-	/**
-	 * Fetch the result handler
-	 *
-	 * @return	mixed
-	 */
-	public function result_assoc()
-	{
-		// If the result already fetched before, use that one
-		if (count($this->result_array) > 0 OR $this->is_fetched)
-		{
-			return $this->result_array();
-		}
-
-		// Define the output
-		$output = array('assoc', 'object');
-
-		// Initial value
-		$this->result_assoc = array() and $this->result_object = array();
-
-		// Fetch the result
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->result_assoc[] = $row;
-			$this->result_object[] = (object) $row;
-		}
-
-		// Save this as buffer and marked the fetch flag
-		$this->result_array = $this->result_assoc;
-		$this->is_fetched = TRUE;
-
-		return $this->result_assoc;
+		return $this->num_rows = count($this->result_array());
 	}
 
 	// --------------------------------------------------------------------
@@ -124,12 +87,14 @@
 	 */
 	public function list_fields()
 	{
-		if ($this->db->db_debug)
+		$field_names = array();
+		for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
 		{
-			return $this->db->display_error('db_unsuported_feature');
+			$field_names[$i] = @$this->result_id->getColumnMeta();
+			$field_names[$i] = $field_names[$i]['name'];
 		}
 
-		return FALSE;
+		return $field_names;
 	}
 
 	// --------------------------------------------------------------------
@@ -240,11 +205,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return $this->result_id->fetch(PDO::FETCH_OBJ);
+		return $this->result_id->fetchObject($class_name);
 	}
 
 }
diff --git a/system/database/drivers/interbase/index.html b/system/database/drivers/pdo/subdrivers/index.html
similarity index 100%
copy from system/database/drivers/interbase/index.html
copy to system/database/drivers/pdo/subdrivers/index.html
diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
new file mode 100644
index 0000000..e287f5c
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
@@ -0,0 +1,223 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO 4D Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_4d_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = '4d';
+
+	// The character used for escaping
+	protected $_escape_char = array('[', ']');
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = '4D:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 3) === FALSE)
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT '.$this->escape_identifiers('TABLE_NAME').' FROM '.$this->escape_identifiers('_USER_TABLES');
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' WHERE '.$this->escape_identifiers('TABLE_NAME')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT '.$this->escape_identifiers('COLUMN_NAME').' FROM '.$this->escape_identifiers('_USER_COLUMNS')
+			.' WHERE '.$this->escape_identifiers('TABLE_NAME').' = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return $sql.' LIMIT '.$limit.($offset ? ' OFFSET '.$offset : '');
+	}
+
+}
+
+/* End of file pdo_4d_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_4d_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
new file mode 100644
index 0000000..05eeacf
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
@@ -0,0 +1,189 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO CUBRID Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_cubrid_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'cubrid';
+
+	protected $_escape_char = '`';
+
+	// clause and character used for LIKE escape sequences - not used in CUBRID
+	protected $_like_escape_str = '';
+	protected $_like_escape_chr = '\\';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'cubrid:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SHOW TABLES';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	protected function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field !== $index)
+				{
+					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$cases = '';
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k." = CASE \n"
+				.implode("\n", $v)."\n"
+				.'ELSE '.$k.' END), ';
+		}
+
+		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.$index.' IN('.implode(',', $ids).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'TRUNCATE '.$table;
+	}
+
+}
+
+/* End of file pdo_cubrid_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
new file mode 100644
index 0000000..7060c9e
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -0,0 +1,265 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO DBLIB Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'dblib';
+
+	protected $_random_keyword = ' NEWID()';
+
+	protected $_quoted_identifier;
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = $params['subdriver'].':host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			if ( ! empty($this->port))
+			{
+				$this->dsn .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port;
+			}
+
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+			empty($this->appname) OR $this->dsn .= ';appname='.$this->appname;
+		}
+		else
+		{
+			if ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)
+			{
+				$this->dsn .= ';charset='.$this->char_set;
+			}
+
+			$this->subdriver = 'dblib';
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @param	bool
+	 * @return	object
+	 */
+	public function db_connect($persistent = FALSE)
+	{
+		$this->conn_id = parent::db_connect($persistent);
+
+		if ( ! is_object($this->conn_id))
+		{
+			return $this->conn_id;
+		}
+
+		// Determine how identifiers are escaped
+		$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+		$query = $query->row_array();
+		$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+		$this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+		return $this->conn_id;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		return 'SELECT '.$this->escape_identifiers('name')
+			.' FROM '.$this->escape_identifiers('sysobjects')
+			.' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql.' ORDER BY '.$this->escape_identifiers('name');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT '.$this->escape_identifiers('column_name')
+				.' FROM '.$this->escape_identifiers('information_schema.columns')
+				.' WHERE '.$this->escape_identifiers('table_name').' = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return ($limit)
+			? 'WITH ci_delete AS (SELECT TOP '.$limit.' * FROM '.$table.$conditions.') DELETE FROM ci_delete'
+			: 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		$limit = $offset + $limit;
+
+		// As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,
+		// however an ORDER BY clause is required for it to work
+		if (version_compare($this->version(), '9', '>=') && $offset && ! empty($this->qb_orderby))
+		{
+			$orderby = 'ORDER BY '.implode(', ', $this->qb_orderby);
+
+			// We have to strip the ORDER BY clause
+			$sql = trim(substr($sql, 0, strrpos($sql, 'ORDER BY '.$orderby)));
+
+			return 'SELECT '.(count($this->qb_select) === 0 ? '*' : implode(', ', $this->qb_select))." FROM (\n"
+				.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.$orderby.') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+				."\n) ".$this->escape_identifiers('CI_subquery')
+				."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.((int) $offset + 1).' AND '.$limit;
+		}
+
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+	}
+
+}
+
+/* End of file pdo_dblib_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php */
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
new file mode 100644
index 0000000..c074a9a
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
@@ -0,0 +1,262 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO Firebird Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_firebird_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'firebird';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_random_keyword = ' RANDOM()'; // Currently not supported
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'firebird:';
+
+			if ( ! empty($this->database))
+			{
+				$this->dsn .= 'dbname='.$this->database;
+			}
+			elseif ( ! empty($this->hostname))
+			{
+				$this->dsn .= 'dbname='.$this->hostname;
+			}
+
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+			empty($this->role) OR $this->dsn .= ';role='.$this->role;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 9) === FALSE)
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "RDB$RELATION_NAME" FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' AND "RDB$RELATION_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "RDB$FIELD_NAME" FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT FIRST 1 * FROM '.$this->protect_identifiers($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return 	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+	 */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
+			.$where
+			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+         */
+	protected function _truncate($table)
+	{
+		return 'DELETE FROM '.$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		// Limit clause depends on if Interbase or Firebird
+		if (stripos($this->version(), 'firebird') !== FALSE)
+		{
+			$select = 'FIRST '. (int) $limit
+				.($offset > 0 ? ' SKIP '. (int) $offset : '');
+		}
+		else
+		{
+			$select = 'ROWS '
+				.($offset > 0 ? (int) $offset.' TO '.($limit + $offset) : (int) $limit);
+		}
+
+		return preg_replace('`SELECT`i', 'SELECT '.$select, $sql);
+	}
+
+}
+
+/* End of file pdo_firebird_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
new file mode 100644
index 0000000..832c03c
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
@@ -0,0 +1,262 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO IBM DB2 Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_ibm_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'ibm';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'ibm:';
+
+			// Pre-defined DSN
+			if (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))
+			{
+				if (isset($this->DSN))
+				{
+					$this->dsn .= 'DSN='.$this->DSN;
+				}
+				elseif ( ! empty($this->database))
+				{
+					$this->dsn .= 'DSN='.$this->database;
+				}
+
+				return;
+			}
+
+			$this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';
+
+			if (isset($this->DATABASE))
+			{
+				$this->dsn .= 'DATABASE='.$this->DATABASE.';';
+			}
+			elseif ( ! empty($this->database))
+			{
+				$this->dsn .= 'DATABASE='.$this->database.';';
+			}
+
+			if (isset($this->HOSTNAME))
+			{
+				$this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';
+			}
+			else
+			{
+				$this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');
+			}
+
+			if (isset($this->PORT))
+			{
+				$this->dsn .= 'PORT='.$this->port.';';
+			}
+			elseif ( ! empty($this->port))
+			{
+				$this->dsn .= ';PORT='.$this->port.';';
+			}
+
+			$this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "tabname" FROM "syscat"."tables" WHERE "type" = \'T\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND "tabname" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "colname" FROM "syscat"."tables"
+			WHERE "syscat"."tabtype" = \'T\' AND "syscat"."tabname" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' FETCH FIRST 1 ROWS ONLY';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		$sql .= ' FETCH FIRST '.($limit + $offset).' ROWS ONLY';
+
+		return ($offset)
+			? 'SELECT * FROM ('.$sql.') WHERE rownum > '.$offset
+			: $sql;
+	}
+
+}
+
+/* End of file pdo_ibm_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
new file mode 100644
index 0000000..a3efc63
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
@@ -0,0 +1,271 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO Informix Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_informix_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'informix';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'informix:';
+
+			// Pre-defined DSN
+			if (empty($this->hostname) && empty($this->host) && empty($this->port) && empty($this->service))
+			{
+				if (isset($this->DSN))
+				{
+					$this->dsn .= 'DSN='.$this->DSN;
+				}
+				elseif ( ! empty($this->database))
+				{
+					$this->dsn .= 'DSN='.$this->database;
+				}
+
+				return;
+			}
+
+			if (isset($this->host))
+			{
+				$this->dsn .= 'host='.$this->host;
+			}
+			else
+			{
+				$this->dsn .= 'host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+			}
+
+			if (isset($this->service))
+			{
+				$this->dsn .= '; service='.$this->service;
+			}
+			elseif ( ! empty($this->port))
+			{
+				$this->dsn .= '; service='.$this->port;
+			}
+
+			empty($this->database) OR $this->dsn .= '; database='.$this->database;
+			empty($this->server) OR $this->dsn .= '; server='.$this->server;
+
+			$this->dsn .= '; protocol='.(isset($this->protocol) ? $this->protocol : 'onsoctcp')
+				.'; EnableScrollableCursors=1';
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "tabname" FROM "systables" WHERE "tabid" > 99 AND "tabtype" = \'T\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND "tabname" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "colname" FROM "systables", "syscolumns"
+			WHERE "systables"."tabid" = "syscolumns"."tabid" AND "systables"."tabtype" = \'T\' AND "systables"."tabname" = '
+			.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT FIRST 1 * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'TRUNCATE TABLE ONLY '.$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		$select = 'SELECT '.($offset ? 'SKIP '.$offset : '').'FIRST '.$limit.' ';
+		return preg_replace('/^(SELECT\s)/i', $select, $sql, 1);
+	}
+
+}
+
+/* End of file pdo_informix_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_informix_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
new file mode 100644
index 0000000..78afe24
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
@@ -0,0 +1,217 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO MySQL Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'mysql';
+
+	protected $_escape_char = '`';
+
+	// clause and character used for LIKE escape sequences - not used in MySQL
+	protected $_like_escape_str = '';
+	protected $_like_escape_chr = '\\';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'mysql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE && is_php('5.3.6'))
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @param	bool
+	 * @return	object
+	 */
+	public function db_connect($persistent = FALSE)
+	{
+		/* Prior to PHP 5.3.6, even if the charset was supplied in the DSN
+		 * on connect - it was ignored. This is a work-around for the issue.
+		 *
+		 * Reference: http://www.php.net/manual/en/ref.pdo-mysql.connection.php
+		 */
+		if ( ! is_php('5.3.6') && ! empty($this->char_set))
+		{
+			$this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set
+				.(empty($this->dbcollat) ? '' : ' COLLATE '.$this->dbcollat);
+		}
+
+		return parent::db_connect($persistent);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SHOW TABLES';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	protected function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field !== $index)
+				{
+					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$cases = '';
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k." = CASE \n"
+				.implode("\n", $v)."\n"
+				.'ELSE '.$k.' END), ';
+		}
+
+		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.$index.' IN('.implode(',', $ids).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'TRUNCATE '.$table;
+	}
+
+}
+
+/* End of file pdo_mysql_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
new file mode 100644
index 0000000..56ec1bc
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
@@ -0,0 +1,230 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO Oracle Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_oci_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'oci';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_count_string = 'SELECT COUNT(1) AS ';
+	protected $_random_keyword = ' ASC'; // Currently not supported
+
+	protected $_reserved_identifiers = array('*', 'rownum');
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'oci:dbname=';
+
+			// Oracle has a slightly different PDO DSN format (Easy Connect),
+			// which also supports pre-defined DSNs.
+			if (empty($this->hostname) && empty($this->port))
+			{
+				$this->dsn .= $this->database;
+			}
+			else
+			{
+				$this->dsn .= '//'.(empty($this->hostname) ? '127.0.0.1' : $this->hostname)
+					.(empty($this->port) ? '' : ':'.$this->port).'/';
+
+				empty($this->database) OR $this->dsn .= $this->database;
+			}
+
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 4) === FALSE)
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "TABLE_NAME" FROM "ALL_TABLES"';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' WHERE "TABLE_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "COLUMN_NAME" FROM "all_tab_columns" WHERE "TABLE_NAME" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' WHERE rownum = 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert_batch statement
+	 *
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return 	string
+	 */
+	protected function _insert_batch($table, $keys, $values)
+	{
+		$keys = implode(', ', $keys);
+		$sql = "INSERT ALL\n";
+
+		for ($i = 0, $c = count($values); $i < $c; $i++)
+		{
+			$sql .= '	INTO '.$table.' ('.$keys.') VALUES '.$values[$i]."\n";
+		}
+
+		return $sql.'SELECT * FROM dual';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+		empty($limit) OR $conditions[] = 'rownum <= '.$limit;
+
+		return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($offset + $limit + 1).')'
+			.($offset ? ' WHERE rnum >= '.($offset + 1): '');
+	}
+
+}
+
+/* End of file pdo_oci_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_oci_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
new file mode 100644
index 0000000..392754f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
@@ -0,0 +1,268 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO ODBC Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_odbc_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'odbc';
+
+	// The character used for escaping - not used in ODBC
+	protected $_escape_char = '';
+
+	// clause and character used for LIKE escape sequences
+	protected $_like_escape_chr = '!';
+	protected $_like_escape_str = " {escape '%s'} ";
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'odbc:';
+
+			// Pre-defined DSN
+			if (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))
+			{
+				if (isset($this->DSN))
+				{
+					$this->dsn .= 'DSN='.$this->DSN;
+				}
+				elseif ( ! empty($this->database))
+				{
+					$this->dsn .= 'DSN='.$this->database;
+				}
+
+				return;
+			}
+
+			// If the DSN is not pre-configured - try to build an IBM DB2 connection string
+			$this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';
+
+			if (isset($this->DATABASE))
+			{
+				$this->dsn .= 'DATABASE='.$this->DATABASE.';';
+			}
+			elseif ( ! empty($this->database))
+			{
+				$this->dsn .= 'DATABASE='.$this->database.';';
+			}
+
+			if (isset($this->HOSTNAME))
+			{
+				$this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';
+			}
+			else
+			{
+				$this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');
+			}
+
+			if (isset($this->PORT))
+			{
+				$this->dsn .= 'PORT='.$this->port.';';
+			}
+			elseif ( ! empty($this->port))
+			{
+				$this->dsn .= ';PORT='.$this->port.';';
+			}
+
+			$this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
+
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
+		{
+			return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+                return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT column_name FROM information_schema.columns WHERE table_name = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'DELETE FROM '.$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+	}
+
+}
+
+/* End of file pdo_odbc_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
new file mode 100644
index 0000000..9a476f1
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
@@ -0,0 +1,341 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO PostgreSQL Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_pgsql_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'pgsql';
+
+	protected $_random_keyword = ' RANDOM()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'pgsql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert ID
+	 *
+	 * @param	string
+	 * @return	int
+	 */
+	public function insert_id($name = NULL)
+	{
+		if ($name === NULL && version_compare($this->version(), '8.1', '>='))
+		{
+			$query = $this->query('SELECT LASTVAL() AS ins_id');
+			$query = $query->row();
+			return $query->ins_id;
+		}
+
+		return $this->conn_id->lastInsertId($name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \'public\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' AND "table_name" LIKE \''
+				.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "column_name" FROM "information_schema"."columns" WHERE "table_name" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	protected function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field !== $index)
+				{
+					$final[$field][] =  'WHEN '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$cases = '';
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k.' = (CASE '.$k."\n"
+				.implode("\n", $v)."\n"
+				.'ELSE '.$k.' END), ';
+		}
+
+		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.$index.' IN('.implode(',', $ids).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return $sql.' LIMIT '.$limit.($offset ? ' OFFSET '.$offset : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * 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
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+		foreach ($key as $k => $v)
+		{
+			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
+				? $this->_group_get_type('')
+				: $this->_group_get_type($type);
+
+			$k = (($op = $this->_get_operator($k)) !== FALSE)
+				? $this->protect_identifiers(substr($k, 0, strpos($k, $op)), FALSE, $escape).strstr($k, $op)
+				: $this->protect_identifiers($k, FALSE, $escape);
+
+			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)
+				{
+					$v = ' '.$this->escape($v);
+				}
+				elseif (is_bool($v))
+				{
+					$v = ($v ? ' TRUE' : ' FALSE');
+				}
+
+				if ( ! $this->_has_operator($k))
+				{
+					$k .= ' = ';
+				}
+			}
+
+			$this->qb_where[] = $prefix.$k.$v;
+			if ($this->qb_caching === TRUE)
+			{
+				$this->qb_cache_where[] = $prefix.$k.$v;
+				$this->qb_cache_exists[] = 'where';
+			}
+
+		}
+
+		return $this;
+	}
+
+}
+
+/* End of file pdo_pgsql_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
new file mode 100644
index 0000000..bf0363f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
@@ -0,0 +1,167 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO SQLite Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_sqlite_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'sqlite';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_random_keyword = ' RANDOM()'; // Currently not supported
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'sqlite:';
+
+			if (empty($this->database) && empty($this->hostname))
+			{
+				$this->database = ':memory:';
+			}
+
+			$this->database = empty($this->database) ? $this->hostname : $this->database;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' AND "NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		// Not supported
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 0,1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Replace statement
+	 *
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return 	string
+	 */
+	protected function _replace($table, $keys, $values)
+	{
+		return 'INSERT OR '.parent::_replace($table, $keys, $values);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+         */
+	protected function _truncate($table)
+	{
+		return 'DELETE FROM '.$table;
+	}
+
+}
+
+/* End of file pdo_sqlite_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
new file mode 100644
index 0000000..f125b8f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -0,0 +1,299 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO SQLSRV Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'sqlsrv';
+
+	protected $_random_keyword = ' NEWID()';
+
+	protected $_quoted_identifier;
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'sqlsrv:Server='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ','.$this->port;
+			empty($this->database) OR $this->dsn .= ';Database='.$this->database;
+
+			// Some custom options
+
+			if (isset($this->QuotedId))
+			{
+				$this->dsn .= ';QuotedId='.$this->QuotedId;
+				$this->_quoted_identifier = (bool) $this->QuotedId;
+			}
+
+			if (isset($this->ConnectionPooling))
+			{
+				$this->dsn .= ';ConnectionPooling='.$this->ConnectionPooling;
+			}
+
+			if (isset($this->Encrypt))
+			{
+				$this->dsn .= ';Encrypt='.$this->Encrypt;
+			}
+
+			if (isset($this->TraceOn))
+			{
+				$this->dsn .= ';TraceOn='.$this->TraceOn;
+			}
+
+			if (isset($this->TrustServerCertificate))
+			{
+				$this->dsn .= ';TrustServerCertificate='.$this->TrustServerCertificate;
+			}
+
+			empty($this->APP) OR $this->dsn .= ';APP='.$this->APP;
+			empty($this->Failover_Partner) OR $this->dsn .= ';Failover_Partner='.$this->Failover_Partner;
+			empty($this->LoginTimeout) OR $this->dsn .= ';LoginTimeout='.$this->LoginTimeout;
+			empty($this->MultipleActiveResultSets) OR $this->dsn .= ';MultipleActiveResultSets='.$this->MultipleActiveResultSets;
+			empty($this->TraceFile) OR $this->dsn .= ';TraceFile='.$this->TraceFile;
+			empty($this->WSID) OR $this->dsn .= ';WSID='.$this->WSID;
+		}
+		elseif (preg_match('/QuotedId=(0|1)/', $this->dsn, $match))
+		{
+			$this->_quoted_identifier = (bool) $match[1];
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @param	bool
+	 * @return	object
+	 */
+	public function db_connect($persistent = FALSE)
+	{
+		if ( ! empty($this->char_set) && preg_match('/utf[^8]*8/i', $this->char_set))
+		{
+			$this->options[PDO::SQLSRV_ENCODING_UTF8] = 1;
+		}
+
+		$this->conn_id = parent::db_connect($persistent);
+
+		if ( ! is_object($this->conn_id) OR is_bool($this->_quoted_identifier))
+		{
+			return $this->conn_id;
+		}
+
+		// Determine how identifiers are escaped
+		$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+		$query = $query->row_array();
+		$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+		$this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+		return $this->conn_id;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		return 'SELECT '.$this->escape_identifiers('name')
+			.' FROM '.$this->escape_identifiers('sysobjects')
+			.' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql.' ORDER BY '.$this->escape_identifiers('name');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT '.$this->escape_identifiers('column_name')
+			.' FROM '.$this->escape_identifiers('information_schema.columns')
+			.' WHERE '.$this->escape_identifiers('table_name').' = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return ($limit)
+			? 'WITH ci_delete AS (SELECT TOP '.$limit.' * FROM '.$table.$conditions.') DELETE FROM ci_delete'
+			: 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		// As of SQL Server 2012 (11.0.*) OFFSET is supported
+		if (version_compare($this->version(), '11', '>='))
+		{
+			return $sql.' OFFSET '.(int) $offset.' ROWS FETCH NEXT '.(int) $limit.' ROWS ONLY';
+		}
+
+		$limit = $offset + $limit;
+
+		// An ORDER BY clause is required for ROW_NUMBER() to work
+		if ($offset && ! empty($this->qb_orderby))
+		{
+			$orderby = 'ORDER BY '.implode(', ', $this->qb_orderby);
+
+			// We have to strip the ORDER BY clause
+			$sql = trim(substr($sql, 0, strrpos($sql, 'ORDER BY '.$orderby)));
+
+			return 'SELECT '.(count($this->qb_select) === 0 ? '*' : implode(', ', $this->qb_select))." FROM (\n"
+				.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.$orderby.') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+				."\n) ".$this->escape_identifiers('CI_subquery')
+				."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.((int) $offset + 1).' AND '.$limit;
+		}
+
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+	}
+
+}
+
+/* End of file pdo_sqlsrv_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php */
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index e73122b..20b7867 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -48,12 +48,6 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RANDOM()'; // database specific random keyword
 
 	/**
diff --git a/system/database/drivers/postgre/postgre_result.php b/system/database/drivers/postgre/postgre_result.php
index f913bc9..eb9d647 100644
--- a/system/database/drivers/postgre/postgre_result.php
+++ b/system/database/drivers/postgre/postgre_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_postgre_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @pg_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @pg_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -156,11 +159,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return pg_fetch_object($this->result_id);
+		return pg_fetch_object($this->result_id, NULL, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index 87be7a5..19824db 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' Random()'; // database specific random keyword
 
 	/**
diff --git a/system/database/drivers/sqlite/sqlite_result.php b/system/database/drivers/sqlite/sqlite_result.php
index 741dc9d..eef9787 100644
--- a/system/database/drivers/sqlite/sqlite_result.php
+++ b/system/database/drivers/sqlite/sqlite_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_sqlite_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @sqlite_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @sqlite_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -140,17 +143,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		if (function_exists('sqlite_fetch_object'))
-		{
-			return sqlite_fetch_object($this->result_id);
-		}
-
-		$arr = sqlite_fetch_array($this->result_id, SQLITE_ASSOC);
-		return is_array($arr) ? (object) $arr : FALSE;
+		return sqlite_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
index 1c6533f..cc35d31 100644
--- a/system/database/drivers/sqlite3/sqlite3_driver.php
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -50,12 +50,6 @@
 	protected $_like_escape_str = ' ESCAPE \'%s\' ';
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RANDOM()';
 
 	/**
diff --git a/system/database/drivers/sqlite3/sqlite3_result.php b/system/database/drivers/sqlite3/sqlite3_result.php
index 946b365..8e9b9c1 100644
--- a/system/database/drivers/sqlite3/sqlite3_result.php
+++ b/system/database/drivers/sqlite3/sqlite3_result.php
@@ -26,40 +26,21 @@
  */
 
 /**
- * SQLite Result Class
+ * SQLite3 Result Class
  *
  * This class extends the parent result class: CI_DB_result
  *
  * @category	Database
- * @author	Andrey Andreev
- * @link	http://codeigniter.com/user_guide/database/
+ * @author		Andrey Andreev
+ * @link		http://codeigniter.com/user_guide/database/
+ * @since	3.0
  */
 class CI_DB_sqlite3_result extends CI_DB_result {
 
-	// Overwriting the parent here, so we have a way to know if it's already set
-	public $num_rows;
-
 	// num_fields() might be called multiple times, so we'll use this one to cache it's result
 	protected $_num_fields;
 
 	/**
-	 * Number of rows in the result set
-	 *
-	 * @return	int
-	 */
-	public function num_rows()
-	{
-		/* The SQLite3 driver doesn't have a graceful way to do this,
-		 * so we'll have to do it on our own.
-		 */
-		return is_int($this->num_rows)
-			? $this->num_rows
-			: $this->num_rows = count($this->result_array());
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Number of fields in the result set
 	 *
 	 * @return	int
@@ -153,443 +134,28 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		// No native support for fetching as an object
-		$row = $this->_fetch_assoc();
-		return ($row !== FALSE) ? (object) $row : FALSE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. "array" version.
-	 *
-	 * return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) > 0)
+		// No native support for fetching rows as objects
+		if (($row = $this->result_id->fetchArray(SQLITE3_ASSOC)) === FALSE)
 		{
-			return $this->result_array;
+			return FALSE;
 		}
-		elseif (is_array($this->row_data))
+		elseif ($class_name === 'stdClass')
 		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_array;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
-		}
-
-		$row = NULL;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->row_data[$row_index++] = $row;
-		}
-
-		return $this->result_array = $this->row_data;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. "object" version.
-	 *
-	 * @return	array
-	 */
-	public function result_object()
-	{
-		if (count($this->result_object) > 0)
-		{
-			return $this->result_object;
-		}
-		elseif (count($this->result_array) > 0)
-		{
-			for ($i = 0, $c = count($this->result_array); $i < $c; $i++)
-			{
-				$this->result_object[] = (object) $this->result_array[$i];
-			}
-
-			return $this->result_object;
-		}
-		elseif (is_array($this->row_data))
-		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_object;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-				for ($i = 0; $i < $row_index; $i++)
-				{
-					$this->result_object[$i] = (object) $this->row_data[$i];
-				}
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
-		}
-
-		$row = NULL;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->row_data[$row_index] = $row;
-			$this->result_object[$row_index++] = (object) $row;
-		}
-
-		$this->result_array = $this->row_data;
-
-		/* As described for the num_rows() method - there's no easy
-		 * way to get the number of rows selected. Our work-around
-		 * solution (as in here as well) first checks if result_array
-		 * exists and returns its count. It doesn't however check for
-		 * custom_object_result, so - do it here.
-		 */
-		if ( ! is_int($this->num_rows))
-		{
-			$this->num_rows = count($this->result_object);
-		}
-
-		return $this->result_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Custom object version.
-	 *
-	 * @param	string	class name used to instantiate rows to
-	 * @return	array
-	 */
-	public function custom_result_object($class_name)
-	{
-		if (array_key_exists($class_name, $this->custom_result_object))
-		{
-			return $this->custom_result_object[$class_name];
-		}
-
-		if ( ! class_exists($class_name) OR ! is_object($this->result_id) OR $this->num_rows() === 0)
-		{
-			return array();
-		}
-
-		/* Even if result_array hasn't been set prior to custom_result_object being called,
-		 * num_rows() has done it.
-		*/
-		$data = &$this->result_array;
-
-		$result_object = array();
-		for ($i = 0, $c = count($data); $i < $c; $i++)
-		{
-			$result_object[$i] = new $class_name();
-			foreach ($data[$i] as $key => $value)
-			{
-				$result_object[$i]->$key = $value;
-			}
-		}
-
-		/* As described for the num_rows() method - there's no easy
-		 * way to get the number of rows selected. Our work-around
-		 * solution (as in here as well) first checks if result_array
-		 * exists and returns its count. It doesn't however check for
-		 * custom_object_result, so - do it here.
-		 */
-		if ( ! is_int($this->num_rows))
-		{
-			$this->num_rows = count($result_object);
-		}
-
-		// Cache and return the array
-		return $this->custom_result_object[$class_name] = $result_object;
-        }
-
-	// --------------------------------------------------------------------
-
-	/* Single row result.
-	 *
-	 * Acts as a wrapper for row_object(), row_array()
-	 * and custom_row_object(). Also used by first_row(), next_row()
-	 * and previous_row().
-	 *
-	 * @param	int	row index
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function row($n = 0, $type = 'object')
-	{
-		if ($type === 'object')
-		{
-			return $this->row_object($n);
-		}
-		elseif ($type === 'array')
-		{
-			return $this->row_array($n);
-		}
-
-		return $this->custom_row_object($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Array version.
-	 *
-	 * @param	int	row index
-	 * @return	array
-	 */
-	public function row_array($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		/* If row_data is initialized, it means that we've already tried
-		 * (at least) to fetch some data, so ... check if we already have
-		 * this row.
-		*/
-		if (is_array($this->row_data))
-		{
-			/* If we already have row_data[$n] - return it.
-			 *
-			 * If we enter the elseif, there's a number of reasons to
-			 * return an empty array:
-			 *
-			 *	- count($this->row_data) === 0 means there are no results
-			 *	- num_rows being set or result_array having count() > 0 means
-			 *	  that we've already fetched all data and $n is greater than
-			 *	  our highest row index available
-			 *	- $n < $this->current_row means that if such row existed,
-			 *	  we would've already returned it, therefore $n is an
-			 *	  invalid index
-			 */
-			if (isset($this->row_data[$n])) // We already have this row
-			{
-				$this->current_row = $n;
-				return $this->row_data[$n];
-			}
-			elseif (count($this->row_data) === 0 OR is_int($this->num_rows)
-				OR count($this->result_array) > 0 OR $n < $this->current_row)
-			{
-				// No such row exists
-				return NULL;
-			}
-
-			// Get the next row index that would actually need to be fetched
-			$current_row = ($this->current_row < count($this->row_data)) ? count($this->row_data) : $this->current_row + 1;
-		}
-		else
-		{
-			$current_row = $this->current_row = 0;
-			$this->row_data = array();
-		}
-
-		/* Fetch more data, if available
-		 *
-		 * NOTE: Operator precedence is important here, if you change
-		 *	 'AND' with '&&' - it WILL BREAK the results, as
-		 *	 $row will be assigned the scalar value of both
-		 *	 expressions!
-		 */
-		while ($row = $this->_fetch_assoc() AND $current_row <= $n)
-		{
-			$this->row_data[$current_row++] = $row;
-		}
-
-		// This would mean that there's no (more) data to fetch
-		if ( ! is_array($this->row_data) OR ! isset($this->row_data[$n]))
-		{
-			// Cache what we already have
-			if (is_array($this->row_data))
-			{
-				$this->num_rows = count($this->row_data);
-				/* Usually, row_data could have less elements than result_array,
-				 * but at this point - they should be exactly the same.
-				 */
-				$this->result_array = $this->row_data;
-			}
-			else
-			{
-				$this->num_rows = 0;
-			}
-
-			return NULL;
-		}
-
-		$this->current_row = $n;
-		return $this->row_data[$n];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Object version.
-	 *
-	 * @param	int	row index
-	 * @return	mixed	object if row found; empty array if not
-	 */
-	public function row_object($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		/* Logic here is exactly the same as in row_array,
-		 * except we have to cast row_data[$n] to an object.
-		 *
-		 * If we already have result_object though - we can
-		 * directly return from it.
-		 */
-		if (isset($this->result_object[$n]))
-		{
-			$this->current_row = $n;
-			return $this->result_object[$n];
-		}
-
-		$row = $this->row_array($n);
-		// Cast only if the row exists
-		if (count($row) > 0)
-		{
-			$this->current_row = $n;
 			return (object) $row;
 		}
 
-		return NULL;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Custom object version.
-	 *
-	 * @param	int	row index
-	 * @param	string	custom class name
-	 * @return	mixed	custom object if row found; empty array otherwise
-	 */
-	public function custom_row_object($n = 0, $class_name)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
+		$class_name = new $class_name();
+		foreach (array_keys($row) as $key)
 		{
-			$n = (int) $n;
+			$class_name->$key = $row[$key];
 		}
 
-		if (array_key_exists($class_name, $this->custom_result_object))
-		{
-			/* We already have a the whole result set with this class_name,
-			 * return the specified row if it exists, and an empty array if
-			 * it doesn't.
-			 */
-			if (isset($this->custom_result_object[$class_name][$n]))
-			{
-				$this->current_row = $n;
-				return $this->custom_result_object[$class_name][$n];
-			}
-			else
-			{
-				return NULL;
-			}
-		}
-		elseif ( ! class_exists($class_name)) // No such class exists
-		{
-			return NULL;
-		}
-
-		$row = $this->row_array($n);
-		// A non-array would mean that the row doesn't exist
-		if ( ! is_array($row))
-		{
-			return NULL;
-		}
-
-		// Convert to the desired class and return
-		$row_object = new $class_name();
-		foreach ($row as $key => $value)
-		{
-			$row_object->$key = $value;
-		}
-
-		$this->current_row = $n;
-		return $row_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* First row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function first_row($type = 'object')
-	{
-		return $this->row(0, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Last row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function last_row($type = 'object')
-	{
-		$result = &$this->result($type);
-		if ( ! isset($this->num_rows))
-		{
-			$this->num_rows = count($result);
-		}
-		$this->current_row = $this->num_rows - 1;
-		return $result[$this->current_row];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Next row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function next_row($type = 'object')
-	{
-		if (is_array($this->row_data))
-		{
-			$count = count($this->row_data);
-			$n = ($this->current_row > $count OR ($this->current_row === 0 && $count === 0)) ? $count : $this->current_row + 1;
-		}
-		else
-		{
-			$n = 0;
-		}
-
-		return $this->row($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Previous row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function previous_row($type = 'object')
-	{
-		$n = ($this->current_row !== 0) ? $this->current_row - 1 : 0;
-		return $this->row($n, $type);
+		return $class_name;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 3a4fc0a..8bd18bd 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' NEWID()';
 
 	// SQLSRV-specific properties
diff --git a/system/database/drivers/sqlsrv/sqlsrv_result.php b/system/database/drivers/sqlsrv/sqlsrv_result.php
index f9d5a0d..fb7a686 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_result.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_result.php
@@ -21,7 +21,7 @@
  * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version 2.0.3
+ * @since		Version 1.0
  * @filesource
  */
 
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	2.0.3
  */
 class CI_DB_sqlsrv_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @sqlsrv_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @sqlsrv_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -142,11 +145,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return sqlsrv_fetch_object($this->result_id);
+		return sqlsrv_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php
index 5d02439..ed2fe3c 100644
--- a/system/helpers/array_helper.php
+++ b/system/helpers/array_helper.php
@@ -52,7 +52,7 @@
 	 */
 	function element($item, $array, $default = NULL)
 	{
-		return empty($array[$item]) ? $default : $array[$item];
+		return array_key_exists($item, $array) ? $array[$item] : $default;
 	}
 }
 
@@ -95,7 +95,7 @@
 
 		foreach ($items as $item)
 		{
-			$return[$item] = isset($array[$item]) ? $array[$item] : $default;
+			$return[$item] = array_key_exists($item, $array) ? $array[$item] : $default;
 		}
 
 		return $return;
diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php
index 9637e26..a45b3d7 100644
--- a/system/helpers/date_helper.php
+++ b/system/helpers/date_helper.php
@@ -117,26 +117,37 @@
 	 *
 	 * Returns a date formatted according to the submitted standard.
 	 *
+	 * As of PHP 5.2, the DateTime extension provides constants that
+	 * serve for the exact same purpose and are used with date().
+	 * Due to that, this function is DEPRECATED and should be removed
+	 * in CodeIgniter 3.1+.
+	 *
+	 * Here are two examples of how you should replace it:
+	 *
+	 *	date(DATE_RFC822, now()); // default
+	 *	date(DATE_W3C, $time); // a different format and time
+	 *
+	 * Reference: http://www.php.net/manual/en/class.datetime.php#datetime.constants.types
+	 *
+	 * @deprecated
 	 * @param	string	the chosen format
 	 * @param	int	Unix timestamp
 	 * @return	string
 	 */
-	function standard_date($fmt = 'DATE_RFC822', $time = '')
+	function standard_date($fmt = 'DATE_RFC822', $time = NULL)
 	{
-		$formats = array(
-				'DATE_ATOM'		=>	'%Y-%m-%dT%H:%i:%s%P',
-				'DATE_COOKIE'	=>	'%l, %d-%M-%y %H:%i:%s UTC',
-				'DATE_ISO8601'	=>	'%Y-%m-%dT%H:%i:%s%P',
-				'DATE_RFC822'	=>	'%D, %d %M %y %H:%i:%s %O',
-				'DATE_RFC850'	=>	'%l, %d-%M-%y %H:%i:%s UTC',
-				'DATE_RFC1036'	=>	'%D, %d %M %y %H:%i:%s %O',
-				'DATE_RFC1123'	=>	'%D, %d %M %Y %H:%i:%s %O',
-				'DATE_RFC2822'	=>	'%r',
-				'DATE_RSS'		=>	'%D, %d %M %Y %H:%i:%s %O',
-				'DATE_W3C'		=>	'%Y-%m-%dT%H:%i:%s%P'
-				);
+		if (empty($time))
+		{
+			$time = now();
+		}
 
-		return isset($formats[$fmt]) ? mdate($formats[$fmt], $time) : FALSE;
+		// Procedural style pre-defined constants from the DateTime extension
+		if (strpos($fmt, 'DATE_') !== 0 OR defined($fmt) === FALSE)
+		{
+			return FALSE;
+		}
+
+		return date(constant($fmt), $time);
 	}
 }
 
diff --git a/system/language/english/migration_lang.php b/system/language/english/migration_lang.php
index af92066..9c8909a 100644
--- a/system/language/english/migration_lang.php
+++ b/system/language/english/migration_lang.php
@@ -27,7 +27,7 @@
 
 $lang['migration_none_found']			= "No migrations were found.";
 $lang['migration_not_found']			= "No migration could be found with the version number: %d.";
-$lang['migration_multiple_version']		= "This are multiple migrations with the same version number: %d.";
+$lang['migration_multiple_version']		= "There are multiple migrations with the same version number: %d.";
 $lang['migration_class_doesnt_exist']	= "The migration class \"%s\" could not be found.";
 $lang['migration_missing_up_method']	= "The migration class \"%s\" is missing an 'up' method.";
 $lang['migration_missing_down_method']	= "The migration class \"%s\" is missing a 'down' method.";
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index 3536241..b490a34 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -460,6 +460,12 @@
 				$this->_field_data[$field]['postdata'] = $validation_array[$field];
 			}
 
+			// Don't try to validate if we have no rules set
+			if (empty($row['rules']))
+			{
+				continue;
+			}
+
 			$this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
 		}
 
diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php
index 3a1e7a0..c786703 100644
--- a/system/libraries/Migration.php
+++ b/system/libraries/Migration.php
@@ -292,7 +292,7 @@
 
 		// Calculate the last migration step from existing migration
 		// filenames and procceed to the standard version migration
-		return $this->version((int) substr($last_migration, 0, 3));
+		return $this->version((int) $last_migration);
 	}
 
 	// --------------------------------------------------------------------
@@ -322,9 +322,9 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set's the schema to the latest migration
+	 * Retrieves list of available migration scripts
 	 *
-	 * @return	mixed	true if already latest, false if failed, int if upgraded
+	 * @return	array	list of migration file paths sorted by version
 	 */
 	protected function find_migrations()
 	{
diff --git a/tests/codeigniter/database/query_builder/join_test.php b/tests/codeigniter/database/query_builder/join_test.php
index b8cf2a8..25bd4ac 100644
--- a/tests/codeigniter/database/query_builder/join_test.php
+++ b/tests/codeigniter/database/query_builder/join_test.php
@@ -35,4 +35,24 @@
 		$this->assertEquals('Developer', $job_user[0]['job_name']);
 	}
 
+	// ------------------------------------------------------------------------
+
+	public function test_join_escape_multiple_conditions()
+	{
+		// We just need a valid query produced, not one that makes sense
+		$fields = array($this->db->protect_identifiers('table1.field1'), $this->db->protect_identifiers('table2.field2'));
+
+		$expected = 'SELECT '.implode(', ', $fields)
+				."\nFROM ".$this->db->escape_identifiers('table1')
+				."\nLEFT JOIN ".$this->db->escape_identifiers('table2').' ON '.implode(' = ', $fields)
+				.' AND '.$fields[0]." = 'foo' AND ".$fields[1].' = 0';
+
+		$result = $this->db->select('table1.field1, table2.field2')
+				->from('table1')
+				->join('table2', "table1.field1 = table2.field2 AND table1.field1 = 'foo' AND table2.field2 = 0", 'LEFT')
+				->get_compiled_select();
+
+		$this->assertEquals($expected, $result);
+	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/date_helper_test.php b/tests/codeigniter/helpers/date_helper_test.php
index eaf4cf8..1b79b94 100644
--- a/tests/codeigniter/helpers/date_helper_test.php
+++ b/tests/codeigniter/helpers/date_helper_test.php
@@ -69,7 +69,7 @@
 	public function test_standard_date_rfc822()
 	{
 		$this->assertEquals(
-			date('D, d M y H:i:s O', $this->time),
+			date(DATE_RFC822, $this->time),
 			standard_date('DATE_RFC822', $this->time)
 		);
 	}
@@ -79,7 +79,7 @@
 	public function test_standard_date_atom()
 	{
 		$this->assertEquals(
-			date('Y-m-d\TH:i:sP', $this->time),
+			date(DATE_ATOM, $this->time),
 			standard_date('DATE_ATOM', $this->time)
 		);
 	}
@@ -89,7 +89,7 @@
 	public function test_standard_date_cookie()
 	{
 		$this->assertEquals(
-			date("l, d-M-y H:i:s \U\T\C", $this->time),
+			date(DATE_COOKIE, $this->time),
 			standard_date('DATE_COOKIE', $this->time)
 		);
 	}
@@ -99,7 +99,7 @@
 	public function test_standard_date_iso8601()
 	{
 		$this->assertEquals(
-			date('Y-m-d\TH:i:sP', $this->time),
+			date(DATE_ISO8601, $this->time),
 			standard_date('DATE_ISO8601', $this->time)
 		);
 	}
@@ -109,7 +109,7 @@
 	public function test_standard_date_rfc850()
 	{
 		$this->assertEquals(
-			date("l, d-M-y H:i:s \U\T\C", $this->time),
+			date(DATE_RFC850, $this->time),
 			standard_date('DATE_RFC850', $this->time)
 		);
 	}
@@ -119,7 +119,7 @@
 	public function test_standard_date_rfc1036()
 	{
 		$this->assertEquals(
-			date('D, d M y H:i:s O', $this->time),
+			date(DATE_RFC1036, $this->time),
 			standard_date('DATE_RFC1036', $this->time)
 		);
 	}
@@ -129,7 +129,7 @@
 	public function test_standard_date_rfc1123()
 	{
 		$this->assertEquals(
-			date('D, d M Y H:i:s O', $this->time),
+			date(DATE_RFC1123, $this->time),
 			standard_date('DATE_RFC1123', $this->time)
 		);
 	}
@@ -139,7 +139,7 @@
 	public function test_standard_date_rfc2822()
 	{
 		$this->assertEquals(
-			date('r', $this->time),
+			date(DATE_RFC2822, $this->time),
 			standard_date('DATE_RFC2822', $this->time)
 		);
 	}
@@ -149,7 +149,7 @@
 	public function test_standard_date_rss()
 	{
 		$this->assertEquals(
-			date('D, d M Y H:i:s O', $this->time),
+			date(DATE_RSS, $this->time),
 			standard_date('DATE_RSS', $this->time)
 		);
 	}
@@ -159,7 +159,7 @@
 	public function test_standard_date_w3c()
 	{
 		$this->assertEquals(
-			date('Y-m-d\TH:i:sP', $this->time),
+			date(DATE_W3C, $this->time),
 			standard_date('DATE_W3C', $this->time)
 		);
 	}
diff --git a/tests/mocks/database/config/pdo/mysql.php b/tests/mocks/database/config/pdo/mysql.php
index fefe0d6..96608f7 100644
--- a/tests/mocks/database/config/pdo/mysql.php
+++ b/tests/mocks/database/config/pdo/mysql.php
@@ -4,13 +4,13 @@
 
 	// Typical Database configuration
 	'pdo/mysql' => array(
-		'dsn' => '',
+		'dsn' => 'mysql:host=localhost;dbname=ci_test',
 		'hostname' => 'localhost',
 		'username' => 'travis',
 		'password' => '',
 		'database' => 'ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'mysql'
+		'subdriver' => 'mysql'
 	),
 
 	// Database configuration with failover
@@ -21,16 +21,16 @@
 		'password' => 'wrong password',
 		'database' => 'not_ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'mysql',
+		'subdriver' => 'mysql',
 		'failover' => array(
 			array(
-				'dsn' => '',
+				'dsn' => 'mysql:host=localhost;dbname=ci_test',
 				'hostname' => 'localhost',
 				'username' => 'travis',
 				'password' => '',
 				'database' => 'ci_test',
 				'dbdriver' => 'pdo',
-				'pdodriver' => 'mysql'
+				'subdriver' => 'mysql'
 			)
 		)
 	)
diff --git a/tests/mocks/database/config/pdo/pgsql.php b/tests/mocks/database/config/pdo/pgsql.php
index ddd638c..e55e3ea 100644
--- a/tests/mocks/database/config/pdo/pgsql.php
+++ b/tests/mocks/database/config/pdo/pgsql.php
@@ -10,7 +10,7 @@
 		'password' => '',
 		'database' => 'ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'pgsql'
+		'subdriver' => 'pgsql'
 	),
 
 	// Database configuration with failover
@@ -21,7 +21,7 @@
 		'password' => 'wrong password',
 		'database' => 'not_ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'pgsql',
+		'subdriver' => 'pgsql',
 		'failover' => array(
 			array(
 				'dsn' => 'pgsql:host=localhost;port=5432;dbname=ci_test;',
@@ -30,7 +30,7 @@
 				'password' => '',
 				'database' => 'ci_test',
 				'dbdriver' => 'pdo',
-				'pdodriver' => 'pgsql'
+				'subdriver' => 'pgsql'
 			)
 		)
 	)
diff --git a/tests/mocks/database/config/pdo/sqlite.php b/tests/mocks/database/config/pdo/sqlite.php
index 3646184..1bf56b3 100644
--- a/tests/mocks/database/config/pdo/sqlite.php
+++ b/tests/mocks/database/config/pdo/sqlite.php
@@ -10,7 +10,7 @@
 		'password' => 'sqlite',
 		'database' => 'sqlite',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'sqlite'
+		'subdriver' => 'sqlite'
 	),
 
 	// Database configuration with failover
@@ -21,7 +21,7 @@
 		'password' => 'sqlite',
 		'database' => 'sqlite',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'sqlite',
+		'subdriver' => 'sqlite',
 		'failover' => array(
 			array(
 				'dsn' => 'sqlite:/'.realpath(__DIR__.'/../..').'/ci_test.sqlite',
@@ -30,7 +30,7 @@
 				'password' => 'sqlite',
 				'database' => 'sqlite',
 				'dbdriver' => 'pdo',
-				'pdodriver' => 'sqlite'
+				'subdriver' => 'sqlite'
 			)
 		)
 	)
diff --git a/tests/mocks/database/db.php b/tests/mocks/database/db.php
index 30504bb..7565853 100644
--- a/tests/mocks/database/db.php
+++ b/tests/mocks/database/db.php
@@ -45,9 +45,9 @@
 		);
 
 		$config = array_merge($this->config[$group], $params);
-		$dsnstring = ( ! empty($config['dsn'])) ? $config['dsn'] : FALSE;
-		$pdodriver = ( ! empty($config['pdodriver'])) ? $config['pdodriver'] : FALSE;
-		$failover = ( ! empty($config['failover'])) ? $config['failover'] : FALSE;
+		$dsnstring = empty($config['dsn']) ? FALSE : $config['dsn'];
+		$subdriver = empty($config['subdriver']) ? FALSE: $config['subdriver'];
+		$failover = empty($config['failover']) ? FALSE : $config['failover'];
 
 		$dsn = $config['dbdriver'].'://'.$config['username'].':'.$config['password']
 			       .'@'.$config['hostname'].'/'.$config['database'];
@@ -55,7 +55,7 @@
 		// Build the parameter
 		$other_params = array_slice($config, 6);
 		if ($dsnstring) $other_params['dsn'] = $dsnstring;
-		if ($pdodriver) $other_params['pdodriver'] = $pdodriver;
+		if ($subdriver) $other_params['subdriver'] = $subdriver;
 		if ($failover) $other_params['failover'] = $failover;
 
 		return $dsn.'?'.http_build_query($other_params);
diff --git a/user_guide_src/source/DCO.rst b/user_guide_src/source/DCO.rst
new file mode 100644
index 0000000..c8f9b49
--- /dev/null
+++ b/user_guide_src/source/DCO.rst
@@ -0,0 +1,27 @@
+#####################################
+Developer's Certificate of Origin 1.1
+#####################################
+
+By making a contribution to this project, I certify that:
+
+(1)	The contribution was created in whole or in part by me and I
+	have the right to submit it under the open source license
+	indicated in the file; or
+
+(2)	The contribution is based upon previous work that, to the best
+	of my knowledge, is covered under an appropriate open source
+	license and I have the right under that license to submit that
+	work with modifications, whether created in whole or in part
+	by me, under the same open source license (unless I am
+	permitted to submit under a different license), as indicated
+	in the file; or
+
+(3)	The contribution was provided directly to me by some other
+	person who certified (1), (2) or (3) and I have not modified
+	it.
+
+(4)	I understand and agree that this project and the contribution
+	are public and that a record of the contribution (including all
+	personal information I submit with it, including my sign-off) is
+	maintained indefinitely and may be redistributed consistent with
+	this project or the open source license(s) involved.
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index efe49cf..0e9ec5e 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -48,11 +48,16 @@
    -  Global config files are loaded first, then environment ones. Environment config keys overwrite base ones, allowing to only set the keys we want changed per environment.
    -  Changed detection of ``$view_folder`` so that if it's not found in the current path, it will now also be searched for under the application folder.
    -  Path constants BASEPATH, APPPATH and VIEWPATH are now (internally) defined as absolute paths.
-   -  Updated email validation methods to use filter_var() instead of PCRE.
+   -  Updated email validation methods to use ``filter_var()`` instead of PCRE.
+   -  Changed environment defaults to report all errors in 'development' and only fatal ones in 'testing' and 'production' but only display them in 'development'.
 
 -  Helpers
 
-   -  :doc:`Date Helper <helpers/date_helper>` function now() now works with all timezone strings supported by PHP.
+   -  :doc:`Date Helper <helpers/date_helper>` changes include:
+	 - ``now()`` now works with all timezone strings supported by PHP.
+	 - Added an optional third parameter to ``timespan()`` that constrains the number of time units displayed.
+	 - Added an optional parameter to ``timezone_menu()`` that allows more attributes to be added to the generated select tag.
+	 - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <http://bg2.php.net/manual/en/class.datetime.php#datetime.constants.types>`_.
    -  ``create_captcha()`` accepts additional colors parameter, allowing for color customization.
    -  :doc:`URL Helper <helpers/url_helper>` changes include:
 	 - ``url_title()`` will now trim extra dashes from beginning and end.
@@ -63,7 +68,6 @@
    -  Changed ``humanize()`` to include a second param for the separator.
    -  Refactored ``plural()`` and ``singular()`` to avoid double pluralization and support more words.
    -  Added an optional third parameter to ``force_download()`` that enables/disables sending the actual file MIME type in the Content-Type header (disabled by default).
-   -  Added an optional third parameter to ``timespan()`` that constrains the number of time units displayed.
    -  Added a work-around in ``force_download()`` for a bug Android <= 2.1, where the filename extension needs to be in uppercase.
    -  ``form_dropdown()`` will now also take an array for unity with other form helpers.
    -  ``do_hash()`` now uses PHP's native ``hash()`` function (supporting more algorithms) and is deprecated.
@@ -72,7 +76,6 @@
 	 - ``set_realpath()`` can now also handle file paths as opposed to just directories.
 	 - Added an optional paramater to ``delete_files()`` to enable it to skip deleting files such as .htaccess and index.html.
 	 - ``read_file()`` is now a deprecated alias of ``file_get_contents()``.
-   -  Added an optional parameter to :doc:`Date Helper <helpers/date_helper>` function ``timezone_menu()`` that allows more attributes to be added to the generated select tag.
    -  :doc:`Security Helper <helpers/security_helper>` function ``strip_image_tags()`` is now an alias for the same method in the :doc:`Security Library <libraries/security>`.
 
 -  Database
@@ -93,7 +96,7 @@
 	 - Added support for backup() in :doc:`Database Utilities <database/utilities>`.
    -  Added 'dsn' configuration setting for drivers that support DSN strings (PDO, PostgreSQL, Oracle, ODBC, CUBRID).
    -  Improved PDO database support.
-   -  Added Interbase/Firebird database support via the "interbase" driver.
+   -  Added Interbase/Firebird database support via the 'ibase' driver.
    -  Added an optional database name parameter to db_select().
    -  Replaced the _error_message() and _error_number() methods with error(), that returns an array containing the last database error code and message.
    -  Improved version() implementation so that drivers that have a native function to get the version number don't have to be defined in the core DB_driver class.
@@ -126,7 +129,6 @@
 	 - Added support for drop_table() in :doc:`Database Forge <database/forge>`.
 	 - Added support for list_databases() in :doc:`Database Utilities <database/utilities>`.
 	 - Generally improved for speed and cleaned up all of its components.
-	 - *Row* result methods now really only fetch only the needed number of rows, instead of depending entirely on result().
 	 - num_rows() is now only called explicitly by the developer and no longer re-executes statements.
    -  Improved support of the SQLite driver, including:
 	 - Added support for replace() in :doc:`Query Builder <database/query_builder>`.
@@ -134,8 +136,12 @@
    -  Added ODBC support for create_database(), drop_database() and drop_table() in :doc:`Database Forge <database/forge>`.
    -  Added PDO support for create_database(), drop_database and drop_table() in :doc:`Database Forge <database/forge>`.
    -  Added unbuffered_row() method for getting a row without prefetching whole result (consume less memory).
+   -  Added PDO support for ``list_fields()`` in :doc:`Database Results <database/results>`.
+   -  Added capability for packages to hold database.php config files 
+   -  Added subdrivers support (currently only used by PDO).
 
 -  Libraries
+
    -  CI_Session now respects php.ini's session.gc_probability and session.gc_divisor
    -  Added max_filename_increment config setting for Upload library.
    -  CI_Loader::_ci_autoloader() is now a protected method.
@@ -163,6 +169,7 @@
 	 -  Removed method is_numeric() as it exists as a native PHP function and _execute() will find and use that (the 'is_numeric' rule itself is deprecated since 1.6.1).
 	 -  Native PHP functions used as rules can now accept an additional parameter, other than the data itself.
 	 -  Updated set_rules() to accept an array of rules as well as a string.
+	 -  Fields that have empty rules set no longer run through validation (and therefore are not considered erroneous).
    -  Changed the :doc:`Session Library <libraries/sessions>` to select only one row when using database sessions.
    -  Added all_flashdata() method to session class. Returns an associative array of only flashdata.
    -  Allowed for setting table class defaults in a config file.
@@ -176,7 +183,7 @@
 	 -  Added support for setting custom attributes.
 	 -  Deprecated usage of the "anchor_class" setting (use the new "attributes" setting instead).
 	 -  Added $config['reuse_query_string'] to allow automatic repopulation of query string arguments, combined with normal URI segments.
-   -  Added the ability to use a proxy with the :doc:`XML-RPC Library <libraries/xmlrpc.rst>`.
+   -  Added the ability to use a proxy with the :doc:`XML-RPC Library <libraries/xmlrpc>`.
 
 -  Core
 
@@ -196,6 +203,8 @@
    -  Added support for HTTP code 303 ("See Other") in set_status_header().
    -  Changed :doc:`Config Library <libraries/config>` method site_url() to accept an array as well.
    -  Added method ``strip_image_tags()`` to the :doc:`Security Library <libraries/security>`.
+   -  Changed ``_exception_handler()`` to respect php.ini 'display_errors' setting.
+
 
 Bug fixes for 3.0
 ------------------
@@ -303,6 +312,8 @@
 -  Fixed a bug (#427) - :doc:`Form Validation Library <libraries/form_validation>` method ``strip_image_tags()`` was an alias to a non-existent method.
 -  Fixed a bug (#1545) - :doc:`Query Builder <database/query_builder>` method ``limit()`` wasn't executed properly under Oracle.
 -  Fixed a bug (#1551) - :doc:`Date Helper <helpers/date_helper>` function ``standard_date()`` didn't properly format *W3C* and *ATOM* standard dates.
+-  Fixed a bug in :doc:`Query Builder <database/query_builder>` method join() where literal values were escaped as if they were fields.
+-  Fixed a bug (#135) - PHP Error logging was impossible without the errors being displayed.
 
 Version 2.1.2
 =============
diff --git a/user_guide_src/source/contributing/index.rst b/user_guide_src/source/contributing/index.rst
new file mode 100644
index 0000000..2ede0e6
--- /dev/null
+++ b/user_guide_src/source/contributing/index.rst
@@ -0,0 +1,105 @@
+###########################
+Contributing to CodeIgniter
+###########################
+
+CodeIgniter is a community driven project and accepts contributions of code
+and documentation from the community. These contributions are made in the form
+of Issues or `Pull Requests <http://help.github.com/send-pull-requests/>`_ on
+the `EllisLab CodeIgniter repository
+<https://github.com/EllisLab/CodeIgniter>`_ on GitHub.
+
+Issues are a quick way to point out a bug. If you find a bug or documentation
+error in CodeIgniter then please check a few things first:
+
+- There is not already an open Issue
+- The issue has already been fixed (check the develop branch, or look for
+  closed Issues)
+- Is it something really obvious that you fix it yourself?
+
+Reporting issues is helpful but an even better approach is to send a Pull
+Request, which is done by "Forking" the main repository and committing to your
+own copy. This will require you to use the version control system called Git.
+
+**********
+Guidelines
+**********
+
+Before we look into how, here are the guidelines. If your Pull Requests fail
+to pass these guidelines it will be declined and you will need to re-submit
+when you’ve made the changes. This might sound a bit tough, but it is required
+for us to maintain quality of the code-base.
+
+PHP Style
+=========
+
+All code must meet the `Style Guide
+<http://codeigniter.com/user_guide/general/styleguide.html>`_, which is
+essentially the `Allman indent style
+<http://en.wikipedia.org/wiki/Indent_style#Allman_style>`_, underscores and
+readable operators. This makes certain that all code is the same format as the
+existing code and means it will be as readable as possible.
+
+Documentation
+=============
+
+If you change anything that requires a change to documentation then you will
+need to add it. New classes, methods, parameters, changing default values, etc
+are all things that will require a change to documentation. The change-log
+must also be updated for every change. Also PHPDoc blocks must be maintained.
+
+Compatibility
+=============
+
+CodeIgniter is compatible with PHP 5.2.4 so all code supplied must stick to
+this requirement. If PHP 5.3 or 5.4 functions or features are used then there
+must be a fallback for PHP 5.2.4.
+
+Branching
+=========
+
+CodeIgniter uses the `Git-Flow
+<http://nvie.com/posts/a-successful-git-branching-model/>`_ branching model
+which requires all pull requests to be sent to the "develop" branch. This is
+where the next planned version will be developed. The "master" branch will
+always contain the latest stable version and is kept clean so a "hotfix" (e.g:
+an emergency security patch) can be applied to master to create a new version,
+without worrying about other features holding it up. For this reason all
+commits need to be made to "develop" and any sent to "master" will be closed
+automatically. If you have multiple changes to submit, please place all
+changes into their own branch on your fork.
+
+One thing at a time: A pull request should only contain one change. That does
+not mean only one commit, but one change - however many commits it took. The
+reason for this is that if you change X and Y but send a pull request for both
+at the same time, we might really want X but disagree with Y, meaning we
+cannot merge the request. Using the Git-Flow branching model you can create
+new branches for both of these features and send two requests.
+
+Signing
+=======
+You must sign your work, certifying that you either wrote the work or
+otherwise have the right to pass it on to an open source project. git makes
+this trivial as you merely have to use `--signoff` on your commits to your
+CodeIgniter fork.
+
+.. code-block:: bash
+
+	git commit --signoff
+
+or simply
+
+.. code-block:: bash
+
+	git commit -s
+
+This will sign your commits with the information setup in your git config, e.g.
+
+	Signed-off-by: John Q Public <john.public@example.com>
+
+If you are using Tower there is a "Sign-Off" checkbox in the commit window. You 
+could even alias git commit to use the -s flag so you don’t have to think about 
+it.
+
+By signing your work in this manner, you certify to a "Developer's Certificate 
+or Origin". The current version of this certificate is in the :doc:`/DCO` file
+in the root of this documentation.
diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst
index 7a19c84..c17de60 100644
--- a/user_guide_src/source/database/configuration.rst
+++ b/user_guide_src/source/database/configuration.rst
@@ -12,26 +12,44 @@
 The config settings are stored in a multi-dimensional array with this
 prototype::
 
-	$db['default']['hostname'] = "localhost";
-	$db['default']['username'] = "root";
-	$db['default']['password'] = "";
-	$db['default']['database'] = "database_name";
-	$db['default']['dbdriver'] = "mysql";
-	$db['default']['dbprefix'] = "";
-	$db['default']['pconnect'] = TRUE;
-	$db['default']['db_debug'] = FALSE;
-	$db['default']['cache_on'] = FALSE;
-	$db['default']['cachedir'] =  "";
-	$db['default']['char_set'] = "utf8";
-	$db['default']['dbcollat'] = "utf8_general_ci";
-	$db['default']['swap_pre'] = "";
-	$db['default']['autoinit'] = TRUE;
-	$db['default']['stricton'] = FALSE;
+	$db['default'] = array(
+		'dsn'	=> '',
+		'hostname' => 'localhost',
+		'username' => 'root',
+		'password' => '',
+		'database' => 'database_name',
+		'dbdriver' => 'mysqli',
+		'dbprefix' => '',
+		'pconnect' => TRUE,
+		'db_debug' => TRUE,
+		'cache_on' => FALSE,
+		'cachedir' => '',
+		'char_set' => 'utf8',
+		'dbcollat' => 'utf8_general_ci',
+		'swap_pre' => '',
+		'autoinit' => TRUE,
+		'stricton' => FALSE,
+		'failover' => array()
+	);
 
-If you use PDO as your dbdriver, you can specify the full DSN string describe a connection to the database like this::
+Some database drivers (such as PDO, PostgreSQL, Oracle, ODBC) might
+require a full DSN string to be provided. If that is the case, you
+should use the 'dsn' configuration setting, as if you're using the
+driver's underlying native PHP extension, like this::
 
+	// PDO
 	$db['default']['dsn'] = 'pgsql:host=localhost;port=5432;dbname=database_name';
 
+	// Oracle
+	$db['default']['dsn'] = '//localhost/XE';
+
+.. note:: If you do not specify a DSN string for a driver that requires it, CodeIgniter
+	will try to build it with the rest of the provided settings.
+
+.. note:: If you provide a DSN string and it is missing some valid settings (e.g. the
+	database character set), which are present in the rest of the configuration
+	fields, CodeIgniter will append them.
+
 You can also specify failovers for the situation when the main connection cannot connect for some reason.
 These failovers can be specified by setting the failover for a connection like this::
 
@@ -41,7 +59,7 @@
 				'username' => '',
 				'password' => '',
 				'database' => '',
-				'dbdriver' => 'mysql',
+				'dbdriver' => 'mysqli',
 				'dbprefix' => '',
 				'pconnect' => TRUE,
 				'db_debug' => TRUE,
@@ -58,7 +76,7 @@
 				'username' => '',
 				'password' => '',
 				'database' => '',
-				'dbdriver' => 'mysql',
+				'dbdriver' => 'mysqli',
 				'dbprefix' => '',
 				'pconnect' => TRUE,
 				'db_debug' => TRUE,
@@ -81,30 +99,34 @@
 connection group for each, then switch between groups as needed. For
 example, to set up a "test" environment you would do this::
 
-	$db['test']['hostname'] = "localhost";
-	$db['test']['username'] = "root";
-	$db['test']['password'] = "";
-	$db['test']['database'] = "database_name";
-	$db['test']['dbdriver'] = "mysql";
-	$db['test']['dbprefix'] = "";
-	$db['test']['pconnect'] = TRUE;
-	$db['test']['db_debug'] = FALSE;
-	$db['test']['cache_on'] = FALSE;
-	$db['test']['cachedir'] =  "";
-	$db['test']['char_set'] = "utf8";
-	$db['test']['dbcollat'] = "utf8_general_ci";
-	$db['test']['swap_pre'] = "";
-	$db['test']['autoinit'] = TRUE;
-	$db['test']['stricton'] = FALSE;
+	$db['test'] = array(
+		'dsn'	=> '',
+		'hostname' => 'localhost',
+		'username' => 'root',
+		'password' => '',
+		'database' => 'database_name',
+		'dbdriver' => 'mysqli',
+		'dbprefix' => '',
+		'pconnect' => TRUE,
+		'db_debug' => TRUE,
+		'cache_on' => FALSE,
+		'cachedir' => '',
+		'char_set' => 'utf8',
+		'dbcollat' => 'utf8_general_ci',
+		'swap_pre' => '',
+		'autoinit' => TRUE,
+		'stricton' => FALSE,
+		'failover' => array()
+	);
 
 Then, to globally tell the system to use that group you would set this
 variable located in the config file::
 
-	$active_group = "test";
+	$active_group = 'test';
 
-Note: The name "test" is arbitrary. It can be anything you want. By
-default we've used the word "default" for the primary connection, but it
-too can be renamed to something more relevant to your project.
+.. note:: The name 'test' is arbitrary. It can be anything you want. By
+	default we've used the word "default" for the primary connection,
+	but it too can be renamed to something more relevant to your project.
 
 Query Builder
 -------------
@@ -119,8 +141,8 @@
 
 	$query_builder = TRUE;
 
-.. note:: that some CodeIgniter classes such as Sessions require Active
-	Records be enabled to access certain functionality.
+.. note:: that some CodeIgniter classes such as Sessions require Query
+	Builder to be enabled to access certain functionality.
 
 Explanation of Values:
 ----------------------
@@ -128,11 +150,12 @@
 ======================  ==================================================================================================
  Name Config             Description
 ======================  ==================================================================================================
-**hostname** 		The hostname of your database server. Often this is "localhost".
+**dsn**			The DSN connect string (an all-in-one configuration sequence).
+**hostname** 		The hostname of your database server. Often this is 'localhost'.
 **username**		The username used to connect to the database.
 **password**		The password used to connect to the database.
 **database**		The name of the database you want to connect to.
-**dbdriver**		The database type. ie: mysql, postgre, odbc, etc. Must be specified in lower case.
+**dbdriver**		The database type. ie: mysqli, postgre, odbc, etc. Must be specified in lower case.
 **dbprefix**		An optional table prefix which will added to the table name when running :doc:
 			`Query Builder <query_builder>` queries. This permits multiple CodeIgniter installations
 			to share one database.
@@ -144,14 +167,7 @@
 **char_set**		The character set used in communicating with the database.
 **dbcollat**		The character collation used in communicating with the database
 
-			.. note:: For MySQL and MySQLi databases, this setting is only used
-				as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
-				(and in table creation queries made with DB Forge). There is an
-				incompatibility in PHP with mysql_real_escape_string() which can
-				make your site vulnerable to SQL injection if you are using a
-				multi-byte character set and are running versions lower than these.
-				Sites using Latin-1 or UTF-8 database character set and collation are
-				unaffected.
+			.. note:: Only used in the 'mysql' and 'mysqli' drivers.
 
 **swap_pre**		A default table prefix that should be swapped with dbprefix. This is useful for distributed
 			applications where you might run manually written queries, and need the prefix to still be
@@ -163,11 +179,11 @@
 **port**		The database port number. To use this value you have to add a line to the database config array.
 			::
 			
-				$db['default']['port'] =  5432;
+				$db['default']['port'] = 5432;
 ======================  ==================================================================================================
 
 .. note:: Depending on what database platform you are using (MySQL, PostgreSQL,
 	etc.) not all values will be needed. For example, when using SQLite you
 	will not need to supply a username or password, and the database name
 	will be the path to your database file. The information above assumes
-	you are using MySQL.
+	you are using MySQL.
\ No newline at end of file
diff --git a/user_guide_src/source/database/connecting.rst b/user_guide_src/source/database/connecting.rst
index 5822ca6..9b81170 100644
--- a/user_guide_src/source/database/connecting.rst
+++ b/user_guide_src/source/database/connecting.rst
@@ -57,25 +57,28 @@
 To connect manually to a desired database you can pass an array of
 values::
 
-	$config['hostname'] = "localhost"; 
-	$config['username'] = "myusername"; 
-	$config['password'] = "mypassword"; 
-	$config['database'] = "mydatabase"; 
-	$config['dbdriver'] = "mysql"; 
-	$config['dbprefix'] = ""; 
-	$config['pconnect'] = FALSE; 
-	$config['db_debug'] = TRUE; 
-	$config['cache_on'] = FALSE; 
-	$config['cachedir'] = ""; 
-	$config['char_set'] = "utf8"; 
-	$config['dbcollat'] = "utf8_general_ci";  
+	$config['hostname'] = 'localhost';
+	$config['username'] = 'myusername';
+	$config['password'] = 'mypassword';
+	$config['database'] = 'mydatabase';
+	$config['dbdriver'] = 'mysqli';
+	$config['dbprefix'] = '';
+	$config['pconnect'] = FALSE;
+	$config['db_debug'] = TRUE;
+	$config['cache_on'] = FALSE;
+	$config['cachedir'] = '';
+	$config['char_set'] = 'utf8';
+	$config['dbcollat'] = 'utf8_general_ci';
 	$this->load->database($config);
 
 For information on each of these values please see the :doc:`configuration
 page <configuration>`.
 
-.. note:: For the PDO driver, $config['hostname'] should look like
-	this: 'mysql:host=localhost'
+.. note:: For the PDO driver, you should use the $config['dsn'] setting
+	instead of 'hostname' and 'database':
+
+	|
+	| $config['dsn'] = 'mysql:host=localhost;dbname=mydatabase';
 
 Or you can submit your database values as a Data Source Name. DSNs must
 have this prototype::
@@ -149,5 +152,4 @@
 
 ::
 
-	$this->db->close();
-
+	$this->db->close();
\ No newline at end of file
diff --git a/user_guide_src/source/general/models.rst b/user_guide_src/source/general/models.rst
index 2e1e025..4e52a96 100644
--- a/user_guide_src/source/general/models.rst
+++ b/user_guide_src/source/general/models.rst
@@ -72,10 +72,11 @@
 
 	class Model_name extends CI_Model {
 
-	    function __construct()
-	    {
-	        parent::__construct();
-	    }
+		public function __construct()
+		{
+			parent::__construct();
+		}
+
 	}
 
 Where **Model_name** is the name of your class. Class names **must** have
@@ -87,10 +88,11 @@
 
 	class User_model extends CI_Model {
 
-	    function __construct()
-	    {
-	        parent::__construct();
-	    }
+		public function __construct()
+		{
+			parent::__construct();
+		}
+
 	}
 
 Your file will be this::
@@ -102,7 +104,7 @@
 
 Your models will typically be loaded and called from within your
 :doc:`controller <controllers>` functions. To load a model you will use
-the following function::
+the following method::
 
 	$this->load->model('model_name');
 
@@ -112,33 +114,34 @@
 
 	$this->load->model('blog/queries');
 
-Once loaded, you will access your model functions using an object with
-the same name as your class::
+Once loaded, you will access your model methods using an object with the
+same name as your class::
 
 	$this->load->model('model_name');
 
-	$this->model_name->function();
+	$this->model_name->method();
 
 If you would like your model assigned to a different object name you can
-specify it via the second parameter of the loading function::
+specify it via the second parameter of the loading method::
 
-	$this->load->model('model_name', 'fubar');
+	$this->load->model('model_name', 'foobar');
 
-	$this->fubar->function();
+	$this->foobar->method();
 
 Here is an example of a controller, that loads a model, then serves a
 view::
 
 	class Blog_controller extends CI_Controller {
 
-	    function blog()
-	    {
-	        $this->load->model('blog');
+		public function blog()
+		{
+			$this->load->model('blog');
 
-	        $data['query'] = $this->Blog->get_last_ten_entries();
+			$data['query'] = $this->Blog->get_last_ten_entries();
 
-	        $this->load->view('blog', $data);
-	    }
+			$this->load->view('blog', $data);
+		}
+
 	}
 	
 
@@ -170,15 +173,13 @@
 -  You can manually pass database connectivity settings via the third
    parameter::
 
-	$config['hostname'] = "localhost";
-	$config['username'] = "myusername";
-	$config['password'] = "mypassword";
-	$config['database'] = "mydatabase";
-	$config['dbdriver'] = "mysql";
-	$config['dbprefix'] = "";
+	$config['hostname'] = 'localhost';
+	$config['username'] = 'myusername';
+	$config['password'] = 'mypassword';
+	$config['database'] = 'mydatabase';
+	$config['dbdriver'] = 'mysqli';
+	$config['dbprefix'] = '';
 	$config['pconnect'] = FALSE;
 	$config['db_debug'] = TRUE;
 
-	$this->load->model('Model_name', '', $config);
-
-
+	$this->load->model('Model_name', '', $config);
\ No newline at end of file
diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst
index 5adfb18..e332a91 100644
--- a/user_guide_src/source/helpers/date_helper.rst
+++ b/user_guide_src/source/helpers/date_helper.rst
@@ -40,7 +40,7 @@
 mdate()
 =======
 
-This function is identical to PHPs `date() <http://www.php.net/date>`_
+This function is identical to PHP's `date() <http://www.php.net/date>`_
 function, except that it lets you use MySQL style date codes, where each
 code letter is preceded with a percent sign: %Y %m %d etc.
 
@@ -85,21 +85,28 @@
 The first parameter must contain the format, the second parameter must
 contain the date as a Unix timestamp.
 
+.. note:: This function is DEPRECATED. Use the native ``date()`` combined
+	with `DateTime's format constants <http://www.php.net/manual/en/class.datetime.php#datetime.constants.types>`_
+	instead:
+
+	|
+	| echo date(DATE_RFC822, time());
+
 Supported formats:
 
 ===============	=======================	======================================
 Constant		Description				Example
 ===============	=======================	======================================
-DATE_ATOM		Atom					2005-08-15T16:13:03+0000
-DATE_COOKIE		HTTP Cookies			Sun, 14 Aug 2005 16:13:03 UTC
-DATE_ISO8601   	ISO-8601				2005-08-14T16:13:03+00:00
-DATE_RFC822		RFC 822					Sun, 14 Aug 05 16:13:03 UTC
-DATE_RFC850		RFC 850					Sunday, 14-Aug-05 16:13:03 UTC
-DATE_RFC1036	RFC 1036				Sunday, 14-Aug-05 16:13:03 UTC
-DATE_RFC1123	RFC 1123				Sun, 14 Aug 2005 16:13:03 UTC
-DATE_RFC2822 	RFC 2822				Sun, 14 Aug 2005 16:13:03 +0000
-DATE_RSS		RSS						Sun, 14 Aug 2005 16:13:03 UTC
-DATE_W3C		W3C						2005-08-14T16:13:03+0000
+DATE_ATOM	Atom			2005-08-15T16:13:03+0000
+DATE_COOKIE	HTTP Cookies		Sun, 14 Aug 2005 16:13:03 UTC
+DATE_ISO8601   	ISO-8601		2005-08-14T16:13:03+00:00
+DATE_RFC822	RFC 822			Sun, 14 Aug 05 16:13:03 UTC
+DATE_RFC850	RFC 850			Sunday, 14-Aug-05 16:13:03 UTC
+DATE_RFC1036	RFC 1036		Sunday, 14-Aug-05 16:13:03 UTC
+DATE_RFC1123	RFC 1123		Sun, 14 Aug 2005 16:13:03 UTC
+DATE_RFC2822 	RFC 2822		Sun, 14 Aug 2005 16:13:03 +0000
+DATE_RSS	RSS			Sun, 14 Aug 2005 16:13:03 UTC
+DATE_W3C	W3C			2005-08-14T16:13:03+0000
 ===============	=======================	======================================
 
 local_to_gmt()
diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst
index a110f3c..fa7b3db 100644
--- a/user_guide_src/source/helpers/form_helper.rst
+++ b/user_guide_src/source/helpers/form_helper.rst
@@ -543,3 +543,7 @@
 	<input type="radio" name="myradio" value="1" <?php echo  set_radio('myradio', '1', TRUE); ?> />
 	<input type="radio" name="myradio" value="2" <?php echo  set_radio('myradio', '2'); ?> />
 
+.. note:: If you are using the Form Validation class, you must always specify a rule for your field,
+	even if empty, in order for the set_*() functions to work. This is because if a Form Validation object
+	is defined, the control for set_*() is handed over to a method of the class instead of the generic helper
+	function.
\ No newline at end of file
diff --git a/user_guide_src/source/index.rst b/user_guide_src/source/index.rst
index c89b41c..e42425b 100644
--- a/user_guide_src/source/index.rst
+++ b/user_guide_src/source/index.rst
@@ -91,6 +91,17 @@
 	
 	helpers/index
 
+***************************
+Contributing to CodeIgniter
+***************************
+
+.. toctree::
+	:glob:
+	:titlesonly:
+	
+	contributing/index
+	DCO
+
 .. toctree::
 	:glob:
 	:titlesonly:
@@ -99,6 +110,7 @@
 	*
 	overview/index
 	general/requirements
+	general/welcome
 	installation/index
 	general/index
 	libraries/index
diff --git a/user_guide_src/source/installation/upgrade_210.rst b/user_guide_src/source/installation/upgrade_210.rst
index 647537d..5874bfc 100644
--- a/user_guide_src/source/installation/upgrade_210.rst
+++ b/user_guide_src/source/installation/upgrade_210.rst
@@ -8,9 +8,7 @@
 Step 1: Update your CodeIgniter files
 =====================================
 
-Replace all files and directories in your "system" folder and replace
-your index.php file. If any modifications were made to your index.php
-they will need to be made fresh in this new one.
+Replace all files and directories in your "system" folder.
 
 .. note:: If you have any custom developed files in these folders please
 	make copies of them first.
@@ -19,4 +17,10 @@
 ======================================
 
 This config file has been updated to contain more user agent types,
-please copy it to _application/config/user_agents.php*.
\ No newline at end of file
+please copy it to _application/config/user_agents.php*.
+
+Step 3: Update your user guide
+==============================
+
+Please also replace your local copy of the user guide with the new
+version.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_211.rst b/user_guide_src/source/installation/upgrade_211.rst
index 80248a7..59faca8 100644
--- a/user_guide_src/source/installation/upgrade_211.rst
+++ b/user_guide_src/source/installation/upgrade_211.rst
@@ -30,4 +30,4 @@
 
 ::
 
-	ALTER TABLE ci_sessions CHANGE ip_address ip_address varchar(45) default '0' NOT NULL
+	ALTER TABLE ci_sessions CHANGE ip_address ip_address varchar(45) default '0' NOT NULL
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_212.rst b/user_guide_src/source/installation/upgrade_212.rst
index cdbf977..205ad86 100644
--- a/user_guide_src/source/installation/upgrade_212.rst
+++ b/user_guide_src/source/installation/upgrade_212.rst
@@ -13,4 +13,10 @@
 they will need to be made fresh in this new one.
 
 .. note:: If you have any custom developed files in these folders please
-	make copies of them first.
\ No newline at end of file
+	make copies of them first.
+
+Step 2: Update your user guide
+==============================
+
+Please also replace your local copy of the user guide with the new
+version.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 7b0d8ab..f304a71 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -102,6 +102,32 @@
 .. note:: This function is still available, but you're strongly encouraged to remove it's usage sooner
 	rather than later.
 
+Date helper standard_date()
+===========================
+
+:doc:`Date Helper <../helpers/date_helper>` function ``standard_date()`` is being deprecated due
+to the availability of native PHP `constants <http://www.php.net/manual/en/class.datetime.php#datetime.constants.types>`_,
+which when combined with ``date()`` provide the same functionality. Furthermore, they have the
+exact same names as the ones supported by ``standard_date()``. Here are examples of how to replace
+it's usage:
+
+::
+
+	// Old way
+	standard_date(); // defaults to standard_date('DATE_RFC822', now());
+
+	// Replacement
+	date(DATE_RFC822, now());
+
+	// Old way
+	standard_date('DATE_ATOM', $time);
+
+	// Replacement
+	date(DATE_ATOM, $time);
+
+.. note:: This function is still available, but you're strongly encouraged to remove its' usage sooner
+	rather than later as it is scheduled for removal in CodeIgniter 3.1+.
+
 Pagination library 'anchor_class' setting
 =========================================
 
@@ -111,5 +137,5 @@
 As a result of that, the 'anchor_class' setting is now deprecated and scheduled for removal in
 CodeIgniter 3.1+.
 
-.. note:: This setting is still available, but you're strongly encouraged to remove it's usage sooner
+.. note:: This setting is still available, but you're strongly encouraged to remove its' usage sooner
 	rather than later.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst
index 3609e42..545f344 100644
--- a/user_guide_src/source/installation/upgrading.rst
+++ b/user_guide_src/source/installation/upgrading.rst
@@ -8,6 +8,7 @@
 -  :doc:`Upgrading from 2.1.2 to 3.0.0 <upgrade_300>`
 -  :doc:`Upgrading from 2.1.1 to 2.1.2 <upgrade_212>`
 -  :doc:`Upgrading from 2.1.0 to 2.1.1 <upgrade_211>`
+-  :doc:`Upgrading from 2.0.3 to 2.1.0 <upgrade_210>`
 -  :doc:`Upgrading from 2.0.2 to 2.0.3 <upgrade_203>`
 -  :doc:`Upgrading from 2.0.1 to 2.0.2 <upgrade_202>`
 -  :doc:`Upgrading from 2.0 to 2.0.1 <upgrade_201>`
diff --git a/user_guide_src/source/libraries/config.rst b/user_guide_src/source/libraries/config.rst
index 6948963..08d9c29 100644
--- a/user_guide_src/source/libraries/config.rst
+++ b/user_guide_src/source/libraries/config.rst
@@ -175,7 +175,7 @@
 as to a stylesheet or image.
 
 The two functions above are normally accessed via the corresponding
-functions in the :doc:`URL Helper <helpers/url_helper>`.
+functions in the :doc:`URL Helper </helpers/url_helper>`.
 
 $this->config->system_url();
 *****************************
diff --git a/license_afl.rst b/user_guide_src/source/license_afl.rst
similarity index 100%
rename from license_afl.rst
rename to user_guide_src/source/license_afl.rst