Merge branch 'develop' of git://github.com/EllisLab/CodeIgniter into email
diff --git a/.travis.yml b/.travis.yml
index 97ea042..31b74b1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,16 +13,14 @@
   - DB=pdo/sqlite
 
 before_script:
-  - pyrus channel-discover pear.php-tools.net
-  - pyrus install http://pear.php-tools.net/get/vfsStream-0.11.2.tgz
-  - phpenv rehash
+  - curl -s http://getcomposer.org/installer | php
+  - php composer.phar install
   - sh -c "if [ '$DB' = 'pgsql' ] || [ '$DB' = 'pdo/pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS ci_test;' -U postgres; fi"
   - sh -c "if [ '$DB' = 'pgsql' ] || [ '$DB' = 'pdo/pgsql' ]; then psql -c 'create database ci_test;' -U postgres; fi"
   - sh -c "if [ '$DB' = 'mysql' ] || [ '$DB' = 'pdo/mysql' ]; then mysql -e 'create database IF NOT EXISTS ci_test;'; fi"
 
-script: phpunit --configuration tests/travis/$DB.phpunit.xml
+script: phpunit --coverage-text --configuration tests/travis/$DB.phpunit.xml
 
 branches:
   only:
-    - develop
-    - master
\ No newline at end of file
+    - develop
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..fa6dc02
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,5 @@
+{
+    "require": {
+        "mikey179/vfsStream": "*"
+    }
+}
\ No newline at end of file
diff --git a/system/core/Lang.php b/system/core/Lang.php
index 5cb0cad..73c9127 100755
--- a/system/core/Lang.php
+++ b/system/core/Lang.php
@@ -65,14 +65,14 @@
 	/**
 	 * Load a language file
 	 *
-	 * @param	mixed	the name of the language file to be loaded. Can be an array
+	 * @param	mixed	the name of the language file to be loaded
 	 * @param	string	the language (english, etc.)
 	 * @param	bool	return loaded array of translations
 	 * @param 	bool	add suffix to $langfile
 	 * @param 	string	alternative path to look for language file
 	 * @return	mixed
 	 */
