<?php
// $Id: database.pdo_pgsql.inc,v 1.3 2007/12/11 07:21:32 hswong3i Exp $
/**
* @file
* Database interface code for PostgreSQL database servers.
*/
/**
* @ingroup database
* @{
*/
// Include functions shared between pgsql and pdo_pgsql.
require_once './includes/common.postgresql.inc';
// Include schema API shared between pgsql and pdo_pgsql.
require_once './includes/schema.postgresql.inc';
/**
* Initialize a database connection.
*/
function db_connect($url) {
// Check if PDO PostgreSQL support is present in PHP.
if (!(class_exists('PDO') && extension_loaded('pdo_pgsql'))) {
_db_error_page('Unable to use the PostgreSQL database because the pdo_pgsql extension for PHP is not installed. Check your php.ini to see how you can enable it.');
}
$url = parse_url($url);
// Decode url-encoded information in the db connection string.
$username = isset($url['user']) ? urldecode($url['user']) : NULL;
$password = isset($url['pass']) ? urldecode($url['pass']) : NULL;
// Build pdo_pgsql connection string and allow for non-standard PostgreSQL port.
$dsn[] = isset($url['host']) ? 'host='. urldecode($url['host']) : NULL;
$dsn[] = isset($url['path']) ? 'dbname='. substr(urldecode($url['path']), 1) : NULL;
$dsn[] = isset($url['port']) ? 'port='. urldecode($url['port']) : NULL;
$dsn = 'pgsql:'. implode(' ', $dsn);
$driver_options = array(
// Throw a PDOException and set its properties to reflect the error code
// and error information.
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
// Leave column names as returned by the database driver.
PDO::ATTR_CASE => PDO::CASE_NATURAL,
);
try {
$connection = new PDO($dsn, $username, $password, $driver_options);
} catch (PDOExecption $e) {
_db_error_page($e->getMessage());
}
return $connection;
}
/**
* Runs a basic query in the active database.
*
* User-supplied arguments to the query should be passed in as separate
* parameters so that they can be properly escaped to avoid SQL injection
* attacks.
*
* @param $query
* A string containing an SQL query.
* @param ...
* A variable number of arguments which are substituted into the query
* using printf() syntax. Instead of a variable number of query arguments,
* you may also pass a single array containing the query arguments.
*
* Valid %-modifiers are: %s, %d, %f and %b (binary data, do not enclose
* in '').
*
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
* and TRUE values to decimal 1.
*
* @return
* A database query result resource, or FALSE if the query was not
* executed correctly.
*/
function db_query($query) {
$args = func_get_args();
array_shift($args);
$query = db_prefix_tables($query);
$query = db_escape_quote($query);
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
$args = $args[0];
}
_db_query_callback(array('query' => $query, 'args' => $args), TRUE);
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
$args = _db_query_callback(NULL, TRUE);
return _db_query(array('query' => $query, 'args' => $args));
}
/**
* Helper function for db_query().
*/
function _db_query($query, $debug = 0) {
global $active_db, $last_result, $queries, $user;
// Expand $query if it is in array format.
$args = array();
if (is_array($query)) {
$args = $query['args'];
$query = $query['query'];
// Check if locator exists in query, or else unset it.
foreach ($args as $key => $value) {
if (!is_array($args[$key]) || !isset($args[$key]['locator']) || !strstr($query, $args[$key]['locator'])) {
unset($args[$key]);
}
}
}
if (variable_get('dev_query', 0)) {
list($usec, $sec) = explode(' ', microtime());
$timer = (float)$usec + (float)$sec;
// If devel.module query logging is enabled, prepend a comment with the username and calling function
// to the SQL string. This is useful when running mysql's SHOW PROCESSLIST to learn what exact
// code is issueing the slow query.
$bt = debug_backtrace();
// t() may not be available yet so we don't wrap 'Anonymous'.
$name = $user->uid ? $user->name : variable_get('anonymous', 'Anonymous');
// str_replace() to prevent SQL injection via username or anonymous name.
$name = str_replace(array('*', '/'), '', $name);
$query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query;
}
$stmt = $active_db->prepare($query);
if ($stmt === FALSE) {
$error = $active_db->errorInfo();
if (is_array($error) && $error[0] != 0 && isset($error[2])) {
// Indicate to drupal_error_handler that this is a database error.
${DB_ERROR} = TRUE;
trigger_error(check_plain($error[2] ."\nquery: ". $query), E_USER_WARNING);
}
return FALSE;
}
if (count($args)) {
foreach ($args as $key => $value) {
if (isset($args[$key]['type'])) {
$stmt->bindValue($args[$key]['locator'], $args[$key]['data'], $args[$key]['type']);
}
else {
$stmt->bindValue($args[$key]['locator'], $args[$key]['data']);
}
}
}
// Execute the statement.
$result = $stmt->execute();
if ($result === FALSE) {
$error = $stmt->errorInfo();
if (is_array($error) && $error[0] != 0 && isset($error[2])) {
// Indicate to drupal_error_handler that this is a database error.
${DB_ERROR} = TRUE;
trigger_error(check_plain($error[2] ."\nquery: ". $query), E_USER_WARNING);
}
return FALSE;
}
if (variable_get('dev_query', 0)) {
$query = $bt[2]['function'] ."\n". $query;
list($usec, $sec) = explode(' ', microtime());
$stop = (float)$usec + (float)$sec;
$diff = $stop - $timer;
$queries[] = array($query, $diff);
}
if ($debug) {
$error = $active_db->errorInfo();
print '<p>query: '. $query .'<br />error:'. $error[2] .'</p>';
}
$last_result = $stmt;
return $last_result;
}
/**
* Fetch one result row from the previous query as an object.
*
* @param $result
* A database query result resource, as returned from db_query().
* @return
* An object representing the next row of the result, or FALSE. The attributes
* of this object are the table fields selected by the query.
*/
function db_fetch_object($result) {
if ($result) {
$object = $result->fetch(PDO::FETCH_OBJ);
return isset($object) ? $object : FALSE;
}
return FALSE;
}
/**
* Fetch one result row from the previous query as an array.
*
* @param $result
* A database query result resource, as returned from db_query().
* @return
* An associative array representing the next row of the result, or FALSE.
* The keys of this object are the names of the table fields selected by the
* query, and the values are the field values for this result row.
*/
function db_fetch_array($result) {
if ($result) {
$array = $result->fetch(PDO::FETCH_ASSOC);
return isset($array) ? $array : FALSE;
}
return FALSE;
}
/**
* Return an individual result field from the previous query.
*
* Only use this function if exactly one field is being selected; otherwise,
* use db_fetch_object() or db_fetch_array().
*
* @param $result
* A database query result resource, as returned from db_query().
* @return
* The resulting field or FALSE.
*/
function db_result($result) {
if ($result) {
$array = $result->fetch(PDO::FETCH_NUM);
return isset($array[0]) ? $array[0] : FALSE;
}
return FALSE;
}
/**
* Determine whether the previous query caused an error.
*/
function db_error() {
global $last_result;
$error = $last_result->errorInfo();
if (is_array($error) && isset($error[2])) {
return $error[2];
}
}
/**
* Determine the number of rows changed by the preceding query.
*/
function db_affected_rows() {
global $last_result;
return $last_result->rowCount();
}
/*
* Prepare user input for use in a database query, preventing SQL injection attacks.
*
* @param $data
* Data to encode. Return with processed array pattern for variable binding.
* @param $locator
* Locator for variable binding.
* @return
* Locator for variable binding.
*/
function db_escape_decimal(&$data, $locator) {
$data = array(
'locator' => $locator,
'data' => (int) $data,
);
return $locator;
}
/**
* Prepare user input for use in a database query, preventing SQL injection attacks.
*
* @param $data
* Data to encode. Return with processed array pattern for variable binding.
* @param $locator
* Locator for variable binding.
* @return
* Locator for variable binding.
*/
function db_escape_float(&$data, $locator) {
$data = array(
'locator' => $locator,
'data' => (float) $data,
);
return $locator;
}
/**
* Prepare user input for use in a database query, preventing SQL injection attacks.
*
* @param $data
* Data to encode. Return with processed array pattern for variable binding.
* @param $locator
* Locator for variable binding.
* @return
* Locator for variable binding.
*/
function db_escape_string(&$data, $locator) {
$data = array(
'locator' => $locator,
'data' => $data,
'type' => PDO::PARAM_STR,
);
return $locator;
}
/**
* Returns a properly formatted Binary Large OBject value.
*
* @param $data
* Data to encode. Return with processed array pattern for variable binding.
* @param $locator
* Locator for variable binding.
* @return
* Locator for variable binding.
*/
function db_encode_blob(&$data, $locator) {
// LOB for PgSQL must define by PDO::PARAM_LOB, or else will case error
// during INSER/UPDATE. So we need to use Oracle style locator-data-type
// variable binding for pdo_pgsql.
$data = array(
'locator' => $locator,
'data' => $data,
'type' => PDO::PARAM_LOB,
);
return $locator;
}
/**
* Returns text from a Binary Large OBject value.
*
* @param $data
* Data to decode.
* @return
* Decoded data.
*/
function db_decode_blob($data) {
// pdo_pgsql return bytea as stream, so we just need to use Streams API.
// Check http://bugs.php.net/bug.php?id=37124 for more information.
return stream_get_contents($data);
}
/**
* @} End of "ingroup database".
*/