diff options
Diffstat (limited to 'system/database/drivers/pdo/pdo_driver.php')
-rw-r--r-- | system/database/drivers/pdo/pdo_driver.php | 774 |
1 files changed, 146 insertions, 628 deletions
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php index e0e7dab65..6afc999c2 100644 --- a/system/database/drivers/pdo/pdo_driver.php +++ b/system/database/drivers/pdo/pdo_driver.php @@ -1,811 +1,329 @@ -<?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 - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. - * @license http://codeigniter.com/user_guide/license.html - * @author EllisLab Dev Team - * @link http://codeigniter.com - * @since Version 2.1.2 + * 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 2.1.0 * @filesource */ - -// ------------------------------------------------------------------------ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * PDO 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 EllisLab Dev Team - * @link http://codeigniter.com/user_guide/database/ + * @link https://codeigniter.com/user_guide/database/ */ class CI_DB_pdo_driver extends CI_DB { - var $dbdriver = 'pdo'; - - // the character used to excape - not necessary for PDO - var $_escape_char = ''; - var $_like_escape_str; - var $_like_escape_chr; - - - /** - * The syntax to count rows is slightly different across different - * database engines, so this string appears in each driver and is - * used for the count_all() and count_all_results() functions. - */ - var $_count_string = "SELECT COUNT(*) AS "; - var $_random_keyword; - - var $options = array(); - - function __construct($params) - { - parent::__construct($params); - - // clause and character used for LIKE escape sequences - if (strpos($this->hostname, 'mysql') !== FALSE) - { - $this->_like_escape_str = ''; - $this->_like_escape_chr = ''; - - //Prior to this version, the charset can't be set in the dsn - if(is_php('5.3.6')) - { - $this->hostname .= ";charset={$this->char_set}"; - } - - //Set the charset with the connection options - $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}"; - } - elseif (strpos($this->hostname, 'odbc') !== FALSE) - { - $this->_like_escape_str = " {escape '%s'} "; - $this->_like_escape_chr = '!'; - } - else - { - $this->_like_escape_str = " ESCAPE '%s' "; - $this->_like_escape_chr = '!'; - } - - empty($this->database) OR $this->hostname .= ';dbname='.$this->database; - - $this->trans_enabled = FALSE; - - $this->_random_keyword = ' RND('.time().')'; // database specific random keyword - } - /** - * Non-persistent database connection + * Database driver * - * @access private called by the base class - * @return resource + * @var string */ - function db_connect() - { - $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; - - return new PDO($this->hostname, $this->username, $this->password, $this->options); - } - - // -------------------------------------------------------------------- + public $dbdriver = 'pdo'; /** - * Persistent database connection + * PDO Options * - * @access private called by the base class - * @return resource + * @var array */ - function db_pconnect() - { - $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; - $this->options['PDO::ATTR_PERSISTENT'] = TRUE; - - return new PDO($this->hostname, $this->username, $this->password, $this->options); - } + public $options = array(); // -------------------------------------------------------------------- /** - * Reconnect + * Class constructor * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout + * Validates the DSN string and/or detects the subdriver. * - * @access public + * @param array $params * @return void */ - function reconnect() - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // Not needed for PDO - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION); - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return object - */ - function _execute($sql) + public function __construct($params) { - $sql = $this->_prep_query($sql); - $result_id = $this->conn_id->prepare($sql); + parent::__construct($params); - if (is_object($result_id) && ($result = $result_id->execute())) + if (preg_match('/([^:]+):/', $this->dsn, $match) && count($match) === 2) { - if (is_numeric(stripos($sql, 'SELECT'))) - { - $this->affect_rows = count($result_id->fetchAll()); - } - else - { - $this->affect_rows = $result_id->rowCount(); - } + // 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 = $match[1]; + return; } - else + // Legacy support for DSN specified in the hostname field + elseif (preg_match('/([^:]+):/', $this->hostname, $match) && count($match) === 2) { - $this->affect_rows = 0; - $result = FALSE; + $this->dsn = $this->hostname; + $this->hostname = NULL; + $this->subdriver = $match[1]; + return; } - - return $result; - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) + elseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE)) { - return TRUE; + $this->subdriver = 'dblib'; } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + elseif ($this->subdriver === '4D') { - return TRUE; + $this->subdriver = '4d'; } - - // 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 = (bool) ($test_mode === TRUE); - - return $this->conn_id->beginTransaction(); - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) + elseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'pgsql', 'sqlite', 'sqlsrv'), TRUE)) { - return TRUE; - } + log_message('error', 'PDO: Invalid or non-existent subdriver'); - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; + if ($this->db_debug) + { + show_error('Invalid or non-existent PDO subdriver'); + } } - $ret = $this->conn->commit(); - return $ret; + $this->dsn = NULL; } // -------------------------------------------------------------------- /** - * Rollback Transaction + * Database connection * - * @access public - * @return bool + * @param bool $persistent + * @return object */ - function trans_rollback() + public function db_connect($persistent = FALSE) { - if ( ! $this->trans_enabled) + if ($persistent === TRUE) { - return TRUE; + $this->options[PDO::ATTR_PERSISTENT] = TRUE; } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + try { - return TRUE; + return new PDO($this->dsn, $this->username, $this->password, $this->options); } - - $ret = $this->conn_id->rollBack(); - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) + catch (PDOException $e) { - foreach ($str as $key => $val) + if ($this->db_debug && empty($this->failover)) { - $str[$key] = $this->escape_str($val, $like); + $this->display_error($e->getMessage(), '', TRUE); } - return $str; - } - - //Escape the string - $str = $this->conn_id->quote($str); - - //If there are duplicated quotes, trim them away - if (strpos($str, "'") === 0) - { - $str = substr($str, 1, -1); - } - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $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); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return $this->affect_rows; - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id($name=NULL) - { - //Convenience method for postgres insertid - if (strpos($this->hostname, 'pgsql') !== FALSE) - { - $v = $this->_version(); - - $table = func_num_args() > 0 ? func_get_arg(0) : NULL; - - if ($table == NULL && $v >= '8.1') - { - $sql='SELECT LASTVAL() as ins_id'; - } - $query = $this->query($sql); - $row = $query->row(); - return $row->ins_id; - } - else - { - return $this->conn_id->lastInsertId($name); + return FALSE; } } // -------------------------------------------------------------------- /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database + * Database version number * - * @access public - * @param string * @return string */ - function count_all($table = '') + public function version() { - if ($table == '') + if (isset($this->data_cache['version'])) { - return 0; + return $this->data_cache['version']; } - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) + // Not all subdrivers support the getAttribute() method + try { - return 0; + return $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION); } - - $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 - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SHOW TABLES FROM `".$this->database."`"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') + catch (PDOException $e) { - //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - return FALSE; // not currently supported + return parent::version(); } - - return $sql; } // -------------------------------------------------------------------- /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched + * Execute the query * - * @access public - * @param string the table name - * @return string + * @param string $sql SQL query + * @return mixed */ - function _list_columns($table = '') + protected function _execute($sql) { - return "SHOW COLUMNS FROM ".$table; + return $this->conn_id->query($sql); } // -------------------------------------------------------------------- /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved + * Begin Transaction * - * @access public - * @param string the table name - * @return object + * @return bool */ - function _field_data($table) + protected function _trans_begin() { - return "SELECT TOP 1 FROM ".$table; + return $this->conn_id->beginTransaction(); } // -------------------------------------------------------------------- /** - * The error message string + * Commit Transaction * - * @access private - * @return string + * @return bool */ - function _error_message() + protected function _trans_commit() { - $error_array = $this->conn_id->errorInfo(); - return $error_array[2]; + return $this->conn_id->commit(); } // -------------------------------------------------------------------- /** - * The error message number + * Rollback Transaction * - * @access private - * @return integer + * @return bool */ - function _error_number() + protected function _trans_rollback() { - return $this->conn_id->errorCode(); + return $this->conn_id->rollBack(); } // -------------------------------------------------------------------- /** - * Escape the SQL Identifiers - * - * This function escapes column and table names + * Platform-dependent string escape * - * @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; - } + // Escape the string + $str = $this->conn_id->quote($str); - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + // If there are duplicated quotes, trim them away + return ($str[0] === "'") + ? substr($str, 1, -1) + : $str; } // -------------------------------------------------------------------- /** - * 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 (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')'; + return is_object($this->result_id) ? $this->result_id->rowCount() : 0; } // -------------------------------------------------------------------- /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch 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 + * @param string $name + * @return int */ - function _insert_batch($table, $keys, $values) + public function insert_id($name = NULL) { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); + return $this->conn_id->lastInsertId($name); } // -------------------------------------------------------------------- /** - * Update statement + * Field data query * - * Generates a platform-specific update 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 update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause + * @param string $table * @return string */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + protected function _field_data($table) { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $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; + return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table); } - + // -------------------------------------------------------------------- /** - * Update_Batch statement + * Error * - * Generates a platform-specific batch update string from the supplied data + * Returns an array containing code and message of the last + * database error that has occurred. * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @return string + * @return array */ - function _update_batch($table, $values, $index, $where = NULL) + public function error() { - $ids = array(); - $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; + $error = array('code' => '00000', 'message' => ''); + $pdo_error = $this->conn_id->errorInfo(); - foreach ($values as $key => $val) + if (empty($pdo_error[0])) { - $ids[] = $val[$index]; - - foreach (array_keys($val) as $field) - { - if ($field != $index) - { - $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; - } - } + return $error; } - $sql = "UPDATE ".$table." SET "; - $cases = ''; - - foreach ($final as $k => $v) + $error['code'] = isset($pdo_error[1]) ? $pdo_error[0].'/'.$pdo_error[1] : $pdo_error[0]; + if (isset($pdo_error[2])) { - $cases .= $k.' = CASE '."\n"; - foreach ($v as $row) - { - $cases .= $row."\n"; - } - - $cases .= 'ELSE '.$k.' END, '; + $error['message'] = $pdo_error[2]; } - $sql .= substr($cases, 0, -2); - - $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; - - return $sql; + return $error; } - // -------------------------------------------------------------------- /** * Truncate statement * * 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" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return $this->_delete($table); - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $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; - } - - // -------------------------------------------------------------------- - - /** - * Limit string * - * Generates a platform-specific LIMIT clause + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value + * @param string $table * @return string */ - function _limit($sql, $limit, $offset) - { - if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE) - { - if ($offset == 0) - { - $offset = ''; - } - else - { - $offset .= ", "; - } - - return $sql."LIMIT ".$offset.$limit; - } - else - { - $sql .= "LIMIT ".$limit; - - if ($offset > 0) - { - $sql .= " OFFSET ".$offset; - } - - return $sql; - } - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) + protected function _truncate($table) { - $this->conn_id = null; + return 'TRUNCATE TABLE '.$table; } - } - - - -/* End of file pdo_driver.php */ -/* Location: ./system/database/drivers/pdo/pdo_driver.php */
\ No newline at end of file |