diff options
Diffstat (limited to 'system/database/drivers/odbc/odbc_driver.php')
-rw-r--r-- | system/database/drivers/odbc/odbc_driver.php | 616 |
1 files changed, 202 insertions, 414 deletions
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php index 0e82d57ae..ef982fc63 100644 --- a/system/database/drivers/odbc/odbc_driver.php +++ b/system/database/drivers/odbc/odbc_driver.php @@ -1,617 +1,414 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * - * An open source application development framework for PHP 5.1.6 or newer + * An open source application development framework for PHP * - * @package CodeIgniter - * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. - * @license http://codeigniter.com/user_guide/license.html - * @link http://codeigniter.com - * @since Version 1.0 + * This content is released under the MIT License (MIT) + * + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) + * @license http://opensource.org/licenses/MIT MIT License + * @link https://codeigniter.com + * @since Version 1.3.0 * @filesource */ - -// ------------------------------------------------------------------------ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * ODBC Database Adapter Class * * Note: _DB is an extender class that the app controller - * creates dynamically based on whether the active record + * creates dynamically based on whether the query builder * class is being used or not. * * @package CodeIgniter * @subpackage Drivers * @category Database - * @author ExpressionEngine Dev Team - * @link http://codeigniter.com/user_guide/database/ + * @author EllisLab Dev Team + * @link https://codeigniter.com/user_guide/database/ */ -class CI_DB_odbc_driver extends CI_DB { - - var $dbdriver = 'odbc'; - - // the character used to excape - not necessary for ODBC - var $_escape_char = ''; - - // clause and character used for LIKE escape sequences - var $_like_escape_str = " {escape '%s'} "; - var $_like_escape_chr = '!'; +class CI_DB_odbc_driver extends CI_DB_driver { /** - * 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. + * Database driver + * + * @var string */ - var $_count_string = "SELECT COUNT(*) AS "; - var $_random_keyword; - - - function __construct($params) - { - parent::__construct($params); - - $this->_random_keyword = ' RND('.time().')'; // database specific random keyword - } + public $dbdriver = 'odbc'; /** - * Non-persistent database connection + * Database schema * - * @access private called by the base class - * @return resource + * @var string */ - function db_connect() - { - return @odbc_connect($this->hostname, $this->username, $this->password); - } + public $schema = 'public'; // -------------------------------------------------------------------- /** - * Persistent database connection + * Identifier escape character * - * @access private called by the base class - * @return resource + * Must be empty for ODBC. + * + * @var string */ - function db_pconnect() - { - return @odbc_pconnect($this->hostname, $this->username, $this->password); - } - - // -------------------------------------------------------------------- + protected $_escape_char = ''; /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout + * ESCAPE statement string * - * @access public - * @return void + * @var string */ - function reconnect() - { - // not implemented in odbc - } - - // -------------------------------------------------------------------- + protected $_like_escape_str = " {escape '%s'} "; /** - * Select the database + * ORDER BY random keyword * - * @access private called by the base class - * @return resource + * @var array */ - function db_select() - { - // Not needed for ODBC - return TRUE; - } + protected $_random_keyword = array('RND()', 'RND(%d)'); // -------------------------------------------------------------------- /** - * Set client character set + * ODBC result ID resource returned from odbc_prepare() * - * @access public - * @param string - * @param string - * @return resource + * @var resource */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- + private $odbc_result; /** - * Version number query string + * Values to use with odbc_execute() for prepared statements * - * @access public - * @return string + * @var array */ - function _version() - { - return "SELECT version() AS ver"; - } + private $binds = array(); // -------------------------------------------------------------------- /** - * Execute the query + * Class constructor * - * @access private called by the base class - * @param string an SQL query - * @return resource + * @param array $params + * @return void */ - function _execute($sql) + public function __construct($params) { - $sql = $this->_prep_query($sql); - return @odbc_exec($this->conn_id, $sql); + parent::__construct($params); + + // Legacy support for DSN in the hostname field + if (empty($this->dsn)) + { + $this->dsn = $this->hostname; + } } // -------------------------------------------------------------------- /** - * Prep the query - * - * If needed, each database adapter can prep the query string + * Non-persistent database connection * - * @access private called by execute() - * @param string an SQL query - * @return string + * @param bool $persistent + * @return resource */ - function _prep_query($sql) + public function db_connect($persistent = FALSE) { - return $sql; + return ($persistent === TRUE) + ? odbc_pconnect($this->dsn, $this->username, $this->password) + : odbc_connect($this->dsn, $this->username, $this->password); } // -------------------------------------------------------------------- /** - * Begin Transaction + * Compile Bindings * - * @access public - * @return bool + * @param string $sql SQL statement + * @param array $binds An array of values to bind + * @return string */ - function trans_begin($test_mode = FALSE) + public function compile_binds($sql, $binds) { - if ( ! $this->trans_enabled) + if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE) { - return TRUE; + return $sql; } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + elseif ( ! is_array($binds)) { - return TRUE; + $binds = array($binds); + $bind_count = 1; + } + else + { + // Make sure we're using numeric keys + $binds = array_values($binds); + $bind_count = count($binds); } - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - return odbc_autocommit($this->conn_id, FALSE); - } - - // -------------------------------------------------------------------- + // We'll need the marker length later + $ml = strlen($this->bind_marker); - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) + // Make sure not to replace a chunk inside a string that happens to match the bind marker + if ($c = preg_match_all("/'[^']*'|\"[^\"]*\"/i", $sql, $matches)) { - return TRUE; + $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', + str_replace($matches[0], + str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]), + $sql, $c), + $matches, PREG_OFFSET_CAPTURE); + + // Bind values' count must match the count of markers in the query + if ($bind_count !== $c) + { + return $sql; + } } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count) { - return TRUE; + return $sql; } - $ret = odbc_commit($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) + if ($this->bind_marker !== '?') { - return TRUE; + do + { + $c--; + $sql = substr_replace($sql, '?', $matches[0][$c][1], $ml); + } + while ($c !== 0); } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + if (FALSE !== ($this->odbc_result = odbc_prepare($this->conn_id, $sql))) { - return TRUE; + $this->binds = array_values($binds); } - $ret = odbc_rollback($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; + return $sql; } // -------------------------------------------------------------------- /** - * Escape String + * Execute the query * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string + * @param string $sql an SQL query + * @return resource */ - function escape_str($str, $like = FALSE) + protected function _execute($sql) { - if (is_array($str)) + if ( ! isset($this->odbc_result)) { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; + return odbc_exec($this->conn_id, $sql); + } + elseif ($this->odbc_result === FALSE) + { + return FALSE; } - // ODBC doesn't require escaping - $str = remove_invisible_characters($str); - - // escape LIKE condition wildcards - if ($like === TRUE) + if (TRUE === ($success = odbc_execute($this->odbc_result, $this->binds))) { - $str = 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); + // For queries that return result sets, return the result_id resource on success + $this->is_write_type($sql) OR $success = $this->odbc_result; } - return $str; - } + $this->odbc_result = NULL; + $this->binds = array(); - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @odbc_num_rows($this->conn_id); + return $success; } // -------------------------------------------------------------------- /** - * Insert ID + * Begin Transaction * - * @access public - * @return integer + * @return bool */ - function insert_id() + protected function _trans_begin() { - return @odbc_insert_id($this->conn_id); + return odbc_autocommit($this->conn_id, FALSE); } // -------------------------------------------------------------------- /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database + * Commit Transaction * - * @access public - * @param string - * @return string + * @return bool */ - function count_all($table = '') + protected function _trans_commit() { - if ($table == '') + if (odbc_commit($this->conn_id)) { - 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; + odbc_autocommit($this->conn_id, TRUE); + return TRUE; } - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; + return FALSE; } // -------------------------------------------------------------------- /** - * Show table query - * - * Generates a platform-specific query string so that the table names can be fetched + * Rollback Transaction * - * @access private - * @param boolean - * @return string + * @return bool */ - function _list_tables($prefix_limit = FALSE) + protected function _trans_rollback() { - $sql = "SHOW TABLES FROM `".$this->database."`"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') + if (odbc_rollback($this->conn_id)) { - //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - return FALSE; // not currently supported + odbc_autocommit($this->conn_id, TRUE); + return TRUE; } - return $sql; + return FALSE; } // -------------------------------------------------------------------- /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched + * Determines if a query is a "write" type. * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SHOW COLUMNS FROM ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object + * @param string An SQL query string + * @return bool */ - function _field_data($table) + public function is_write_type($sql) { - return "SELECT TOP 1 FROM ".$table; - } - - // -------------------------------------------------------------------- + if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql)) + { + return FALSE; + } - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return odbc_errormsg($this->conn_id); + return parent::is_write_type($sql); } // -------------------------------------------------------------------- /** - * The error message number + * Platform-dependent string escape * - * @access private - * @return integer - */ - function _error_number() - { - return odbc_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private * @param string * @return string */ - function _escape_identifiers($item) + protected function _escape_str($str) { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + $this->display_error('db_unsupported_feature'); } // -------------------------------------------------------------------- /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards + * Affected Rows * - * @access public - * @param type - * @return type + * @return int */ - function _from_tables($tables) + public function affected_rows() { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return '('.implode(', ', $tables).')'; + return odbc_num_rows($this->result_id); } // -------------------------------------------------------------------- /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data + * Insert ID * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string + * @return bool */ - function _insert($table, $keys, $values) + public function insert_id() { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE; } // -------------------------------------------------------------------- /** - * Update statement + * Show table query * - * Generates a platform-specific update string from the supplied data + * Generates a platform-specific query string so that the table names can be fetched * - * @access public - * @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 bool $prefix_limit * @return string */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + protected function _list_tables($prefix_limit = FALSE) { - foreach ($values as $key => $val) + $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '".$this->schema."'"; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') { - $valstr[] = $key." = ".$val; + return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); } - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - return $sql; } - // -------------------------------------------------------------------- /** - * Truncate statement + * Show column query * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" + * Generates a platform-specific query string so that the column names can be fetched * - * @access public - * @param string the table name + * @param string $table * @return string */ - function _truncate($table) + protected function _list_columns($table = '') { - return $this->_delete($table); + return 'SHOW COLUMNS FROM '.$table; } // -------------------------------------------------------------------- /** - * Delete statement + * Field data query * - * Generates a platform-specific delete string from the supplied data + * Generates a platform-specific query so that the column data can be retrieved * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause + * @param string $table * @return string */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) + protected function _field_data($table) { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; + return 'SELECT TOP 1 FROM '.$table; } // -------------------------------------------------------------------- /** - * Limit string + * Error * - * Generates a platform-specific LIMIT clause + * Returns an array containing code and message of the last + * database error that has occurred. * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string + * @return array */ - function _limit($sql, $limit, $offset) + public function error() { - // Does ODBC doesn't use the LIMIT clause? - return $sql; + return array('code' => odbc_error($this->conn_id), 'message' => odbc_errormsg($this->conn_id)); } // -------------------------------------------------------------------- @@ -619,19 +416,10 @@ class CI_DB_odbc_driver extends CI_DB { /** * Close DB Connection * - * @access public - * @param resource * @return void */ - function _close($conn_id) + protected function _close() { - @odbc_close($conn_id); + odbc_close($this->conn_id); } - - } - - - -/* End of file odbc_driver.php */ -/* Location: ./system/database/drivers/odbc/odbc_driver.php */
\ No newline at end of file |