diff options
Diffstat (limited to 'system/database/drivers/postgre/postgre_driver.php')
-rw-r--r-- | system/database/drivers/postgre/postgre_driver.php | 641 |
1 files changed, 308 insertions, 333 deletions
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php index 5367f9759..e5d861bd9 100644 --- a/system/database/drivers/postgre/postgre_driver.php +++ b/system/database/drivers/postgre/postgre_driver.php @@ -1,76 +1,120 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * CodeIgniter * - * An open source application development framework for PHP 5.1.6 or newer + * 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 ExpressionEngine Dev Team - * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc. - * @license http://codeigniter.com/user_guide/license.html + * @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 1.0 * @filesource */ -// ------------------------------------------------------------------------ - /** * Postgre 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 + * @author EllisLab Dev Team * @link http://codeigniter.com/user_guide/database/ */ class CI_DB_postgre_driver extends CI_DB { - var $dbdriver = 'postgre'; + public $dbdriver = 'postgre'; - var $_escape_char = '"'; + protected $_escape_char = '"'; // clause and character used for LIKE escape sequences - var $_like_escape_str = " ESCAPE '%s' "; - var $_like_escape_chr = '!'; + protected $_like_escape_str = " ESCAPE '%s' "; + protected $_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 = ' RANDOM()'; // database specific random keyword + protected $_count_string = 'SELECT COUNT(*) AS '; + protected $_random_keyword = ' RANDOM()'; // database specific random keyword /** - * Connection String + * Constructor * - * @access private - * @return string + * Creates a DSN string to be used for db_connect() and db_pconnect() + * + * @return void */ - function _connect_string() + public function __construct($params) { - $components = array( - 'hostname' => 'host', - 'port' => 'port', - 'database' => 'dbname', - 'username' => 'user', - 'password' => 'password' - ); - - $connect_string = ""; - foreach ($components as $key => $val) + parent::__construct($params); + + if ( ! empty($this->dsn)) + { + return; + } + + $this->dsn === '' OR $this->dsn = ''; + + if (strpos($this->hostname, '/') !== FALSE) + { + // If UNIX sockets are used, we shouldn't set a port + $this->port = ''; + } + + $this->hostname === '' OR $this->dsn = 'host='.$this->hostname.' '; + + if ( ! empty($this->port) && ctype_digit($this->port)) + { + $this->dsn .= 'port='.$this->port.' '; + } + + if ($this->username !== '') + { + $this->dsn .= 'user='.$this->username.' '; + + /* An empty password is valid! + * + * $db['password'] = NULL must be done in order to ignore it. + */ + $this->password === NULL OR $this->dsn .= "password='".$this->password."' "; + } + + $this->database === '' OR $this->dsn .= 'dbname='.$this->database.' '; + + /* We don't have these options as elements in our standard configuration + * array, but they might be set by parse_url() if the configuration was + * provided via string. Example: + * + * postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1 + */ + foreach (array('connect_timeout', 'options', 'sslmode', 'service') as $key) { - if (isset($this->$key) && $this->$key != '') + if (isset($this->$key) && is_string($this->key) && $this->key !== '') { - $connect_string .= " $val=".$this->$key; + $this->dsn .= $key."='".$this->key."' "; } } - return trim($connect_string); + + $this->dsn = rtrim($this->dsn); } // -------------------------------------------------------------------- @@ -78,12 +122,11 @@ class CI_DB_postgre_driver extends CI_DB { /** * Non-persistent database connection * - * @access private called by the base class * @return resource */ - function db_connect() + public function db_connect() { - return @pg_connect($this->_connect_string()); + return @pg_connect($this->dsn); } // -------------------------------------------------------------------- @@ -91,12 +134,11 @@ class CI_DB_postgre_driver extends CI_DB { /** * Persistent database connection * - * @access private called by the base class * @return resource */ - function db_pconnect() + public function db_pconnect() { - return @pg_pconnect($this->_connect_string()); + return @pg_pconnect($this->dsn); } // -------------------------------------------------------------------- @@ -107,10 +149,9 @@ class CI_DB_postgre_driver extends CI_DB { * Keep / reestablish the db connection if no queries have been * sent for a length of time exceeding the server's idle timeout * - * @access public * @return void */ - function reconnect() + public function reconnect() { if (pg_ping($this->conn_id) === FALSE) { @@ -121,44 +162,43 @@ class CI_DB_postgre_driver extends CI_DB { // -------------------------------------------------------------------- /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // Not needed for Postgre so we'll return TRUE - return TRUE; - } - - // -------------------------------------------------------------------- - - /** * Set client character set * - * @access public - * @param string * @param string - * @return resource + * @return bool */ - function db_set_charset($charset, $collation) + protected function _db_set_charset($charset) { - // @todo - add support if needed - return TRUE; + return (pg_set_client_encoding($this->conn_id, $charset) === 0); } // -------------------------------------------------------------------- /** - * Version number query string + * Database version number * - * @access public * @return string */ - function _version() + public function version() { - return "SELECT version() AS ver"; + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if (($pg_version = pg_version($this->conn_id)) === FALSE) + { + return FALSE; + } + + /* If PHP was compiled with PostgreSQL lib versions earlier + * than 7.4, pg_version() won't return the server version + * and so we'll have to fall back to running a query in + * order to get it. + */ + return isset($pg_version['server']) + ? $this->data_cache['version'] = $pg_version['server'] + : parent::version(); } // -------------------------------------------------------------------- @@ -166,49 +206,26 @@ class CI_DB_postgre_driver extends CI_DB { /** * Execute the query * - * @access private called by the base class * @param string an SQL query * @return resource */ - function _execute($sql) + protected function _execute($sql) { - $sql = $this->_prep_query($sql); return @pg_query($this->conn_id, $sql); } // -------------------------------------------------------------------- /** - * 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 + * @param bool * @return bool */ - function trans_begin($test_mode = FALSE) + public function trans_begin($test_mode = FALSE) { - if ( ! $this->trans_enabled) - { - return TRUE; - } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + if ( ! $this->trans_enabled OR $this->_trans_depth > 0) { return TRUE; } @@ -216,9 +233,9 @@ class CI_DB_postgre_driver extends CI_DB { // 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; + $this->_trans_failure = ($test_mode === TRUE); - return @pg_exec($this->conn_id, "begin"); + return (bool) @pg_query($this->conn_id, 'BEGIN'); } // -------------------------------------------------------------------- @@ -226,23 +243,17 @@ class CI_DB_postgre_driver extends CI_DB { /** * Commit Transaction * - * @access public * @return bool */ - function trans_commit() + public function trans_commit() { - if ( ! $this->trans_enabled) - { - return TRUE; - } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + if ( ! $this->trans_enabled OR $this->_trans_depth > 0) { return TRUE; } - return @pg_exec($this->conn_id, "commit"); + return (bool) @pg_query($this->conn_id, 'COMMIT'); } // -------------------------------------------------------------------- @@ -250,23 +261,17 @@ class CI_DB_postgre_driver extends CI_DB { /** * Rollback Transaction * - * @access public * @return bool */ - function trans_rollback() + public function trans_rollback() { - if ( ! $this->trans_enabled) - { - return TRUE; - } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) + if ( ! $this->trans_enabled OR $this->_trans_depth > 0) { return TRUE; } - return @pg_exec($this->conn_id, "rollback"); + return (bool) @pg_query($this->conn_id, 'ROLLBACK'); } // -------------------------------------------------------------------- @@ -274,12 +279,11 @@ class CI_DB_postgre_driver extends CI_DB { /** * 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) + public function escape_str($str, $like = FALSE) { if (is_array($str)) { @@ -296,9 +300,9 @@ class CI_DB_postgre_driver extends CI_DB { // 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_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; @@ -307,86 +311,80 @@ class CI_DB_postgre_driver extends CI_DB { // -------------------------------------------------------------------- /** - * Affected Rows + * "Smart" Escape String + * + * Escapes data based on type + * Sets boolean and null types * - * @access public - * @return integer + * @param string + * @return mixed */ - function affected_rows() + public function escape($str) { - return @pg_affected_rows($this->result_id); + if (is_bool($str)) + { + return ($str) ? 'TRUE' : 'FALSE'; + } + + return parent::escape($str); } // -------------------------------------------------------------------- /** - * Insert ID + * Affected Rows * - * @access public - * @return integer + * @return int */ - function insert_id() + public function affected_rows() { - $v = $this->_version(); - $v = $v['server']; - - $table = func_num_args() > 0 ? func_get_arg(0) : NULL; - $column = func_num_args() > 1 ? func_get_arg(1) : NULL; - - if ($table == NULL && $v >= '8.1') - { - $sql='SELECT LASTVAL() as ins_id'; - } - elseif ($table != NULL && $column != NULL && $v >= '8.0') - { - $sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column); - $query = $this->query($sql); - $row = $query->row(); - $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq); - } - elseif ($table != NULL) - { - // seq_name passed in table parameter - $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table); - } - else - { - return pg_last_oid($this->result_id); - } - $query = $this->query($sql); - $row = $query->row(); - return $row->ins_id; + return @pg_affected_rows($this->result_id); } // -------------------------------------------------------------------- /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database + * Insert ID * - * @access public - * @param string * @return string */ - function count_all($table = '') + public function insert_id() { - if ($table == '') + $v = pg_version($this->conn_id); + $v = isset($v['server']) ? $v['server'] : 0; // 'server' key is only available since PosgreSQL 7.4 + + $table = (func_num_args() > 0) ? func_get_arg(0) : NULL; + $column = (func_num_args() > 1) ? func_get_arg(1) : NULL; + + if ($table === NULL && $v >= '8.1') { - return 0; + $sql = 'SELECT LASTVAL() AS ins_id'; } + elseif ($table !== NULL) + { + if ($column !== NULL && $v >= '8.0') + { + $sql = 'SELECT pg_get_serial_sequence(\''.$table."', '".$column."') AS seq"; + $query = $this->query($sql); + $query = $query->row(); + $seq = $query->seq; + } + else + { + // seq_name passed in table parameter + $seq = $table; + } - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) + $sql = 'SELECT CURRVAL(\''.$seq."') AS ins_id"; + } + else { - return 0; + return pg_last_oid($this->result_id); } - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; + $query = $this->query($sql); + $query = $query->row(); + return (int) $query->ins_id; } // -------------------------------------------------------------------- @@ -396,17 +394,16 @@ class CI_DB_postgre_driver extends CI_DB { * * Generates a platform-specific query string so that the table names can be fetched * - * @access private - * @param boolean + * @param bool * @return string */ - function _list_tables($prefix_limit = FALSE) + protected function _list_tables($prefix_limit = FALSE) { $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"; - if ($prefix_limit !== FALSE AND $this->dbprefix != '') + if ($prefix_limit !== FALSE && $this->dbprefix !== '') { - $sql .= " AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); + return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); } return $sql; @@ -419,13 +416,12 @@ class CI_DB_postgre_driver extends CI_DB { * * Generates a platform-specific query string so that the column names can be fetched * - * @access public * @param string the table name * @return string */ - function _list_columns($table = '') + protected function _list_columns($table = '') { - return "SELECT column_name FROM information_schema.columns WHERE table_name ='".$table."'"; + return "SELECT column_name FROM information_schema.columns WHERE table_name = '".$table."'"; } // -------------------------------------------------------------------- @@ -435,81 +431,27 @@ class CI_DB_postgre_driver extends CI_DB { * * Generates a platform-specific query so that the column data can be retrieved * - * @access public * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT * FROM ".$table." LIMIT 1"; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private * @return string */ - function _error_message() - { - return pg_last_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() + protected function _field_data($table) { - return ''; + return 'SELECT * FROM '.$table.' LIMIT 1'; } // -------------------------------------------------------------------- /** - * Escape the SQL Identifiers + * Error * - * This function escapes column and table names + * Returns an array containing code and message of the last + * database error that has occured. * - * @access private - * @param string - * @return string + * @return array */ - function _escape_identifiers($item) + public function error() { - 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); + return array('code' => '', 'message' => pg_last_error($this->conn_id)); } // -------------------------------------------------------------------- @@ -520,11 +462,10 @@ class CI_DB_postgre_driver extends CI_DB { * This function implicitly groups FROM tables so there is no confusion * about operator precedence in harmony with SQL standards * - * @access public - * @param type - * @return type + * @param array + * @return string */ - function _from_tables($tables) + protected function _from_tables($tables) { if ( ! is_array($tables)) { @@ -537,90 +478,74 @@ class CI_DB_postgre_driver extends CI_DB { // -------------------------------------------------------------------- /** - * 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 - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert_batch($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); - } - - // -------------------------------------------------------------------- - - /** * Update statement * * Generates a platform-specific update string from the supplied data * - * @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 array the orderby clause (ignored) + * @param array the limit clause (ignored) + * @param array the like clause * @return string */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array()) { foreach ($values as $key => $val) { - $valstr[] = $key." = ".$val; + $valstr[] = $key.' = '.$val; } - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + $where = empty($where) ? '' : ' WHERE '.implode(' ', $where); - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; + if ( ! empty($like)) + { + $where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like); + } - return $sql; + return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where; } // -------------------------------------------------------------------- /** - * Truncate statement + * Update_Batch 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" + * Generates a platform-specific batch update string from the supplied data * - * @access public * @param string the table name + * @param array the update data + * @param array the where clause * @return string */ - function _truncate($table) + protected function _update_batch($table, $values, $index, $where = NULL) { - return "TRUNCATE ".$table; + $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).')'; } // -------------------------------------------------------------------- @@ -630,55 +555,109 @@ class CI_DB_postgre_driver extends CI_DB { * * 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 + * @param array the like clause + * @param string the limit clause (ignored) * @return string */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) + protected function _delete($table, $where = array(), $like = array(), $limit = FALSE) { - $conditions = ''; + $conditions = array(); - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); + empty($where) OR $conditions[] = implode(' ', $where); + empty($like) OR $conditions[] = implode(' ', $like); - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; + return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : ''); } // -------------------------------------------------------------------- + /** * Limit string * * Generates a platform-specific LIMIT clause * - * @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 int the number of rows to limit the query to + * @param int the offset value * @return string */ - function _limit($sql, $limit, $offset) + protected function _limit($sql, $limit, $offset) + { + return $sql.' LIMIT '.$limit.($offset == 0 ? '' : ' OFFSET '.$offset); + } + + // -------------------------------------------------------------------- + + /** + * Where + * + * Called by where() or or_where() + * + * @param mixed + * @param mixed + * @param string + * @return object + * + */ + protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) { - $sql .= "LIMIT ".$limit; + $type = $this->_group_get_type($type); - if ($offset > 0) + if ( ! is_array($key)) { - $sql .= " OFFSET ".$offset; + $key = array($key => $value); } - return $sql; + // If the escape value was not set will will base it on the global setting + if ( ! is_bool($escape)) + { + $escape = $this->_protect_identifiers; + } + + foreach ($key as $k => $v) + { + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type; + + if (is_null($v) && ! $this->_has_operator($k)) + { + // value appears not to have been set, assign the test to IS NULL + $k .= ' IS NULL'; + } + + if ( ! is_null($v)) + { + if ($escape === TRUE) + { + $k = $this->protect_identifiers($k, FALSE, $escape); + $v = ' '.$this->escape($v); + } + elseif (is_bool($v)) + { + $v = ($v ? ' TRUE' : ' FALSE'); + } + + if ( ! $this->_has_operator($k)) + { + $k .= ' = '; + } + } + else + { + $k = $this->protect_identifiers($k, FALSE, $escape); + } + + $this->qb_where[] = $prefix.$k.$v; + if ($this->qb_caching === TRUE) + { + $this->qb_cache_where[] = $prefix.$k.$v; + $this->qb_cache_exists[] = 'where'; + } + + } + + return $this; } // -------------------------------------------------------------------- @@ -686,18 +665,14 @@ class CI_DB_postgre_driver extends CI_DB { /** * Close DB Connection * - * @access public - * @param resource * @return void */ - function _close($conn_id) + protected function _close() { - @pg_close($conn_id); + @pg_close($this->conn_id); } - } - /* End of file postgre_driver.php */ /* Location: ./system/database/drivers/postgre/postgre_driver.php */
\ No newline at end of file |