Add pdo_dblib subdriver
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index 0029e1e..94a1210 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -68,7 +68,7 @@
 		{
 			// 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->subdriver = end($match);
+			$this->subdriver = $match[1];
 		}
 		else
 		{
@@ -76,6 +76,11 @@
 			$this->_connect_string($params);
 		}
 
+		if (in_array($this->subdriver, array('mssql', 'sybase'), TRUE))
+		{
+			$this->subdriver = 'dblib';
+		}
+
 		// clause and character used for LIKE escape sequences
 		// this one depends on the driver being used
 		if ($this->subdriver === 'odbc')
@@ -124,9 +129,8 @@
 			$this->dsn = $this->subdriver.':';
 
 			// Add hostname to the DSN for databases that need it
-			if ( ! empty($this->hostname)
-				&& strpos($this->hostname, ':') === FALSE
-				&& in_array($this->subdriver, array('informix', 'sybase', 'mssql', 'dblib', 'cubrid')))
+			if ( ! empty($this->hostname) && strpos($this->hostname, ':') === FALSE
+				&& in_array($this->subdriver, array('informix', 'cubrid')))
 			{
 			    $this->dsn .= 'host='.$this->hostname.';';
 			}
@@ -134,17 +138,16 @@
 			// Add a port to the DSN for databases that can use it
 			if ( ! empty($this->port) && in_array($this->subdriver, array('informix', 'ibm', 'cubrid')))
 			{
-			    $this->dsn .= 'port='.$this->port.';';
+				$this->dsn .= 'port='.$this->port.';';
 			}
 		}
 
 		// Add the database name to the DSN, if needed
-		if (stripos($this->dsn, 'dbname') === FALSE
-			&& in_array($this->subdriver, array('4D', 'firebird', 'sybase', 'mssql', 'dblib', 'cubrid')))
+		if (stripos($this->dsn, 'dbname') === FALSE && in_array($this->subdriver, array('4D', 'firebird', 'cubrid')))
 		{
 			$this->dsn .= 'dbname='.$this->database.';';
 		}
-		elseif (stripos($this->dsn, 'database') === FALSE && $this->subdriver === ibm')
+		elseif (stripos($this->dsn, 'database') === FALSE && $this->subdriver === 'ibm')
 		{
 			if (stripos($this->dsn, 'dsn') === FALSE)
 			{
@@ -153,7 +156,7 @@
 		}
 
 		// Add charset to the DSN, if needed
-		if ( ! empty($this->char_set) && in_array($this->subdriver, array('4D', 'sybase', 'mssql', 'dblib')))
+		if ( ! empty($this->char_set) && $this->subdriver === '4D')
 		{
 			$this->dsn .= 'charset='.$this->char_set.';';
 		}
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..cee936a
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -0,0 +1,270 @@
+<?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=') === FALSE)
+			{
+				$this->dsn .= ';charset='.$this->char_set;
+			}
+
+			$this->subdriver = 'dblib';
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * 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 "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 TOP 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 (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 '.$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 '.implde(' 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 */
\ 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
index 20837ee..f7e2420 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -64,6 +64,41 @@
 
 			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))
 		{