<?php
// $Id: common.postgresql.inc,v 1.2 2007/12/11 07:21:32 hswong3i Exp $
/**
* @file
* Functions shared between pgsql and pdo_pgsql database engines.
*/
/**
* @ingroup database
* @{
*/
/**
* Indicates the string to use to quote identifiers and names.
*/
define('DB_QUOTE_OPERATOR', '"');
/**
* Indicates the concatenation operator.
*/
define('DB_CONCAT_OPERATOR', '||');
/**
* Indicates the name of the SQL strtoupper function.
*/
define('DB_UPPER', 'UPPER');
/**
* Indicates the SQL to generate a random number between 0.00 and 1.00.
*/
define('DB_RAND', 'RANDOM()');
/**
* Indicates the name of the SQL strlen function.
*/
define('DB_STRLEN', 'LENGTH');
/**
* Indicates the name of the SQL substr function.
*/
define('DB_SUBSTR', 'SUBSTRING');
/**
* Replace all escape characters in a query, based on the database specific
* implementation.
*
* Queries sent to Drupal should wrap all identifiers and names in square
* brackets. This function will search for this syntax and replace it as
* corresponding escape characters, based on the database specific
* requirement.
*/
function db_escape_quote($sql) {
return preg_replace('/\[([A-Za-z0-9_]+)\]/', DB_QUOTE_OPERATOR .'\1'. DB_QUOTE_OPERATOR, $sql);
}
/**
* Return a portably concatenate strings.
*
* @param ...
* Variable number of string parameters.
* @return
* Portably concatenate strings.
*/
function db_concat() {
$args = func_get_args();
return implode(DB_CONCAT_OPERATOR, $args);
}
/**
* Returns a string that is the equivalent of MySQL IFNULL or Oracle NVL.
*
* @return
* If $expr1 is not NULL, returns $expr1; otherwise it returns $expr2.
*/
function db_if_null($expr1, $expr2) {
return " COALESCE($expr1, $expr2) ";
}
/**
* Report database status.
*/
function db_status_report() {
$t = get_t();
$version = db_version();
$form['postgresql'] = array(
'title' => $t('PostgreSQL database'),
'value' => $version,
);
if (version_compare($version, DRUPAL_MINIMUM_POSTGRESQL) < 0) {
$form['postgresql']['severity'] = REQUIREMENT_ERROR;
$form['postgresql']['description'] = $t('Your PostgreSQL Server is too old. Drupal requires at least PostgreSQL %version.', array('%version' => DRUPAL_MINIMUM_POSTGRESQL));
}
return $form;
}
/**
* Returns the version of the database server currently in use.
*
* @return Database server version
*/
function db_version() {
return db_result(db_query("SHOW SERVER_VERSION"));
}
/**
* Insert a row of record into database.
*
* @param $table
* Table to insert.
* @param $values
* An array containing the insert values. Each element of the array
* should be an associatie array with the following keys:
* - "field": The database field represented in the table column.
* - "placeholder": The placeholder of the table column, using printf()
* syntax. Valid %-modifiers are: %d, %f, %s and %b.
* - "data": The data to insert into the table column.
* @return
* A database query result resource, or FALSE if the query was not
* executed correctly.
*/
function db_query_insert($table, $values) {
$fields = array();
$placeholders = array();
$data = array();
foreach ($values as $value) {
$fields[] = $value['field'];
$placeholders[] = $value['placeholder'];
$data[] = $value['data'];
}
if (!count($fields)) {
return FALSE;
}
$query = "INSERT INTO [{". $table ."}] ([". implode('], [', $fields) ."]) VALUES (". implode(', ', $placeholders) .")";
return db_query($query, $data);
}
/**
* Update a row of record in database.
*
* @param $table
* Table to update.
* @param $values
* An array containing the update values. Each element of the array
* should be an associatie array with the following keys:
* - "field": The database field represented in the table column.
* - "placeholder": The placeholder of the table column, using printf()
* syntax. Valid %-modifiers are: %d, %f, %s and %b.
* - "data": The data to insert into the table column.
* @param $where_clause
* A string containing an update condition query (where clause).
* @param ...
* A variable number of arguments which are substituted into the query
* WHERE condition, 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: %d, %f and %s.
*
* 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_update($table, $values, $where_clause = NULL) {
$args = func_get_args();
$args = array_slice($args, 3);
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
$args = $args[0];
}
$fields = array();
$data = array();
foreach ($values as $value) {
$fields[] = '['. $value['field'] .'] = '. $value['placeholder'];
$data[] = $value['data'];
}
if (!count($fields)) {
return FALSE;
}
$query = "UPDATE [{". $table ."}] SET ". implode(', ', $fields);
if ($where_clause) {
$query .= " WHERE ". $where_clause;
$data = array_merge($data, $args);
}
return db_query($query, $data);
}
/**
* Delete a row of record from database.
*
* @param $table
* Table to delete.
* @param $where_clause
* A string containing an update condition query (where clause).
* @param ...
* A variable number of arguments which are substituted into the query
* WHERE condition, 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: %d, %f and %s.
*
* 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_delete($table, $where_clause = NULL) {
$args = func_get_args();
$args = array_slice($args, 2);
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
$args = $args[0];
}
$data = array();
$query = "DELETE FROM [{". $table ."}]";
if ($where_clause) {
$query .= " WHERE ". $where_clause;
$data = $args;
}
return db_query($query, $data);
}
/**
* Returns the last insert id. This function is thread safe.
*
* @param $table
* The name of the table you inserted into.
* @param $field
* The name of the autoincrement field.
*/
function db_last_insert_id($table, $field) {
return db_result(db_query("SELECT CURRVAL('{". db_escape_table($table) ."}_". db_escape_table($field) ."_seq')"));
}
/**
* Runs a limited-range query in the active database.
*
* Use this as a substitute for db_query() when a subset of the query
* is to be returned.
* 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.
*
* @param $from
* The first result row to return.
* @param $count
* The maximum number of result rows to return.
* @return
* A database query result resource, or FALSE if the query was not executed
* correctly.
*/
function db_query_range($query) {
$args = func_get_args();
$count = array_pop($args);
$from = array_pop($args);
array_shift($args);
$query .= ' LIMIT '. (int) $count .' OFFSET '. (int) $from;
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
$args = $args[0];
}
return db_query($query, $args);
}
/**
* Runs a SELECT query and stores its results in a temporary table.
*
* Use this as a substitute for db_query() when the results need to stored
* in a temporary table. Temporary tables exist for the duration of the page
* request.
* 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.
*
* Note that if you need to know how many results were returned, you should do
* a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does
* not give consistent result across different database types in this case.
*
* @param $query
* A string containing a normal SELECT SQL query.
* @param ...
* A variable number of arguments which are substituted into the query
* using printf() syntax. The query arguments can be enclosed in one
* array instead.
* 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.
*
* @param $table
* The name of the temporary table to select into. This name will not be
* prefixed as there is no risk of collision.
* @return
* A database query result resource, or FALSE if the query was not executed
* correctly.
*/
function db_query_temporary($query) {
$args = func_get_args();
$tablename = array_pop($args);
array_shift($args);
$query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' AS SELECT', $query);
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
$args = $args[0];
}
return db_query($query, $args);
}
/**
* Lock a table.
* This function automatically starts a transaction.
*/
function db_lock_table($table) {
db_query('LOCK TABLE [{'. db_escape_table($table) .'}] IN EXCLUSIVE MODE');
}
/**
* Unlock all locked tables.
* This function automatically commits a transaction.
*/
function db_unlock_tables() {
db_query('COMMIT');
}
/**
* Check if a table exists.
*/
function db_table_exists($table) {
return (bool) db_result(db_query("SELECT COUNT(*) FROM [pg_class] WHERE [relname] = '{". db_escape_table($table) ."}'"));
}
/**
* Check if a column exists in the given table.
*/
function db_column_exists($table, $column) {
return (bool) db_result(db_query("SELECT COUNT([pg_attribute].[attname]) FROM [pg_class], [pg_attribute] WHERE [pg_attribute].[attrelid] = [pg_class].[oid] AND [pg_class].[relname] = '{". db_escape_table($table) ."}' AND [attname] = '". db_escape_table($column) ."'"));
}
/**
* Wraps the given table.field entry with a DISTINCT(). The wrapper is added to
* the SELECT list entry of the given query and the resulting query is returned.
* This function only applies the wrapper if a DISTINCT doesn't already exist in
* the query.
*
* @param $table Table containing the field to set as DISTINCT
* @param $field Field to set as DISTINCT
* @param $query Query to apply the wrapper to
* @return SQL query with the DISTINCT wrapper surrounding the given table.field.
*/
function db_distinct_field($table, $field, $query) {
$field_to_select = 'DISTINCT ON (['. $table .'].['. $field ."]) [$table].[$field]";
// (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT).
$query = preg_replace('/(SELECT.*)(?:\['. $table .'\]\.|\s)(?<!DISTINCT\()(?<!DISTINCT\(\['. $table .'\]\.)\['. $field .'\](.*FROM )/AUsi', '\1 '. $field_to_select .'\2', $query);
$query = preg_replace('/(ORDER BY )(?!\['. $table .'\]\.\['. $field .'\])/', '\1'."[$table].[$field], ", $query);
return $query;
}
/**
* Verify if the database is set up correctly.
*/
function db_check_setup() {
$t = get_t();
$encoding = db_result(db_query('SHOW server_encoding'));
if (!in_array(strtolower($encoding), array('unicode', 'utf8'))) {
drupal_set_message($t('Your PostgreSQL database is set up with the wrong character encoding (%encoding). It is possible it will not work as expected. It is advised to recreate it with UTF-8/Unicode encoding. More information can be found in the <a href="@url">PostgreSQL documentation</a>.', array('%encoding' => $encoding, '@url' => 'http://www.postgresql.org/docs/7.4/interactive/multibyte.html')), 'status');
}
}
/**
* @} End of "ingroup database".
*/