-	public function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')
+	public function load($langfile, $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')
 	{
 		$langfile = str_replace('.php', '', $langfile);
 
diff --git a/system/core/Security.php b/system/core/Security.php
index f953011..9b7ba57 100755
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -191,6 +191,7 @@
 	 * Set Cross Site Request Forgery Protection Cookie
 	 *
 	 * @return	object
+	 * @codeCoverageIgnore
 	 */
 	public function csrf_set_cookie()
 	{
diff --git a/system/database/DB.php b/system/database/DB.php
index b28439b..1fe44c0 100755
--- a/system/database/DB.php
+++ b/system/database/DB.php
@@ -118,6 +118,13 @@
 	{
 		$query_builder = $query_builder_override;
 	}
+	// Backwards compatibility work-around for keeping the
+	// $active_record config variable working. Should be
+	// removed in v3.1
+	elseif ( ! isset($query_builder) && isset($active_record))
+	{
+		$query_builder = $active_record;
+	}
 
 	require_once(BASEPATH.'database/DB_driver.php');
 
@@ -159,4 +166,4 @@
 }
 
 /* End of file DB.php */
-/* Location: ./system/database/DB.php */
+/* Location: ./system/database/DB.php */
\ No newline at end of file
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index a0812d4..bbb7b7a 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -742,6 +742,35 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * "Count All" query
+	 *
+	 * Generates a platform-specific query string that counts all records in
+	 * the specified database
+	 *
+	 * @param	string
+	 * @return	int
+	 */
+	public function count_all($table = '')
+	{
+		if ($table == '')
+		{
+			return 0;
+		}
+
+		$query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
+		if ($query->num_rows() == 0)
+		{
+			return 0;
+		}
+
+		$query = $query->row();
+		$this->_reset_select();
+		return (int) $query->numrows;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Returns an array of table names
 	 *
 	 * @return	array
@@ -1395,8 +1424,7 @@
 
 	/**
 	 * Dummy method that allows Query Builder class to be disabled
-	 *
-	 * This function is used extensively by every db driver.
+	 * and keep count_all() working.
 	 *
 	 * @return	void
 	 */
@@ -1404,23 +1432,6 @@
 	{
 	}
 
-	// --------------------------------------------------------------------
-
-	/**
-	 * Destructor
-	 *
-	 * Closes the database connection, if needed.
-	 *
-	 * @return	void
-	 */
-	public function __destruct()
-	{
-		if ( ! $this->pconnect)
-		{
-			$this->close();
-		}
-	}
-
 }
 
 /* End of file DB_driver.php */
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index d0af66d..cee4354 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -211,8 +211,8 @@
 			$alias = $this->_create_alias_from_table(trim($select));
 		}
 
-		$sql = $this->protect_identifiers($type.'('.trim($select).')').' AS '.$this->protect_identifiers(trim($alias));
-		
+		$sql = $this->protect_identifiers($type.'('.trim($select).')').' AS '.$this->escape_identifiers(trim($alias));
+
 		$this->qb_select[] = $sql;
 		$this->qb_no_escape[] = NULL;
 
@@ -256,7 +256,7 @@
 	 */
 	public function distinct($val = TRUE)
 	{
-		$this->qb_distinct = (is_bool($val)) ? $val : TRUE;
+		$this->qb_distinct = is_bool($val) ? $val : TRUE;
 		return $this;
 	}
 
@@ -272,7 +272,7 @@
 	 */
 	public function from($from)
 	{
-		foreach ((array)$from as $val)
+		foreach ((array) $from as $val)
 		{
 			if (strpos($val, ',') !== FALSE)
 			{
@@ -1111,6 +1111,8 @@
 		return $result;
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * "Count All Results" query
 	 *
@@ -1139,6 +1141,7 @@
 		$row = $result->row();
 		return (int) $row->numrows;
 	}
+
 	// --------------------------------------------------------------------
 
 	/**
@@ -1401,17 +1404,14 @@
 			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
 		}
 
-		if ($table == '')
-		{
-			if ( ! isset($this->qb_from[0]))
-			{
-				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
-			}
-		}
-		else
+		if ($table != '')
 		{
 			$this->qb_from[0] = $table;
 		}
+		elseif ( ! isset($this->qb_from[0]))
+		{
+			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+		}
 
 		return TRUE;
 	}
@@ -1600,17 +1600,14 @@
 			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
 		}
 
-		if ($table == '')
-		{
-			if ( ! isset($this->qb_from[0]))
-			{
-				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
-			}
-		}
-		else
+		if ($table != '')
 		{
 			$this->qb_from[0] = $table;
 		}
+		elseif ( ! isset($this->qb_from[0]))
+		{
+			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+		}
 
 		return TRUE;
 	}
@@ -1696,15 +1693,11 @@
 				{
 					$index_set = TRUE;
 				}
-				else
-				{
-					$not[] = $k.'-'.$v;
-				}
 
 				$clean[$this->protect_identifiers($k2)] = ($escape === FALSE) ? $v2 : $this->escape($v2);
 			}
 
-			if ($index_set == FALSE)
+			if ($index_set === FALSE)
 			{
 				return $this->display_error('db_batch_missing_index');
 			}
@@ -2102,7 +2095,7 @@
 	 * @param	object
 	 * @return	array
 	 */
-	public function _object_to_array($object)
+	protected function _object_to_array($object)
 	{
 		if ( ! is_object($object))
 		{
@@ -2132,7 +2125,7 @@
 	 * @param	object
 	 * @return	array
 	 */
-	public function _object_to_array_batch($object)
+	protected function _object_to_array_batch($object)
 	{
 		if ( ! is_object($object))
 		{
diff --git a/system/database/DB_result.php b/system/database/DB_result.php
index 690734b..334e08c 100644
--- a/system/database/DB_result.php
+++ b/system/database/DB_result.php
@@ -242,7 +242,7 @@
 		$result = $this->custom_result_object($type);
 		if (count($result) === 0)
 		{
-			return $result;
+			return NULL;
 		}
 
 		if ($n != $this->current_row && isset($result[$n]))
@@ -253,6 +253,8 @@
 		return $result[$this->current_row];
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * Returns a single result row - object version
 	 *
@@ -263,7 +265,7 @@
 		$result = $this->result_object();
 		if (count($result) === 0)
 		{
-			return $result;
+			return NULL;
 		}
 
 		if ($n != $this->current_row && isset($result[$n]))
@@ -286,7 +288,7 @@
 		$result = $this->result_array();
 		if (count($result) === 0)
 		{
-			return $result;
+			return NULL;
 		}
 
 		if ($n != $this->current_row && isset($result[$n]))
@@ -307,7 +309,7 @@
 	public function first_row($type = 'object')
 	{
 		$result = $this->result($type);
-		return (count($result) === 0) ? $result : $result[0];
+		return (count($result) === 0) ? NULL : $result[0];
 	}
 
 	// --------------------------------------------------------------------
@@ -320,7 +322,7 @@
 	public function last_row($type = 'object')
 	{
 		$result = $this->result($type);
-		return (count($result) === 0) ? $result : $result[count($result) - 1];
+		return (count($result) === 0) ? NULL : $result[count($result) - 1];
 	}
 
 	// --------------------------------------------------------------------
@@ -335,7 +337,7 @@
 		$result = $this->result($type);
 		if (count($result) === 0)
 		{
-			return $result;
+			return NULL;
 		}
 
 		if (isset($result[$this->current_row + 1]))
@@ -358,7 +360,7 @@
 		$result = $this->result($type);
 		if (count($result) === 0)
 		{
-			return $result;
+			return NULL;
 		}
 
 		if (isset($result[$this->current_row - 1]))
diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php
index 587dfdc..cb97ff4 100644
--- a/system/database/DB_utility.php
+++ b/system/database/DB_utility.php
@@ -212,7 +212,7 @@
 		$out = rtrim($out).$newline;
 
 		// Next blast through the result array and build out the rows
-		foreach ($query->result_array() as $row)
+		while ($row = $query->unbuffered_row('array'))
 		{
 			foreach ($row as $item)
 			{
@@ -258,7 +258,7 @@
 
 		// Generate the result
 		$xml = '<'.$root.'>'.$newline;
-		foreach ($query->result_array() as $row)
+		while ($row = $query->unbuffered_row())
 		{
 			$xml .= $tab.'<'.$element.'>'.$newline;
 			foreach ($row as $key => $val)
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index 944df99..817dfdc 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -329,35 +329,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified table
-	 *
-	 * @param	string
-	 * @return	int
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$query = $query->row();
-		$this->_reset_select();
-		return (int) $query->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/interbase/interbase_driver.php b/system/database/drivers/interbase/interbase_driver.php
index c457f63..49d3cda 100644
--- a/system/database/drivers/interbase/interbase_driver.php
+++ b/system/database/drivers/interbase/interbase_driver.php
@@ -244,35 +244,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$query = $query->row();
-		$this->_reset_select();
-		return (int) $query->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index 914de49..342ff26 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -304,35 +304,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$row = $query->row();
-		$this->_reset_select();
-		return (int) $row->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php
index 8f8e7c5..bbf2d96 100644
--- a/system/database/drivers/mssql/mssql_forge.php
+++ b/system/database/drivers/mssql/mssql_forge.php
@@ -48,16 +48,13 @@
 	 */
 	protected function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
 	{
-		$sql = 'CREATE TABLE ';
+		$sql = ($if_not_exists === TRUE)
+			? "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'".$table."') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\n"
+			: '';
 
-		if ($if_not_exists === TRUE)
-		{
-			$sql .= 'IF NOT EXISTS ';
-		}
+		$sql .= 'CREATE TABLE '.$this->db->escape_identifiers($table).' (';
 
-		$sql .= $this->db->escape_identifiers($table).' (';
 		$current_field_count = 0;
-
 		foreach ($fields as $field => $attributes)
 		{
 			// Numeric field names aren't allowed in databases, so if the key is
@@ -65,15 +62,13 @@
 			// entered the field information, so we'll simply add it to the list
 			if (is_numeric($field))
 			{
-				$sql .= "\n\t$attributes";
+				$sql .= "\n\t".$attributes;
 			}
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
-
-				$sql .=  ' '.$attributes['TYPE'];
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
 				if (array_key_exists('CONSTRAINT', $attributes))
 				{
@@ -115,7 +110,7 @@
 		if (count($primary_keys) > 0)
 		{
 			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
@@ -131,13 +126,11 @@
 					$key = array($this->db->protect_identifiers($key));
 				}
 
-				$sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
+				$sql .= ",\n\tFOREIGN KEY (".implode(', ', $key).')';
 			}
 		}
 
-		$sql .= "\n)";
-
-		return $sql;
+		return $sql."\n)";
 	}
 
 	// --------------------------------------------------------------------
@@ -167,21 +160,14 @@
 			return $sql;
 		}
 
-		$sql .= " $column_definition";
+		$sql .= " ".$column_definition;
 
 		if ($default_value != '')
 		{
-			$sql .= " DEFAULT \"$default_value\"";
+			$sql .= " DEFAULT '".$default_value."'";
 		}
 
-		if ($null === NULL)
-		{
-			$sql .= ' NULL';
-		}
-		else
-		{
-			$sql .= ' NOT NULL';
-		}
+		$sql .= ($null === NULL) ? ' NULL' : ' NOT NULL';
 
 		if ($after_field != '')
 		{
@@ -189,7 +175,6 @@
 		}
 
 		return $sql;
-
 	}
 
 }
diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php
index 161f995..7a1a7b9 100644
--- a/system/database/drivers/mysql/mysql_driver.php
+++ b/system/database/drivers/mysql/mysql_driver.php
@@ -47,7 +47,7 @@
 
 	// clause and character used for LIKE escape sequences - not used in MySQL
 	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '';
+	protected $_like_escape_chr = '\\';
 
 	/**
 	 * The syntax to count rows is slightly different across different
@@ -291,7 +291,9 @@
 		// escape LIKE condition wildcards
 		if ($like === TRUE)
 		{
-			return str_replace(array('%', '_'), array('\\%', '\\_'), $str);
+			return str_replace(array($this->_like_escape_chr, '%', '_'),
+						array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
+						$str);
 		}
 
 		return $str;
@@ -324,35 +326,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$query = $query->row();
-		$this->_reset_select();
-		return (int) $query->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php
index 9261883..dd544f6 100644
--- a/system/database/drivers/mysqli/mysqli_driver.php
+++ b/system/database/drivers/mysqli/mysqli_driver.php
@@ -47,7 +47,7 @@
 
 	// clause and character used for LIKE escape sequences - not used in MySQL
 	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '';
+	protected $_like_escape_chr = '\\';
 
 	/**
 	 * The syntax to count rows is slightly different across different
@@ -291,7 +291,9 @@
 		// escape LIKE condition wildcards
 		if ($like === TRUE)
 		{
-			return str_replace(array('%', '_'), array('\\%', '\\_'), $str);
+			return str_replace(array($this->_like_escape_chr, '%', '_'),
+						array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
+						$str);
 		}
 
 		return $str;
@@ -324,35 +326,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$query = $query->row();
-		$this->_reset_select();
-		return (int) $query->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index e2fa513..b979c8a 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -455,35 +455,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	int
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query == FALSE)
-		{
-			return 0;
-		}
-
-		$row = $query->row();
-		$this->_reset_select();
-		return (int) $row->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php
index 7b05e0a..6fb6c81 100644
--- a/system/database/drivers/oci8/oci8_result.php
+++ b/system/database/drivers/oci8/oci8_result.php
@@ -419,7 +419,7 @@
 				OR $n < $this->current_row)
 			{
 				// No such row exists
-				return array();
+				return NULL;
 			}
 
 			// Get the next row index that would actually need to be fetched
@@ -460,7 +460,7 @@
 				$this->num_rows = 0;
 			}
 
-			return array();
+			return NULL;
 		}
 
 		$this->current_row = $n;
@@ -507,7 +507,7 @@
 			return (object) $row;
 		}
 
-		return array();
+		return NULL;
 	}
 
 	// --------------------------------------------------------------------
@@ -539,19 +539,19 @@
 			}
 			else
 			{
-				return array();
+				return NULL;
 			}
 		}
 		elseif ( ! class_exists($class_name)) // No such class exists
 		{
-			return array();
+			return NULL;
 		}
 
 		$row = $this->row_array($n);
-		// An array would mean that the row doesn't exist
-		if (is_array($row))
+		// A non-array would mean that the row doesn't exist
+		if ( ! is_array($row))
 		{
-			return $row;
+			return NULL;
 		}
 
 		// Convert to the desired class and return
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index e317211..98fd806 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -245,36 +245,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string . $this->protect_identifiers('numrows') . " FROM " . $this->protect_identifiers($table, TRUE, NULL, FALSE));
-
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$row = $query->row();
-		$this->_reset_select();
-		return (int) $row->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index e38c114..ec7f3e1 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -79,13 +79,13 @@
 
 		// clause and character used for LIKE escape sequences
 		// this one depends on the driver being used
-		if ($this->pdodriver == 'mysql')
+		if ($this->pdodriver === 'mysql')
 		{
 			$this->_escape_char = '`';
 			$this->_like_escape_str = '';
-			$this->_like_escape_chr = '';
+			$this->_like_escape_chr = '\\';
 		}
-		elseif ($this->pdodriver == 'odbc')
+		elseif ($this->pdodriver === 'odbc')
 		{
 			$this->_like_escape_str = " {escape '%s'} ";
 		}
@@ -409,38 +409,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$sql = $this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
-		$query = $this->query($sql);
-
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$row = $query->row();
-		$this->_reset_select();
-
-		return (int) $row->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index 0ddfd0a..c2a1884 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -390,35 +390,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$query = $query->row();
-		$this->_reset_select();
-		return (int) $query->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
@@ -539,6 +510,47 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * 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
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index d710b94..d8b869c 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -268,35 +268,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query($this->_count_string.$this->protect_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$row = $query->row();
-		$this->_reset_select();
-		return (int) $row->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
index ad2848e..ea4cf2d 100644
--- a/system/database/drivers/sqlite3/sqlite3_driver.php
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -245,30 +245,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	int
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$result = $this->conn_id->querySingle($this->_count_string.$this->protect_identifiers('numrows')
-							.' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-
-		return empty($result) ? 0 : (int) $result;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/sqlite3/sqlite3_result.php b/system/database/drivers/sqlite3/sqlite3_result.php
index d83d6b2..946b365 100644
--- a/system/database/drivers/sqlite3/sqlite3_result.php
+++ b/system/database/drivers/sqlite3/sqlite3_result.php
@@ -386,7 +386,7 @@
 				OR count($this->result_array) > 0 OR $n < $this->current_row)
 			{
 				// No such row exists
-				return array();
+				return NULL;
 			}
 
 			// Get the next row index that would actually need to be fetched
@@ -427,7 +427,7 @@
 				$this->num_rows = 0;
 			}
 
-			return array();
+			return NULL;
 		}
 
 		$this->current_row = $n;
@@ -469,7 +469,7 @@
 			return (object) $row;
 		}
 
-		return array();
+		return NULL;
 	}
 
 	// --------------------------------------------------------------------
@@ -501,19 +501,19 @@
 			}
 			else
 			{
-				return array();
+				return NULL;
 			}
 		}
 		elseif ( ! class_exists($class_name)) // No such class exists
 		{
-			return array();
+			return NULL;
 		}
 
 		$row = $this->row_array($n);
-		// An array would mean that the row doesn't exist
-		if (is_array($row))
+		// A non-array would mean that the row doesn't exist
+		if ( ! is_array($row))
 		{
-			return $row;
+			return NULL;
 		}
 
 		// Convert to the desired class and return
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 3e9fa7b..961066d 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -132,11 +132,9 @@
 	 */
 	protected function _execute($sql)
 	{
-		return sqlsrv_query($this->conn_id,
-					$sql,
-					NULL,
-					array('Scrollable'=> SQLSRV_CURSOR_STATIC, 'SendStreamParamsAtExec' => TRUE)
-			);
+		return (is_write_type($sql) && stripos($sql, 'INSERT') === FALSE)
+			? sqlsrv_query($this->conn_id, $sql)
+			: sqlsrv_query($this->conn_id, $sql, NULL, array('Scrollable' => SQLSRV_CURSOR_STATIC));
 	}
 
 	// --------------------------------------------------------------------
@@ -237,7 +235,7 @@
 	 */
 	public function affected_rows()
 	{
-		return @sqlrv_rows_affected($this->conn_id);
+		return sqlrv_rows_affected($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -281,35 +279,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * "Count All" query
-	 *
-	 * Generates a platform-specific query string that counts all records in
-	 * the specified database
-	 *
-	 * @param	string
-	 * @return	int
-	 */
-	public function count_all($table = '')
-	{
-		if ($table == '')
-		{
-			return 0;
-		}
-
-		$query = $this->query("SELECT COUNT(*) AS numrows FROM " . $this->dbprefix . $table);
-		if ($query->num_rows() == 0)
-		{
-			return 0;
-		}
-
-		$row = $query->row();
-		$this->_reset_select();
-		return (int) $row->numrows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * List table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php
index e9143b2..c817c2c 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_forge.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_forge.php
@@ -48,16 +48,13 @@
 	 */
 	protected function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
 	{
-		$sql = 'CREATE TABLE ';
+		$sql = ($if_not_exists === TRUE)
+			? "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'".$table."') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\n"
+			: '';
 
-		if ($if_not_exists === TRUE)
-		{
-			$sql .= 'IF NOT EXISTS ';
-		}
+		$sql .= 'CREATE TABLE '.$this->db->escape_identifiers($table).' (';
 
-		$sql .= $this->db->escape_identifiers($table).' (';
 		$current_field_count = 0;
-
 		foreach ($fields as $field => $attributes)
 		{
 			// Numeric field names aren't allowed in databases, so if the key is
@@ -65,15 +62,13 @@
 			// entered the field information, so we'll simply add it to the list
 			if (is_numeric($field))
 			{
-				$sql .= "\n\t$attributes";
+				$sql .= "\n\t".$attributes;
 			}
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
-
-				$sql .=  ' '.$attributes['TYPE'];
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
 				if (array_key_exists('CONSTRAINT', $attributes))
 				{
@@ -115,7 +110,7 @@
 		if (count($primary_keys) > 0)
 		{
 			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
@@ -131,13 +126,11 @@
 					$key = array($this->db->protect_identifiers($key));
 				}
 
-				$sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
+				$sql .= ",\n\tFOREIGN KEY (".implode(', ', $key).')';
 			}
 		}
 
-		$sql .= "\n)";
-
-		return $sql;
+		return $sql."\n)";
 	}
 
 	// --------------------------------------------------------------------
@@ -167,21 +160,14 @@
 			return $sql;
 		}
 
-		$sql .= " $column_definition";
+		$sql .= ' '.$column_definition;
 
 		if ($default_value != '')
 		{
-			$sql .= " DEFAULT \"$default_value\"";
+			$sql .= " DEFAULT '".$default_value."'";
 		}
 
-		if ($null === NULL)
-		{
-			$sql .= ' NULL';
-		}
-		else
-		{
-			$sql .= ' NOT NULL';
-		}
+		$sql .= ($null === NULL) ? ' NULL' : ' NOT NULL';
 
 		if ($after_field != '')
 		{
@@ -189,7 +175,6 @@
 		}
 
 		return $sql;
-
 	}
 
 }
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 271c6d2..7456e92 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -747,6 +747,8 @@
 				';',
 				'?',
 				'/',
+				'!',
+				'#',
 				'%20',
 				'%22',
 				'%3c',		// <
diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php
index 9f89d1b..5216038 100644
--- a/tests/Bootstrap.php
+++ b/tests/Bootstrap.php
@@ -7,13 +7,30 @@
 $dir = realpath(dirname(__FILE__));
 
 // Path constants
-define('PROJECT_BASE',	realpath($dir.'/../').'/');
-define('BASEPATH',		PROJECT_BASE.'system/');
-define('APPPATH',		PROJECT_BASE.'application/');
-define('VIEWPATH',		PROJECT_BASE.'');
+defined('PROJECT_BASE') OR define('PROJECT_BASE', realpath($dir.'/../').'/');
+defined('BASEPATH') OR define('BASEPATH', PROJECT_BASE.'system/');
+defined('APPPATH') OR define('APPPATH', PROJECT_BASE.'application/');
+defined('VIEWPATH') OR define('VIEWPATH', PROJECT_BASE.'');
+
+// Get vfsStream either via PEAR or composer
+foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
+{
+	if (file_exists($path.DIRECTORY_SEPARATOR.'vfsStream/vfsStream.php'))
+	{
+		require_once 'vfsStream/vfsStream.php';
+		break;
+	}
+}
+
+if ( ! class_exists('vfsStream') && file_exists(PROJECT_BASE.'vendor/autoload.php'))
+{
+	include_once PROJECT_BASE.'vendor/autoload.php';
+	class_alias('org\bovigo\vfs\vfsStream', 'vfsStream');
+	class_alias('org\bovigo\vfs\vfsStreamDirectory', 'vfsStreamDirectory');
+	class_alias('org\bovigo\vfs\vfsStreamWrapper', 'vfsStreamWrapper');
+}
 
 // Prep our test environment
-require_once 'vfsStream/vfsStream.php';
 include_once $dir.'/mocks/core/common.php';
 include_once $dir.'/mocks/autoloader.php';
 spl_autoload_register('autoload');
diff --git a/tests/codeigniter/core/Security_test.php b/tests/codeigniter/core/Security_test.php
index 1796ba7..b2f8c69 100644
--- a/tests/codeigniter/core/Security_test.php
+++ b/tests/codeigniter/core/Security_test.php
@@ -70,4 +70,36 @@
 
 		$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $harmless_string);
 	}
+
+	// --------------------------------------------------------------------
+	
+	public function test_xss_hash()
+	{
+		$this->assertEmpty($this->security->xss_hash);
+
+		// Perform hash
+		$this->security->xss_hash();
+
+		$this->assertTrue(preg_match('#^[0-9a-f]{32}$#iS', $this->security->xss_hash) === 1);
+	}
+
+	// --------------------------------------------------------------------
+	
+	public function test_entity_decode()
+	{
+		$encoded = '&lt;div&gt;Hello &lt;b&gt;Booya&lt;/b&gt;&lt;/div&gt;';
+		$decoded = $this->security->entity_decode($encoded);
+
+		$this->assertEquals('<div>Hello <b>Booya</b></div>', $decoded);
+	}
+
+	// --------------------------------------------------------------------
+	
+	public function test_sanitize_filename()
+	{
+		$filename = './<!--foo-->';
+		$safe_filename = $this->security->sanitize_filename($filename);
+
+		$this->assertEquals('foo', $safe_filename);
+	}
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/DB_driver_test.php b/tests/codeigniter/database/DB_driver_test.php
index fb40f06..9e16e29 100644
--- a/tests/codeigniter/database/DB_driver_test.php
+++ b/tests/codeigniter/database/DB_driver_test.php
@@ -2,8 +2,6 @@
 
 class DB_driver_test extends CI_TestCase {
 
-	// ------------------------------------------------------------------------
-
 	public function test_initialize()
 	{
 		$config = Mock_Database_DB::config(DB_DRIVER);
@@ -32,5 +30,5 @@
 	{
 		return new Mock_Database_Drivers_Postgre($config);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/DB_test.php b/tests/codeigniter/database/DB_test.php
index 9b93e22..d5c0dea 100644
--- a/tests/codeigniter/database/DB_test.php
+++ b/tests/codeigniter/database/DB_test.php
@@ -2,8 +2,6 @@
 
 class DB_test extends CI_TestCase {
 
-	// ------------------------------------------------------------------------
-
 	public function test_db_invalid()
 	{
 		$connection = new Mock_Database_DB(array(
@@ -45,5 +43,5 @@
 		$this->assertTrue($db instanceof CI_DB);
 		$this->assertTrue($db instanceof CI_DB_Driver);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/escape_test.php b/tests/codeigniter/database/query_builder/escape_test.php
new file mode 100644
index 0000000..5d575a3
--- /dev/null
+++ b/tests/codeigniter/database/query_builder/escape_test.php
@@ -0,0 +1,67 @@
+<?php
+
+class Escape_test extends CI_TestCase {
+
+	/**
+	 * @var object Database/Query Builder holder
+	 */
+	protected $db;
+
+	public function set_up()
+	{
+		$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);
+
+		Mock_Database_Schema_Skeleton::create_tables();
+		Mock_Database_Schema_Skeleton::create_data();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * @see ./mocks/schema/skeleton.php
+	 */
+	public function test_escape_like_percent_sign()
+	{
+		// Escape the like string
+		$string = $this->db->escape_like_str('\%foo');
+
+		if (strpos(DB_DRIVER, 'mysql') !== FALSE)
+		{
+			$sql = "SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '';";
+		}
+		else
+		{
+			$sql = 'SELECT "value" FROM "misc" WHERE "key" LIKE \''.$string.'%\' ESCAPE \'!\';';
+		}
+
+		$res = $this->db->query($sql)->result_array();
+		
+		// Check the result
+		$this->assertEquals(1, count($res));
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * @see ./mocks/schema/skeleton.php
+	 */
+	public function test_escape_like_backslash_sign()
+	{
+		// Escape the like string
+		$string = $this->db->escape_like_str('\\');
+
+		if (strpos(DB_DRIVER, 'mysql') !== FALSE)
+		{
+			$sql = "SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '';";
+		}
+		else
+		{
+			$sql = 'SELECT "value" FROM "misc" WHERE "key" LIKE \''.$string.'%\' ESCAPE \'!\';';
+		}
+
+		$res = $this->db->query($sql)->result_array();
+		
+		// Check the result
+		$this->assertEquals(2, count($res));
+	}
+}
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Table_test.php b/tests/codeigniter/libraries/Table_test.php
index 13f338c..f5133de 100644
--- a/tests/codeigniter/libraries/Table_test.php
+++ b/tests/codeigniter/libraries/Table_test.php
@@ -291,6 +291,26 @@
 		);
 	}
 	
-	// Test main generate method
-	// --------------------------------------------------------------------
+	function test_generate()
+	{
+		// Prepare the data
+		$data = array(
+			array('Name', 'Color', 'Size'),
+			array('Fred', 'Blue', 'Small'),
+			array('Mary', 'Red', 'Large'),
+			array('John', 'Green', 'Medium')	
+		);
+
+		$table = $this->table->generate($data);
+
+		// Test the table header
+		$this->assertTrue(strpos($table, '<th>Name</th>') !== FALSE);
+		$this->assertTrue(strpos($table, '<th>Color</th>') !== FALSE);
+		$this->assertTrue(strpos($table, '<th>Size</th>') !== FALSE);
+
+		// Test the first entry
+		$this->assertTrue(strpos($table, '<td>Fred</td>') !== FALSE);
+		$this->assertTrue(strpos($table, '<td>Blue</td>') !== FALSE);
+		$this->assertTrue(strpos($table, '<td>Small</td>') !== FALSE);
+	}
 }
\ No newline at end of file
diff --git a/tests/mocks/autoloader.php b/tests/mocks/autoloader.php
index 92c9bea..441c889 100644
--- a/tests/mocks/autoloader.php
+++ b/tests/mocks/autoloader.php
@@ -22,7 +22,7 @@
 	);
 
 	$ci_libraries = array(
-		'Calendar', 'Cart', 'Driver',
+		'Calendar', 'Cart', 'Driver_Library',
 		'Email', 'Encrypt', 'Form_validation',
 		'Ftp', 'Image_lib', 'Javascript',
 		'Log', 'Migration', 'Pagination',
@@ -50,7 +50,7 @@
 		elseif (in_array($subclass, $ci_libraries))
 		{
 			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR;
-			$class = $subclass;
+			$class = ($subclass == 'Driver_Library') ? 'Driver' : $subclass;
 		}
 		elseif (preg_match('/^CI_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) == 3)
 		{
diff --git a/tests/mocks/core/common.php b/tests/mocks/core/common.php
index fc94d7f..e745766 100644
--- a/tests/mocks/core/common.php
+++ b/tests/mocks/core/common.php
@@ -2,53 +2,65 @@
 
 // Set up the global CI functions in their most minimal core representation
 
-function &get_instance() 
+if ( ! function_exists('get_instance'))
 {
-	$test = CI_TestCase::instance();
-	$instance = $test->ci_instance();
-	return $instance;
+	function &get_instance() 
+	{
+		$test = CI_TestCase::instance();
+		$instance = $test->ci_instance();
+		return $instance;
+	}
 }
 
 // --------------------------------------------------------------------
 
-function &get_config() {
-	$test = CI_TestCase::instance();
-	$config = $test->ci_get_config();
+if ( ! function_exists('get_config'))
+{
+	function &get_config() {
+		$test = CI_TestCase::instance();
+		$config = $test->ci_get_config();
+			
+		return $config;
+	}
+}
+
+if ( ! function_exists('config_item'))
+{
+	function config_item($item)
+	{
+		$config =& get_config();
 		
-	return $config;
-}
-
-function config_item($item)
-{
-	$config =& get_config();
-	
-	if ( ! isset($config[$item]))
-	{
-		return FALSE;
+		if ( ! isset($config[$item]))
+		{
+			return FALSE;
+		}
+		
+		return $config[$item];
 	}
-	
-	return $config[$item];
 }
 
 // --------------------------------------------------------------------
 
-function load_class($class, $directory = 'libraries', $prefix = 'CI_')
+if ( ! function_exists('load_class'))
 {
-	if ($directory != 'core' OR $prefix != 'CI_')
+	function load_class($class, $directory = 'libraries', $prefix = 'CI_')
 	{
-		throw new Exception('Not Implemented: Non-core load_class()');
+		if ($directory != 'core' OR $prefix != 'CI_')
+		{
+			throw new Exception('Not Implemented: Non-core load_class()');
+		}
+		
+		$test = CI_TestCase::instance();
+		
+		$obj =& $test->ci_core_class($class);
+		
+		if (is_string($obj))
+		{
+			throw new Exception('Bad Isolation: Use ci_set_core_class to set '.$class.'');
+		}
+		
+		return $obj;
 	}
-	
-	$test = CI_TestCase::instance();
-	
-	$obj =& $test->ci_core_class($class);
-	
-	if (is_string($obj))
-	{
-		throw new Exception('Bad Isolation: Use ci_set_core_class to set '.$class.'');
-	}
-	
-	return $obj;
 }
 
 // This is sort of meh. Should probably be mocked up with
@@ -57,76 +69,103 @@
 // bootstrap testsuite.
 // --------------------------------------------------------------------
 
-function remove_invisible_characters($str, $url_encoded = TRUE)
+if ( ! function_exists('remove_invisible_characters'))
 {
-	$non_displayables = array();
-	
-	// every control character except newline (dec 10)
-	// carriage return (dec 13), and horizontal tab (dec 09)
-	
-	if ($url_encoded)
+	function remove_invisible_characters($str, $url_encoded = TRUE)
 	{
-		$non_displayables[] = '/%0[0-8bcef]/';	// url encoded 00-08, 11, 12, 14, 15
-		$non_displayables[] = '/%1[0-9a-f]/';	// url encoded 16-31
-	}
-	
-	$non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127
+		$non_displayables = array();
+		
+		// every control character except newline (dec 10)
+		// carriage return (dec 13), and horizontal tab (dec 09)
+		
+		if ($url_encoded)
+		{
+			$non_displayables[] = '/%0[0-8bcef]/';	// url encoded 00-08, 11, 12, 14, 15
+			$non_displayables[] = '/%1[0-9a-f]/';	// url encoded 16-31
+		}
+		
+		$non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127
 
-	do
-	{
-		$str = preg_replace($non_displayables, '', $str, -1, $count);
-	}
-	while ($count);
+		do
+		{
+			$str = preg_replace($non_displayables, '', $str, -1, $count);
+		}
+		while ($count);
 
-	return $str;
+		return $str;
+	}
 }
 
 
 // Clean up error messages
 // --------------------------------------------------------------------
 
-function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')
+if ( ! function_exists('show_error'))
 {
-	throw new RuntimeException('CI Error: '.$message);
+	function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')
+	{
+		throw new RuntimeException('CI Error: '.$message);
+	}
 }
 
-function show_404($page = '', $log_error = TRUE)
+if ( ! function_exists('show_404'))
 {
-	throw new RuntimeException('CI Error: 404');
+	function show_404($page = '', $log_error = TRUE)
+	{
+		throw new RuntimeException('CI Error: 404');
+	}
 }
 
-function _exception_handler($severity, $message, $filepath, $line)
+if ( ! function_exists('_exception_handler'))
 {
-	throw new RuntimeException('CI Exception: '.$message.' | '.$filepath.' | '.$line);
+	function _exception_handler($severity, $message, $filepath, $line)
+	{
+		throw new RuntimeException('CI Exception: '.$message.' | '.$filepath.' | '.$line);
+	}
 }
 
 
 // We assume a few things about our environment ...
 // --------------------------------------------------------------------
 
-function is_php($version = '5.0.0')
+if ( ! function_exists('is_php'))
 {
-	return ! (version_compare(PHP_VERSION, $version) < 0);
+	function is_php($version = '5.0.0')
+	{
+		return ! (version_compare(PHP_VERSION, $version) < 0);
+	}
 }
 
-function is_really_writable($file)
+if ( ! function_exists('is_really_writable'))
 {
-	return is_writable($file);
+	function is_really_writable($file)
+	{
+		return is_writable($file);
+	}
 }
 
-function is_loaded()
+if ( ! function_exists('is_loaded'))
 {
-	throw new Exception('Bad Isolation: mock up environment');
+	function is_loaded()
+	{
+		throw new Exception('Bad Isolation: mock up environment');
+	}
 }
 
-function log_message($level = 'error', $message, $php_error = FALSE)
+if ( ! function_exists('log_message'))
 {
-	return TRUE;
+	function log_message($level = 'error', $message, $php_error = FALSE)
+	{
+		return TRUE;
+	}
 }
 
-function set_status_header($code = 200, $text = '')
+if ( ! function_exists('set_status_header'))
 {
-	return TRUE;
+	function set_status_header($code = 200, $text = '')
+	{
+		return TRUE;
+	}
 }
 
 // EOF
\ No newline at end of file
diff --git a/tests/mocks/database/ci_test.sqlite b/tests/mocks/database/ci_test.sqlite
index 23a3de2..44dcef9 100755
--- a/tests/mocks/database/ci_test.sqlite
+++ b/tests/mocks/database/ci_test.sqlite
Binary files differ
diff --git a/tests/mocks/database/schema/skeleton.php b/tests/mocks/database/schema/skeleton.php
index 671336c..05499f8 100644
--- a/tests/mocks/database/schema/skeleton.php
+++ b/tests/mocks/database/schema/skeleton.php
@@ -88,6 +88,23 @@
 		));
 		static::$forge->add_key('id', TRUE);
 		static::$forge->create_table('job', (strpos(static::$driver, 'pgsql') === FALSE));
+
+		// Misc Table
+		static::$forge->add_field(array(
+			'id' => array(
+				'type' => 'INTEGER',
+				'constraint' => 3,
+			),
+			'key' => array(
+				'type' => 'VARCHAR',
+				'constraint' => 40,
+			),
+			'value' => array(
+				'type' => 'TEXT',
+			),
+		));
+		static::$forge->add_key('id', TRUE);
+		static::$forge->create_table('misc', (strpos(static::$driver, 'pgsql') === FALSE));
 	}
 
 	/**
@@ -111,6 +128,10 @@
     			array('id' => 3, 'name' => 'Accountant', 'description' => 'Boring job, but you will get free snack at lunch'),
 			    array('id' => 4, 'name' => 'Musician', 'description' => 'Only Coldplay can actually called Musician'),
 			),
+			'misc' => array(
+				array('id' => 1, 'key' => '\\xxxfoo456', 'value' => 'Entry with \\xxx'), 
+				array('id' => 2, 'key' => '\\%foo456', 'value' => 'Entry with \\%'),
+			),
 		);
 
 		foreach ($data as $table => $dummy_data) 
diff --git a/tests/travis/mysql.phpunit.xml b/tests/travis/mysql.phpunit.xml
index 1792ae3..38c8eba 100644
--- a/tests/travis/mysql.phpunit.xml
+++ b/tests/travis/mysql.phpunit.xml
@@ -17,10 +17,9 @@
 			<directory suffix="test.php">../codeigniter</directory>
 		</testsuite>
 	</testsuites>
-	<filters>
-		<blacklist>
-			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
-			<directory suffix=".php">PHP_LIBDIR</directory>
-		</blacklist>
-	</filters>
+	<filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">../../system</directory>
+        </whitelist>
+	</filter>
 </phpunit>
\ No newline at end of file
diff --git a/tests/travis/pdo/mysql.phpunit.xml b/tests/travis/pdo/mysql.phpunit.xml
index 602030d..c3113a6 100644
--- a/tests/travis/pdo/mysql.phpunit.xml
+++ b/tests/travis/pdo/mysql.phpunit.xml
@@ -17,10 +17,9 @@
 			<directory suffix="test.php">../../codeigniter</directory>
 		</testsuite>
 	</testsuites>
-	<filters>
-		<blacklist>
-			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
-			<directory suffix=".php">PHP_LIBDIR</directory>
-		</blacklist>
-	</filters>
+	<filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">../../../system</directory>
+        </whitelist>
+	</filter>
 </phpunit>
\ No newline at end of file
diff --git a/tests/travis/pdo/pgsql.phpunit.xml b/tests/travis/pdo/pgsql.phpunit.xml
index 77e1493..2320255 100644
--- a/tests/travis/pdo/pgsql.phpunit.xml
+++ b/tests/travis/pdo/pgsql.phpunit.xml
@@ -17,10 +17,9 @@
 			<directory suffix="test.php">../../codeigniter</directory>
 		</testsuite>
 	</testsuites>
-	<filters>
-		<blacklist>
-			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
-			<directory suffix=".php">PHP_LIBDIR</directory>
-		</blacklist>
-	</filters>
+	<filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">../../../system</directory>
+        </whitelist>
+	</filter>
 </phpunit>
\ No newline at end of file
diff --git a/tests/travis/pdo/sqlite.phpunit.xml b/tests/travis/pdo/sqlite.phpunit.xml
index cdccef0..3d12567 100644
--- a/tests/travis/pdo/sqlite.phpunit.xml
+++ b/tests/travis/pdo/sqlite.phpunit.xml
@@ -17,10 +17,9 @@
 			<directory suffix="test.php">../../codeigniter</directory>
 		</testsuite>
 	</testsuites>
-	<filters>
-		<blacklist>
-			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
-			<directory suffix=".php">PHP_LIBDIR</directory>
-		</blacklist>
-	</filters>
+	<filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">../../../system</directory>
+        </whitelist>
+	</filter>
 </phpunit>
\ No newline at end of file
diff --git a/tests/travis/pgsql.phpunit.xml b/tests/travis/pgsql.phpunit.xml
index dfc1bff..51e433d 100644
--- a/tests/travis/pgsql.phpunit.xml
+++ b/tests/travis/pgsql.phpunit.xml
@@ -17,10 +17,9 @@
 			<directory suffix="test.php">../codeigniter</directory>
 		</testsuite>
 	</testsuites>
-	<filters>
-		<blacklist>
-			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
-			<directory suffix=".php">PHP_LIBDIR</directory>
-		</blacklist>
-	</filters>
+	<filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">../../system</directory>
+        </whitelist>
+	</filter>
 </phpunit>
\ No newline at end of file
diff --git a/tests/travis/sqlite.phpunit.xml b/tests/travis/sqlite.phpunit.xml
index 3223da5..7011657 100644
--- a/tests/travis/sqlite.phpunit.xml
+++ b/tests/travis/sqlite.phpunit.xml
@@ -17,10 +17,9 @@
 			<directory suffix="test.php">../codeigniter</directory>
 		</testsuite>
 	</testsuites>
-	<filters>
-		<blacklist>
-			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
-			<directory suffix=".php">PHP_LIBDIR</directory>
-		</blacklist>
-	</filters>
+	<filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">../../system</directory>
+        </whitelist>
+	</filter>
 </phpunit>
\ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 5a9bca2..c47076d 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -76,6 +76,7 @@
 	 -  Added db_set_charset() support.
 	 -  Added _optimize_table() support for the :doc:`Database Utility Class <database/utilities>` (rebuilds table indexes).
 	 -  Added boolean data type support in escape().
+	 -  Added update_batch() support.
    -  Added a constructor to the DB_result class and moved all driver-specific properties and logic out of the base DB_driver class to allow better abstraction.
    -  Removed limit() and order_by() support for UPDATE and DELETE queries in PostgreSQL driver. Postgres does not support those features.
    -  Removed protect_identifiers() and renamed internal method _protect_identifiers() to it instead - it was just an alias.
@@ -150,7 +151,7 @@
 
 -  Fixed a bug where ``unlink()`` raised an error if cache file did not exist when you try to delete it.
 -  Fixed a bug (#181) where a mis-spelling was in the form validation language file.
--  Fixed a bug (#159, #163) that mishandled Active Record nested transactions because _trans_depth was not getting incremented.
+-  Fixed a bug (#159, #163) that mishandled Query Builder nested transactions because _trans_depth was not getting incremented.
 -  Fixed a bug (#737, #75) where pagination anchor class was not set properly when using initialize method.
 -  Fixed a bug (#419) - auto_link() now recognizes URLs that come after a word boundary.
 -  Fixed a bug (#724) - is_unique in form validation now checks that you are connected to a database.
@@ -191,11 +192,11 @@
 -  Fixed a bug (#499) - a CSRF cookie was created even with CSRF protection being disabled.
 -  Fixed a bug (#306) - ODBC's insert_id() method was calling non-existent function odbc_insert_id(), which resulted in a fatal error.
 -  Fixed a bug in Oracle's DB_result class where the cursor id passed to it was always NULL.
--  Fixed a bug (#64) - Regular expression in DB_active_rec.php failed to handle queries containing SQL bracket delimiters in the join condition.
+-  Fixed a bug (#64) - Regular expression in DB_query_builder.php failed to handle queries containing SQL bracket delimiters in the join condition.
 -  Fixed a bug in the :doc:`Session Library <libraries/sessions>` where a PHP E_NOTICE error was triggered by _unserialize() due to results from databases such as MSSQL and Oracle being space-padded on the right.
 -  Fixed a bug (#501) - set_rules() to check if the request method is not 'POST' before aborting, instead of depending on count($_POST) in the :doc:`Form Validation Library <libraries/form_validation>`.
 -  Fixed a bug (#940) - csrf_verify() used to set the CSRF cookie while processing a POST request with no actual POST data, which resulted in validating a request that should be considered invalid.
--  Fixed a bug in PostgreSQL's escape_str() where it didn't properly escape LIKE wild characters.
+-  Fixed a bug (#136) - PostgreSQL, MySQL and MySQLi's escape_str() method didn't properly escape LIKE wild characters.
 -  Fixed a bug in the library loader where some PHP versions wouldn't execute the class constructor.
 -  Fixed a bug (#88) - An unexisting property was used for configuration of the Memcache cache driver.
 -  Fixed a bug (#14) - create_database() method in the :doc:`Database Forge Library <database/forge>` didn't utilize the configured database character set.
@@ -214,7 +215,13 @@
 -  Fixed a bug (#128) - :doc:`Language Library <libraries/language>` did not correctly keep track of loaded language files.
 -  Fixed a bug (#1242) - Added Windows path compatibility to function read_dir of ZIP library.
 -  Fixed a bug (#1314) - sess_destroy() did not destroy userdata.
--  Fixed a bug (#1349) - get_extension() in the `File Uploading Library <libraries/file_uploading>` returned the original filename when it didn't have an actual extension.
+-  Fixed a bug (#1349) - get_extension() in the :doc:`File Uploading Library <libraries/file_uploading>` returned the original filename when it didn't have an actual extension.
+-  Fixed a bug (#1273) - E_NOTICE being generated by :doc:`Query Builder <database/query_builder>`'s set_update_batch() method.
+-  Fixed a bug (#44, #110) - :doc:`Upload library <libraries/file_uploading>`'s clean_file_name() method didn't clear '!' and '#' characters.
+-  Fixed a bug (#121) - ``CI_DB_result::row()`` returned an array when there's no actual result to be returned.
+-  Fixed a bug (#319) - SQLSRV's affected_rows() method failed due to a scrollable cursor being created for write-type queries.
+-  Fixed a bug (#356) - PostgreSQL driver didn't have an _update_batch() method, which resulted in fatal error being triggered when update_batch() is used with it.
+-  Fixed a bug (#862) - create_table() failed on SQLSRV/MSSQL when used with 'IF NOT EXISTS'.
 
 Version 2.1.1
 =============