drupal/includes/database.inc

179 lines
5.5 KiB
PHP
Raw Normal View History

<?php
// $Id$
/**
* @file
* Wrapper for database interface code.
*/
/**
* @defgroup database Database abstraction layer
* @{
* Allow the use of different database servers using the same code base.
*
* Drupal provides a slim database abstraction layer to provide developers with
* the ability to support multiple database servers easily. The intent of this
* layer is to preserve the syntax and power of SQL as much as possible, while
* letting Drupal control the pieces of queries that need to be written
* differently for different servers and provide basic security checks.
*
* Most Drupal database queries are performed by a call to db_query() or
* db_query_range(). Module authors should also consider using pager_query() for
* queries that return results that need to be presented on multiple pages, and
* tablesort_sql() for generating appropriate queries for sortable tables.
*
* For example, one might wish to return a list of the most recent 10 nodes
* authored by a given user. Instead of directly issuing the SQL query
* @code
* SELECT n.title, n.body, n.created FROM node n WHERE n.uid = $uid LIMIT 0, 10;
* @endcode
* one would instead call the Drupal functions:
* @code
* $result = db_query_range('SELECT n.title, n.body, n.created
* FROM {node} n WHERE n.uid = %d', $uid, 0, 10);
* while ($node = db_fetch_object($result)) {
* // Perform operations on $node->body, etc. here.
* }
* @endcode
* Curly braces are used around "node" to provide table prefixing via
* db_prefix_tables(). The explicit use of a user ID is pulled out into an
* argument passed to db_query() so that SQL injection attacks from user input
* can be caught and nullified. The LIMIT syntax varies between database servers,
* so that is abstracted into db_query_range() arguments. Finally, note the
* common pattern of iterating over the result set using db_fetch_object().
*/
/**
* Append a database prefix to all tables in a query.
*
* Queries sent to Drupal should wrap all table names in curly brackets. This
* function searches for this syntax and adds Drupal's table prefix to all
* tables, allowing Drupal to coexist with other systems in the same database if
* necessary.
*
* @param $sql
* A string containing a partial or entire SQL query.
* @return
* The properly-prefixed string.
*/
function db_prefix_tables($sql) {
global $db_prefix;
if (is_array($db_prefix)) {
$prefix = $db_prefix['default'];
foreach ($db_prefix as $key => $val) {
if ($key !== 'default') {
$sql = strtr($sql, array('{'. $key. '}' => $val. $key));
}
}
}
else {
$prefix = $db_prefix;
}
return strtr($sql, array('{' => $prefix, '}' => ''));
}
/**
* Activate a database for future queries.
*
* If it is necessary to use external databases in a project, this function can
* be used to change where database queries are sent. If the database has not
* yet been used, it is initialized using the URL specified for that name in
* Drupal's configuration file. If this name is not defined, a duplicate of the
* default connection is made instead.
*
* Be sure to change the connection back to the default when done with custom
* code.
*
* @param $name
* The name assigned to the newly active database connection. If omitted, the
* default connection will be made active.
*/
function db_set_active($name = 'default') {
global $db_url, $db_type, $active_db;
static $db_conns;
if (!isset($db_conns[$name])) {
// Initiate a new connection, using the named DB URL specified.
if (is_array($db_url)) {
$connect_url = array_key_exists($name, $db_url) ? $db_url[$name] : $db_url['default'];
}
else {
$connect_url = $db_url;
}
$db_type = substr($connect_url, 0, strpos($connect_url, '://'));
$handler = "includes/database.$db_type.inc";
if (is_file($handler)) {
include_once($handler);
}
else {
die('Unsupported database type');
}
$db_conns[$name] = db_connect($connect_url);
}
// Set the active connection.
$active_db = $db_conns[$name];
}
- Patch #13581 by Steven: Db_query() allows a variable amount of parameters so you can pass the query arguments in. There is however an alternative syntax: instead of passing the query arguments as function arguments, you can also pass a single array with the query arguments in it. For example the following two statements are equivalent: db_query($query, $a, $b, $c); db_query($query, array($a, $b, $c)); This usage is particularly interesting when the query is constructed dynamically, and the amount of arguments to pass varies. In that case we use the second method to avoid using call_user_func_array(). This behaviour is not documented explicitly, but it is used in several places. However, db_query_range() and pager_query() do not support this syntax properly, which means there are several pieces of code which still revert to the ugly call_user_func_array() call. This patch updates db_query_range() and pager_query() so they support the array-passing method. I also added documentation about this method to each of the db functions. I also cleaned up the code for db_query (it was weird and hard to understand) and moved db_query() and db_queryd() from database.xxxxx.inc to database.inc: it was the same between both mysql and pgsql, as it doesn't do anything database specific. It just prefixes the tables and inserts the arguments. The actual db query is performed in _db_query(), which is still in database.xxxxx.inc. Finally, I updated several places with the new syntax, and the code is a lot cleaner. For example: - array_unshift($params, "SELECT u.* FROM {users} u WHERE $query u.status < 3"); - $params[] = 0; - $params[] = 1; - $result = call_user_func_array('db_query_range', $params); + $result = db_query_range("SELECT u.* FROM {users} u WHERE $query u.status < 3", $params, 0, 1); and - return call_user_func_array('db_query_range', array_merge(array($query), $args, array((int)$pager_from_array[$element], (int)$limit))); + return db_query_range($query, $args, (int)$pager_from_array[$element], (int)$limit); I've tested it on mysql. I didn't alter the actual db behaviour, so pgsql should be okay too. This patch is important because many people avoid the call_user_func_array() method and put data directly into the db query. This is very, very bad because the database prefix will be applied to it, and strip out braces. It's also generally bad form as you have to call check_query() yourself. With the new, documented syntax, there is no more excuse to put data directly in the query.
2004-11-29 13:13:29 +00:00
/**
* 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.
* @return
* A database query result resource, or FALSE if the query was not executed
* correctly.
*/
function db_query($query) {
$args = func_get_args();
$query = db_prefix_tables($query);
if (count($args) > 1) {
if (is_array($args[1])) {
$args = array_merge(array($query), $args[1]);
}
$args = array_map('db_escape_string', $args);
$args[0] = $query;
$query = call_user_func_array('sprintf', $args);
}
return _db_query($query);
}
/**
* Debugging version of db_query().
*
* Echoes the query to the browser.
*/
function db_queryd($query) {
$args = func_get_args();
$query = db_prefix_tables($query);
if (count($args) > 1) {
if (is_array($args[1])) {
$args = array_merge(array($query), $args[1]);
}
$args = array_map('db_escape_string', $args);
$args[0] = $query;
$query = call_user_func_array('sprintf', $args);
}
return _db_query($query, 1);
}
/**
* @} End of "defgroup database".
*/
// Initialize the default database.
db_set_active();
?>