2 // $Id: database.inc,v 1.62.2.4 2007/07/12 06:25:47 drumm Exp $
6 * Wrapper for database interface code.
10 * @defgroup database Database abstraction layer
12 * Allow the use of different database servers using the same code base.
14 * Drupal provides a slim database abstraction layer to provide developers with
15 * the ability to support multiple database servers easily. The intent of this
16 * layer is to preserve the syntax and power of SQL as much as possible, while
17 * letting Drupal control the pieces of queries that need to be written
18 * differently for different servers and provide basic security checks.
20 * Most Drupal database queries are performed by a call to db_query() or
21 * db_query_range(). Module authors should also consider using pager_query() for
22 * queries that return results that need to be presented on multiple pages, and
23 * tablesort_sql() for generating appropriate queries for sortable tables.
25 * For example, one might wish to return a list of the most recent 10 nodes
26 * authored by a given user. Instead of directly issuing the SQL query
28 * SELECT n.title, n.body, n.created FROM node n WHERE n.uid = $uid LIMIT 0, 10;
30 * one would instead call the Drupal functions:
32 * $result = db_query_range('SELECT n.title, n.body, n.created
33 * FROM {node} n WHERE n.uid = %d', $uid, 0, 10);
34 * while ($node = db_fetch_object($result)) {
35 * // Perform operations on $node->body, etc. here.
38 * Curly braces are used around "node" to provide table prefixing via
39 * db_prefix_tables(). The explicit use of a user ID is pulled out into an
40 * argument passed to db_query() so that SQL injection attacks from user input
41 * can be caught and nullified. The LIMIT syntax varies between database servers,
42 * so that is abstracted into db_query_range() arguments. Finally, note the
43 * common pattern of iterating over the result set using db_fetch_object().
47 * Append a database prefix to all tables in a query.
49 * Queries sent to Drupal should wrap all table names in curly brackets. This
50 * function searches for this syntax and adds Drupal's table prefix to all
51 * tables, allowing Drupal to coexist with other systems in the same database if
55 * A string containing a partial or entire SQL query.
57 * The properly-prefixed string.
59 function db_prefix_tables($sql) {
62 if (is_array($db_prefix)) {
63 if (array_key_exists('default', $db_prefix)) {
65 unset($tmp['default']);
66 foreach ($tmp as $key => $val) {
67 $sql = strtr($sql, array('{'. $key. '}' => $val. $key));
69 return strtr($sql, array('{' => $db_prefix['default'], '}' => ''));
72 foreach ($db_prefix as $key => $val) {
73 $sql = strtr($sql, array('{'. $key. '}' => $val. $key));
75 return strtr($sql, array('{' => '', '}' => ''));
79 return strtr($sql, array('{' => $db_prefix, '}' => ''));
84 * Activate a database for future queries.
86 * If it is necessary to use external databases in a project, this function can
87 * be used to change where database queries are sent. If the database has not
88 * yet been used, it is initialized using the URL specified for that name in
89 * Drupal's configuration file. If this name is not defined, a duplicate of the
90 * default connection is made instead.
92 * Be sure to change the connection back to the default when done with custom
96 * The name assigned to the newly active database connection. If omitted, the
97 * default connection will be made active.
99 * @return the name of the previously active database or FALSE if non was found.
101 function db_set_active($name = 'default') {
102 global $db_url, $db_type, $active_db;
105 if (!isset($db_conns[$name])) {
106 // Initiate a new connection, using the named DB URL specified.
107 if (is_array($db_url)) {
108 $connect_url = array_key_exists($name, $db_url) ? $db_url[$name] : $db_url['default'];
111 $connect_url = $db_url;
114 $db_type = substr($connect_url, 0, strpos($connect_url, '://'));
115 $handler = "./includes/database.$db_type.inc";
117 if (is_file($handler)) {
118 include_once $handler;
121 drupal_maintenance_theme();
122 drupal_set_title('Unsupported database type');
123 print theme('maintenance_page', '<p>The database type '. theme('placeholder', $db_type) .' is unsupported. Please use either <var>mysql</var> for MySQL 3.x & 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>
124 <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>');
128 $db_conns[$name] = db_connect($connect_url);
131 $previous_db = $active_db;
132 // Set the active connection.
133 $active_db = $db_conns[$name];
135 return array_search($previous_db, $db_conns);
139 * Helper function for db_query().
141 function _db_query_callback($match, $init = FALSE) {
149 case '%d': // We must use type casting to int to convert FALSE/NULL/(TRUE?)
150 return (int) array_shift($args); // We don't need db_escape_string as numbers are db-safe
152 return db_escape_string(array_shift($args));
156 return (float) array_shift($args);
157 case '%b': // binary data
158 return db_encode_blob(array_shift($args));
163 * Indicates the place holders that should be replaced in _db_query_callback().
165 define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b)/');
168 * Runs a basic query in the active database.
170 * User-supplied arguments to the query should be passed in as separate
171 * parameters so that they can be properly escaped to avoid SQL injection
175 * A string containing an SQL query.
177 * A variable number of arguments which are substituted into the query
178 * using printf() syntax. Instead of a variable number of query arguments,
179 * you may also pass a single array containing the query arguments.
181 * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
184 * NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
185 * and TRUE values to decimal 1.
188 * A database query result resource, or FALSE if the query was not
189 * executed correctly.
191 function db_query($query) {
192 $args = func_get_args();
194 $query = db_prefix_tables($query);
195 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
198 _db_query_callback($args, TRUE);
199 $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
200 return _db_query($query);
204 * Helper function for db_rewrite_sql.
206 * Collects JOIN and WHERE statements via hook_db_rewrite_sql().
207 * Decides whether to select primary_key or DISTINCT(primary_key)
210 * Query to be rewritten.
211 * @param $primary_table
212 * 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.
213 * @param $primary_field
214 * Name of the primary field.
216 * Array of additional arguments.
218 * An array: join statements, where statements, field or DISTINCT(field).
220 function _db_rewrite_sql($query = '', $primary_table = 'n', $primary_field = 'nid', $args = array()) {
224 foreach (module_implements('db_rewrite_sql') as $module) {
225 $result = module_invoke($module, 'db_rewrite_sql', $query, $primary_table, $primary_field, $args);
226 if (isset($result) && is_array($result)) {
227 if (isset($result['where'])) {
228 $where[] = $result['where'];
230 if (isset($result['join'])) {
231 $join[] = $result['join'];
233 if (isset($result['distinct']) && $result['distinct']) {
237 elseif (isset($result)) {
242 $where = empty($where) ? '' : '('. implode(') AND (', $where) .')';
243 $join = empty($join) ? '' : implode(' ', $join);
245 return array($join, $where, $distinct);
249 * Rewrites node, taxonomy and comment queries. Use it for listing queries. Do not
250 * use FROM table1, table2 syntax, use JOIN instead.
253 * Query to be rewritten.
254 * @param $primary_table
255 * 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.
256 * @param $primary_field
257 * Name of the primary field.
259 * An array of arguments, passed to the implementations of hook_db_rewrite_sql.
261 * The original query with JOIN and WHERE statements inserted from hook_db_rewrite_sql implementations. nid is rewritten if needed.
263 function db_rewrite_sql($query, $primary_table = 'n', $primary_field = 'nid', $args = array()) {
264 list($join, $where, $distinct) = _db_rewrite_sql($query, $primary_table, $primary_field, $args);
267 $query = db_distinct_field($primary_table, $primary_field, $query);
270 if (!empty($where) || !empty($join)) {
271 if (!empty($where)) {
272 $new = "WHERE $where ";
274 $new = " $join $new";
275 if (strpos($query, 'WHERE')) {
276 $query = str_replace('WHERE', $new .'AND (', $query);
282 if (strpos($query, 'GROUP')) {
285 elseif (strpos($query, 'HAVING')) {
288 elseif (strpos($query, 'ORDER')) {
291 elseif (strpos($query, 'LIMIT')) {
297 if (isset($replace)) {
298 $query = str_replace($replace, $insert . $replace, $query);
306 * Restrict a dynamic tablename to safe characters.
308 * Only keeps alphanumeric and underscores.
310 function db_escape_table($string) {
311 return preg_replace('/[^A-Za-z0-9_]+/', '', $string);
315 * @} End of "defgroup database".