From d42cc4686ef0c52157b7613810b50706dc057b98 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Sun, 24 Jun 2012 23:52:40 +0300 Subject: Add pdo_sqlsrv subdriver --- system/database/drivers/pdo/pdo_driver.php | 28 +- .../drivers/pdo/subdrivers/pdo_mysql_driver.php | 41 +-- .../drivers/pdo/subdrivers/pdo_sqlite_driver.php | 2 +- .../drivers/pdo/subdrivers/pdo_sqlsrv_driver.php | 295 +++++++++++++++++++++ 4 files changed, 334 insertions(+), 32 deletions(-) create mode 100644 system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php index b16ca8219..0029e1ebe 100644 --- a/system/database/drivers/pdo/pdo_driver.php +++ b/system/database/drivers/pdo/pdo_driver.php @@ -85,7 +85,7 @@ class CI_DB_pdo_driver extends CI_DB { } $this->trans_enabled = FALSE; - $this->_random_keyword = ' RND('.time().')'; // database specific random keyword +// $this->_random_keyword = ' RND('.time().')'; // database specific random keyword } /** @@ -144,19 +144,19 @@ class CI_DB_pdo_driver extends CI_DB { { $this->dsn .= 'dbname='.$this->database.';'; } - elseif (stripos($this->dsn, 'database') === FALSE && in_array($this->subdriver, array('ibm', 'sqlsrv'))) - { - if (stripos($this->dsn, 'dsn') === FALSE) - { - $this->dsn .= 'database='.$this->database.';'; - } - } - - // Add charset to the DSN, if needed - if ( ! empty($this->char_set) && in_array($this->subdriver, array('4D', 'sybase', 'mssql', 'dblib'))) - { - $this->dsn .= 'charset='.$this->char_set.';'; - } + elseif (stripos($this->dsn, 'database') === FALSE && $this->subdriver === ibm') + { + if (stripos($this->dsn, 'dsn') === FALSE) + { + $this->dsn .= 'database='.$this->database.';'; + } + } + + // Add charset to the DSN, if needed + if ( ! empty($this->char_set) && in_array($this->subdriver, array('4D', 'sybase', 'mssql', 'dblib'))) + { + $this->dsn .= 'charset='.$this->char_set.';'; + } } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php index 166f72ce0..be56212ef 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php @@ -68,25 +68,32 @@ class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver { 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; + } + } - if ( ! empty($this->char_set)) - { - /* 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')) - { - $this->dsn .= ';charset='.$this->char_set; - } - else - { - $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set - .(empty($this->db_collat) ? '' : " COLLATE '".$this->dbcollat."'"); - } - } + // -------------------------------------------------------------------- + + /** + * 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')) + { + $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set + .(empty($this->db_collat) ? '' : " COLLATE '".$this->dbcollat."'"); } + + return parent::db_connect($persistent); } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php index f01ff1b93..bf0363f63 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php @@ -26,7 +26,7 @@ */ /** - * PDO Oracle Database Adapter Class + * PDO SQLite Database Adapter Class * * Note: _DB is an extender class that the app controller * creates dynamically based on whether the query builder 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 000000000..20837ee1f --- /dev/null +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php @@ -0,0 +1,295 @@ +dsn)) + { + $this->dsn = 'sqlsrv:Server='.(empty($this->hostname) ? 'localhost' : $this->hostname); + + empty($this->port) OR $this->dsn .= ','.$this->port; + empty($this->database) OR $this->dsn .= ';Database='.$this->database; + } + 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; + } + + $pdo_obj = parent::db_connect($persistent); + + if ( ! is_object($pdo_obj) OR is_bool($this->_quoted_identifier)) + { + return $pdo_obj; + } + + // 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 $pdo_obj; + } + + // -------------------------------------------------------------------- + + /** + * 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) + { + // 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 */ \ No newline at end of file -- cgit v1.2.3-24-g4f1b