2001-03-10 11:07:52 +00:00
< ? php
2003-07-10 17:46:44 +00:00
// $Id$
2004-07-14 19:15:25 +00:00
/**
* @ file
* Wrapper for database interface code .
*/
2007-08-29 18:38:55 +00:00
/**
* A hash value to check when outputting database errors , md5 ( 'DB_ERROR' ) .
*
* @ see drupal_error_handler
*/
define ( 'DB_ERROR' , 'a515ac9c2796ca0e23adbe92c68fc9fc' );
2004-07-14 19:15:25 +00:00
/**
* @ defgroup database Database abstraction layer
* @ {
2004-09-09 05:51:08 +00:00
* Allow the use of different database servers using the same code base .
2004-07-14 19:15:25 +00:00
*
* 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 () .
*/
2007-05-25 12:46:46 +00:00
/**
* Perform an SQL query and return success or failure .
*
* @ param $sql
* A string containing a complete SQL query . %- substitution
* parameters are not supported .
* @ return
* An array containing the keys :
* success : a boolean indicating whether the query succeeded
* query : the SQL query executed , passed through check_plain ()
*/
function update_sql ( $sql ) {
$result = db_query ( $sql , true );
return array ( 'success' => $result !== FALSE , 'query' => check_plain ( $sql ));
}
2004-07-14 19:15:25 +00:00
/**
* 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 .
*/
2003-07-10 17:46:44 +00:00
function db_prefix_tables ( $sql ) {
global $db_prefix ;
2003-08-26 06:44:37 +00:00
if ( is_array ( $db_prefix )) {
2005-04-08 14:24:03 +00:00
if ( array_key_exists ( 'default' , $db_prefix )) {
$tmp = $db_prefix ;
unset ( $tmp [ 'default' ]);
foreach ( $tmp as $key => $val ) {
2007-04-13 08:56:59 +00:00
$sql = strtr ( $sql , array ( '{' . $key . '}' => $val . $key ));
2003-08-26 06:44:37 +00:00
}
2005-04-08 14:24:03 +00:00
return strtr ( $sql , array ( '{' => $db_prefix [ 'default' ], '}' => '' ));
}
else {
foreach ( $db_prefix as $key => $val ) {
2007-04-13 08:56:59 +00:00
$sql = strtr ( $sql , array ( '{' . $key . '}' => $val . $key ));
2005-04-08 14:24:03 +00:00
}
return strtr ( $sql , array ( '{' => '' , '}' => '' ));
2003-08-26 06:44:37 +00:00
}
}
else {
2005-04-08 14:24:03 +00:00
return strtr ( $sql , array ( '{' => $db_prefix , '}' => '' ));
2003-08-26 06:44:37 +00:00
}
2003-07-10 17:46:44 +00:00
}
2000-06-11 13:28:26 +00:00
2004-04-30 05:12:46 +00:00
/**
2004-07-14 19:15:25 +00:00
* 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 .
2006-03-26 14:11:38 +00:00
*
2006-03-26 14:46:51 +00:00
* @ return the name of the previously active database or FALSE if non was found .
2004-07-14 19:15:25 +00:00
*/
2004-04-30 05:12:46 +00:00
function db_set_active ( $name = 'default' ) {
2004-05-06 20:25:48 +00:00
global $db_url , $db_type , $active_db ;
2004-04-30 05:12:46 +00:00
static $db_conns ;
2007-05-08 16:36:55 +00:00
if ( empty ( $db_url )) {
include_once 'includes/install.inc' ;
install_goto ( 'install.php' );
}
2004-04-30 05:12:46 +00:00
if ( ! isset ( $db_conns [ $name ])) {
2004-07-14 19:15:25 +00:00
// Initiate a new connection, using the named DB URL specified.
2004-04-30 05:12:46 +00:00
if ( is_array ( $db_url )) {
2004-07-14 19:15:25 +00:00
$connect_url = array_key_exists ( $name , $db_url ) ? $db_url [ $name ] : $db_url [ 'default' ];
2004-04-30 05:12:46 +00:00
}
else {
$connect_url = $db_url ;
}
2004-07-14 19:15:25 +00:00
$db_type = substr ( $connect_url , 0 , strpos ( $connect_url , '://' ));
2006-03-26 14:11:38 +00:00
$handler = " ./includes/database. $db_type .inc " ;
2004-04-30 05:12:46 +00:00
2004-12-30 13:13:22 +00:00
if ( is_file ( $handler )) {
2005-09-08 19:19:01 +00:00
include_once $handler ;
2004-04-30 05:12:46 +00:00
}
else {
2005-07-27 01:58:43 +00:00
drupal_maintenance_theme ();
drupal_set_title ( 'Unsupported database type' );
2005-09-12 20:13:04 +00:00
print theme ( 'maintenance_page' , '<p>The database type ' . theme ( 'placeholder' , $db_type ) . ' is unsupported . Please use either < var > mysql </ var > for MySQL 3. x & amp ; 4.0 . x databases , < var > mysqli </ var > for MySQL 4.1 . x + databases , or < var > pgsql </ var > for PostgreSQL databases . The database information is in your < code > settings . php </ code > file .</ p >
2005-07-27 01:58:43 +00:00
< p > For more help , see the < a href = " http://drupal.org/node/258 " > Installation and upgrading handbook </ a >. If you are unsure what these terms mean you should probably contact your hosting provider .</ p > ' );
exit ;
2004-04-30 05:12:46 +00:00
}
$db_conns [ $name ] = db_connect ( $connect_url );
}
2006-03-26 14:11:38 +00:00
$previous_db = $active_db ;
2004-07-14 19:15:25 +00:00
// Set the active connection.
2004-04-30 05:12:46 +00:00
$active_db = $db_conns [ $name ];
2006-03-26 14:11:38 +00:00
2006-03-26 18:19:04 +00:00
return array_search ( $previous_db , $db_conns );
2001-03-03 22:59:11 +00:00
}
2005-11-27 11:52:08 +00:00
/**
* Helper function for db_query () .
*/
function _db_query_callback ( $match , $init = FALSE ) {
static $args = NULL ;
if ( $init ) {
$args = $match ;
return ;
}
switch ( $match [ 1 ]) {
2006-07-05 11:45:51 +00:00
case '%d' : // We must use type casting to int to convert FALSE/NULL/(TRUE?)
2005-11-27 11:52:08 +00:00
return ( int ) array_shift ( $args ); // We don't need db_escape_string as numbers are db-safe
case '%s' :
return db_escape_string ( array_shift ( $args ));
case '%%' :
return '%' ;
case '%f' :
return ( float ) array_shift ( $args );
case '%b' : // binary data
return db_encode_blob ( array_shift ( $args ));
}
}
2007-09-27 16:52:00 +00:00
/**
* Generate placeholders for an array of query arguments of a single type .
*
* Given a Schema API field type , return correct %- placeholders to
* embed in a query
*
* @ param $arguments
* An array with at least one element .
* @ param $type
* The Schema API type of a field ( e . g . 'int' , 'text' , or 'varchar' ) .
*/
function db_placeholders ( $arguments , $type = 'int' ) {
2007-10-02 16:15:56 +00:00
$placeholder = db_type_placeholder ( $type );
2007-09-27 16:52:00 +00:00
return implode ( ',' , array_fill ( 0 , count ( $arguments ), $placeholder ));
}
2006-12-11 22:58:17 +00:00
/**
* Indicates the place holders that should be replaced in _db_query_callback () .
*/
2005-11-27 11:52:08 +00:00
define ( 'DB_QUERY_REGEXP' , '/(%d|%s|%%|%f|%b)/' );
2005-01-29 22:02:37 +00:00
/**
* Helper function for db_rewrite_sql .
*
2007-02-27 12:01:37 +00:00
* Collects JOIN and WHERE statements via hook_db_rewrite_sql ()
2005-01-29 22:02:37 +00:00
* Decides whether to select primary_key or DISTINCT ( primary_key )
*
* @ param $query
2005-01-30 17:48:52 +00:00
* Query to be rewritten .
2005-01-29 22:02:37 +00:00
* @ param $primary_table
2007-07-01 21:39:07 +00:00
* Name or alias of the table which has the primary key field for this query .
* Possible values are : blocks , comments , forum , node , menu , term_data ,
* vocabulary .
2005-01-31 20:45:10 +00:00
* @ param $primary_field
* Name of the primary field .
2005-01-29 22:02:37 +00:00
* @ param $args
2005-01-30 17:48:52 +00:00
* Array of additional arguments .
2005-01-29 22:02:37 +00:00
* @ return
2005-01-30 17:48:52 +00:00
* An array : join statements , where statements , field or DISTINCT ( field ) .
2005-01-29 22:02:37 +00:00
*/
2005-01-31 20:45:10 +00:00
function _db_rewrite_sql ( $query = '' , $primary_table = 'n' , $primary_field = 'nid' , $args = array ()) {
2005-01-29 22:02:37 +00:00
$where = array ();
$join = array ();
$distinct = FALSE ;
foreach ( module_implements ( 'db_rewrite_sql' ) as $module ) {
2005-01-31 20:45:10 +00:00
$result = module_invoke ( $module , 'db_rewrite_sql' , $query , $primary_table , $primary_field , $args );
2005-12-14 20:10:45 +00:00
if ( isset ( $result ) && is_array ( $result )) {
2005-01-29 22:02:37 +00:00
if ( isset ( $result [ 'where' ])) {
2006-03-08 15:13:20 +00:00
$where [] = $result [ 'where' ];
2005-01-29 22:02:37 +00:00
}
if ( isset ( $result [ 'join' ])) {
2006-03-08 15:13:20 +00:00
$join [] = $result [ 'join' ];
2005-01-29 22:02:37 +00:00
}
if ( isset ( $result [ 'distinct' ]) && $result [ 'distinct' ]) {
$distinct = TRUE ;
}
}
elseif ( isset ( $result )) {
2006-03-08 15:13:20 +00:00
$where [] = $result ;
2005-01-29 22:02:37 +00:00
}
}
2005-02-19 22:24:24 +00:00
$where = empty ( $where ) ? '' : '(' . implode ( ') AND (' , $where ) . ')' ;
$join = empty ( $join ) ? '' : implode ( ' ' , $join );
2005-01-29 22:02:37 +00:00
2005-02-19 22:24:24 +00:00
return array ( $join , $where , $distinct );
2005-01-29 22:02:37 +00:00
}
/**
2006-03-08 15:13:20 +00:00
* Rewrites node , taxonomy and comment queries . Use it for listing queries . Do not
* use FROM table1 , table2 syntax , use JOIN instead .
2005-01-29 22:02:37 +00:00
*
* @ param $query
2005-01-30 17:48:52 +00:00
* Query to be rewritten .
2005-01-29 22:02:37 +00:00
* @ param $primary_table
2006-04-07 11:28:20 +00:00
* Name or alias of the table which has the primary key field for this query . Possible values are : comments , forum , node , menu , term_data , vocabulary .
2005-01-31 20:45:10 +00:00
* @ param $primary_field
* Name of the primary field .
2005-01-29 22:02:37 +00:00
* @ param $args
2005-01-30 17:48:52 +00:00
* An array of arguments , passed to the implementations of hook_db_rewrite_sql .
2005-01-29 22:02:37 +00:00
* @ return
* The original query with JOIN and WHERE statements inserted from hook_db_rewrite_sql implementations . nid is rewritten if needed .
*/
2005-01-31 20:45:10 +00:00
function db_rewrite_sql ( $query , $primary_table = 'n' , $primary_field = 'nid' , $args = array ()) {
list ( $join , $where , $distinct ) = _db_rewrite_sql ( $query , $primary_table , $primary_field , $args );
2005-01-29 22:02:37 +00:00
2005-01-31 20:45:10 +00:00
if ( $distinct ) {
2006-10-23 18:38:45 +00:00
$query = db_distinct_field ( $primary_table , $primary_field , $query );
2005-01-29 22:02:37 +00:00
}
2005-01-31 20:45:10 +00:00
2006-03-08 15:13:20 +00:00
if ( ! empty ( $where ) || ! empty ( $join )) {
if ( ! empty ( $where )) {
2007-01-03 10:59:02 +00:00
$new = " WHERE $where " ;
2006-03-08 15:13:20 +00:00
}
$new = " $join $new " ;
2005-01-31 20:45:10 +00:00
if ( strpos ( $query , 'WHERE' )) {
2007-01-03 10:59:02 +00:00
$query = str_replace ( 'WHERE' , $new . 'AND (' , $query );
$insert = ') ' ;
2005-01-31 20:45:10 +00:00
}
2007-01-03 10:59:02 +00:00
else {
$insert = $new ;
}
if ( strpos ( $query , 'GROUP' )) {
2005-01-31 20:45:10 +00:00
$replace = 'GROUP' ;
}
2007-07-12 09:21:30 +00:00
elseif ( strpos ( $query , 'HAVING' )) {
$replace = 'HAVING' ;
}
2005-01-31 20:45:10 +00:00
elseif ( strpos ( $query , 'ORDER' )) {
$replace = 'ORDER' ;
}
elseif ( strpos ( $query , 'LIMIT' )) {
$replace = 'LIMIT' ;
}
else {
2007-01-03 10:59:02 +00:00
$query .= $insert ;
2005-01-31 20:45:10 +00:00
}
if ( isset ( $replace )) {
2007-01-03 10:59:02 +00:00
$query = str_replace ( $replace , $insert . $replace , $query );
2005-01-31 20:45:10 +00:00
}
2005-01-29 22:02:37 +00:00
}
2005-01-31 20:45:10 +00:00
2005-01-29 22:02:37 +00:00
return $query ;
}
2006-04-27 20:38:49 +00:00
/**
* Restrict a dynamic tablename to safe characters .
*
* Only keeps alphanumeric and underscores .
*/
function db_escape_table ( $string ) {
return preg_replace ( '/[^A-Za-z0-9_]+/' , '' , $string );
}
2004-07-14 19:15:25 +00:00
/**
2004-09-09 05:51:08 +00:00
* @ } End of " defgroup database " .
2004-07-14 19:15:25 +00:00
*/
2004-04-30 05:12:46 +00:00
2007-05-25 12:46:46 +00:00
/**
* @ defgroup schemaapi Schema API
* @ {
*
* A Drupal schema definition is an array structure representing one or
* more tables and their related keys and indexes . A schema is defined by
* hook_schema (), which usually lives in a modulename . schema file .
*
2007-07-02 14:41:37 +00:00
* By implementing hook_schema () and specifying the tables your module
2007-05-25 12:46:46 +00:00
* declares , you can easily create and drop these tables on all
* supported database engines . You don ' t have to deal with the
* different SQL dialects for table creation and alteration of the
* supported database engines .
*
* hook_schema () should return an array with a key for each table that
* the module defines .
*
2007-09-14 10:43:26 +00:00
* The following keys are defined :
2007-05-25 12:46:46 +00:00
*
2007-09-14 10:43:26 +00:00
* - 'description' : A string describing this table and its purpose .
* References to other tables should be enclosed in
* curly - brackets . For example , the node_revisions table
* description field might contain " Stores per-revision title and
* body data for each { node } . "
2007-05-25 12:46:46 +00:00
* - 'fields' : An associative array ( 'fieldname' => specification )
* that describes the table ' s database columns . The specification
* is also an array . The following specification parameters are defined :
*
2007-09-14 10:43:26 +00:00
* - 'description' : A string describing this field and its purpose .
* References to other tables should be enclosed in
* curly - brackets . For example , the node table vid field
* description might contain " Always holds the largest (most
* recent ) { node_revisions } . vid value for this nid . "
2007-05-25 12:46:46 +00:00
* - 'type' : The generic datatype : 'varchar' , 'int' , 'serial'
* 'float' , 'numeric' , 'text' , 'blob' or 'datetime' . Most types
* just map to the according database engine specific
* datatypes . Use 'serial' for auto incrementing fields . This
* will expand to 'int auto_increment' on mysql .
* - 'size' : The data size : 'tiny' , 'small' , 'medium' , 'normal' ,
* 'big' . This is a hint about the largest value the field will
* store and determines which of the database engine specific
* datatypes will be used ( e . g . on MySQL , TINYINT vs . INT vs . BIGINT ) .
* 'normal' , the default , selects the base type ( e . g . on MySQL ,
* INT , VARCHAR , BLOB , etc . ) .
*
* Not all sizes are available for all data types . See
* db_type_map () for possible combinations .
* - 'not null' : If true , no NULL values will be allowed in this
* database column . Defaults to false .
* - 'default' : The field ' s default value . The PHP type of the
* value matters : '' , '0' , and 0 are all different . If you
* specify '0' as the default value for a type 'int' field it
* will not work because '0' is a string containing the
* character " zero " , not an integer .
* - 'length' : The maximal length of a type 'varchar' or 'text'
* field . Ignored for other field types .
* - 'unsigned' : A boolean indicating whether a type 'int' , 'float'
* and 'numeric' only is signed or unsigned . Defaults to
* FALSE . Ignored for other field types .
* - 'precision' , 'scale' : For type 'numeric' fields , indicates
* the precision ( total number of significant digits ) and scale
* ( decimal digits right of the decimal point ) . Both values are
* mandatory . Ignored for other field types .
*
* All parameters apart from 'type' are optional except that type
* 'numeric' columns must specify 'precision' and 'scale' .
*
2007-07-02 14:41:37 +00:00
* - 'primary key' : An array of one or more key column specifiers ( see below )
2007-05-25 12:46:46 +00:00
* that form the primary key .
* - 'unique key' : An associative array of unique keys ( 'keyname' =>
* specification ) . Each specification is an array of one or more
* key column specifiers ( see below ) that form a unique key on the table .
* - 'indexes' : An associative array of indexes ( 'indexame' =>
* specification ) . Each specification is an array of one or more
* key column specifiers ( see below ) that form an index on the
* table .
*
* A key column specifier is either a string naming a column or an
* array of two elements , column name and length , specifying a prefix
* of the named column .
*
* As an example , here is a SUBSET of the schema definition for
* Drupal 's ' node ' table . It show four fields ( nid , vid , type , and
* title ), the primary key on field 'nid' , a unique key named 'vid' on
* field 'vid' , and two indexes , one named 'nid' on field 'nid' and
* one named 'node_title_type' on the field 'title' and the first four
* bytes of the field 'type' :
*
2007-08-06 10:25:56 +00:00
* @ code
2007-05-25 12:46:46 +00:00
* $schema [ 'node' ] = array (
* 'fields' => array (
* 'nid' => array ( 'type' => 'serial' , 'unsigned' => TRUE , 'not null' => TRUE ),
* 'vid' => array ( 'type' => 'int' , 'unsigned' => TRUE , 'not null' => TRUE , 'default' => 0 ),
* 'type' => array ( 'type' => 'varchar' , 'length' => 32 , 'not null' => TRUE , 'default' => '' ),
2007-08-06 10:25:56 +00:00
* 'title' => array ( 'type' => 'varchar' , 'length' => 128 , 'not null' => TRUE , 'default' => '' ),
2007-05-25 12:46:46 +00:00
* ),
* 'primary key' => array ( 'nid' ),
* 'unique keys' => array (
* 'vid' => array ( 'vid' )
* ),
* 'indexes' => array (
* 'nid' => array ( 'nid' ),
* 'node_title_type' => array ( 'title' , array ( 'type' , 4 )),
* ),
* );
2007-08-06 10:25:56 +00:00
* @ endcode
2007-05-25 12:46:46 +00:00
*
* @ see drupal_install_schema ()
*/
/**
* Create a new table from a Drupal table definition .
*
* @ param $ret
* Array to which query results will be added .
2007-06-26 20:24:19 +00:00
* @ param $name
* The name of the table to create .
2007-05-25 12:46:46 +00:00
* @ param $table
2007-06-26 20:24:19 +00:00
* A Schema API table definition array .
2007-05-25 12:46:46 +00:00
*/
2007-06-26 20:24:19 +00:00
function db_create_table ( & $ret , $name , $table ) {
$statements = db_create_table_sql ( $name , $table );
2007-05-25 12:46:46 +00:00
foreach ( $statements as $statement ) {
$ret [] = update_sql ( $statement );
}
}
/**
2007-09-27 16:52:00 +00:00
* Return an array of field names from an array of key / index column specifiers .
*
* This is usually an identity function but if a key / index uses a column prefix
* specification , this function extracts just the name .
2007-05-25 12:46:46 +00:00
*
* @ param $fields
* An array of key / index column specifiers .
* @ return
* An array of field names .
*/
function db_field_names ( $fields ) {
$ret = array ();
foreach ( $fields as $field ) {
if ( is_array ( $field )) {
$ret [] = $field [ 0 ];
}
else {
$ret [] = $field ;
}
}
return $ret ;
}
2007-08-26 08:27:09 +00:00
/**
2007-09-27 16:52:00 +00:00
* Given a Schema API field type , return the correct %- placeholder .
*
* Embed the placeholder in a query to be passed to db_query and and pass as an
* argument to db_query a value of the specified type .
2007-08-26 08:27:09 +00:00
*
* @ param $type
* The Schema API type of a field .
* @ return
* The placeholder string to embed in a query for that type .
*/
2007-10-02 16:15:56 +00:00
function db_type_placeholder ( $type ) {
2007-08-26 08:27:09 +00:00
switch ( $type ) {
case 'varchar' :
case 'text' :
case 'datetime' :
return '\'%s\'' ;
case 'numeric' :
// For 'numeric' values, we use '%s', not '\'%s\'' as with
// string types, because numeric values should not be enclosed
// in quotes in queries (though they can be, at least on mysql
// and pgsql). Numerics should only have [0-9.+-] and
// presumably no db's "escape string" function will mess with
// those characters.
return '%s' ;
2007-08-30 19:54:22 +00:00
2007-08-26 08:27:09 +00:00
case 'serial' :
case 'int' :
return '%d' ;
case 'float' :
return '%f' ;
2007-08-30 19:54:22 +00:00
2007-08-26 08:27:09 +00:00
case 'blob' :
return '%b' ;
}
// There is no safe value to return here, so return something that
// will cause the query to fail.
2007-10-02 16:15:56 +00:00
return 'unsupported type ' . $type . 'for db_type_placeholder' ;
2007-08-26 08:27:09 +00:00
}
2007-05-25 12:46:46 +00:00
/**
* @ } End of " defgroup schemaapi " .
*/