/[drupal]/contributions/sandbox/crell/pdo/database.inc
ViewVC logotype

Contents of /contributions/sandbox/crell/pdo/database.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.29 - (show annotations) (download) (as text)
Fri Feb 15 05:46:13 2008 UTC (21 months, 1 week ago) by crell
Branch: MAIN
CVS Tags: HEAD
Changes since 1.28: +6 -14 lines
File MIME type: text/x-php
- Table creation now works.
1 <?php
2
3 error_reporting(E_ALL | E_STRICT);
4
5 define('DEBUG', TRUE);
6
7 include_once('database_query_builder.inc');
8
9 function debug($msg, $label = 'DEBUG', $stealth = FALSE) {
10 if (defined('DEBUG') && DEBUG) {
11 if (is_bool($msg)) $msg = $msg ? 'TRUE' : 'FALSE';
12 $display = $stealth ? ' style="display: none;"' : '';
13 $backtrace = debug_backtrace();
14 $debug = array();
15 $stack = (isset($backtrace[1]['class']) ? "{$backtrace[1]['class']}::" : '')
16 . (isset($backtrace[1]['function']) ? "{$backtrace[1]['function']}" : '');
17 if ($stack) $debug[] = $stack;
18 $debug[] = "Line {$backtrace[0]['line']} of {$backtrace[0]['file']}";
19 $debug = implode('<br />', $debug);
20 print "<pre{$display}>{$label}: {$debug}:<br />" . print_r($msg, 1) . "</pre><br />\n";
21 }
22 }
23
24 //debug(False, 'Something here', TRUE);
25
26 /**
27 * Base Database API class.
28 *
29 * This class provides a Drupal-specific extension of the PDO database abstraction class in PHP.
30 * All database access calls should be issued statically against this class. It acts as a Factory,
31 * returning the appropriate child class for a specific database implmentation based on the
32 * values of the global $db_url array.
33 *
34 */
35 abstract class DatabaseConnection extends PDO {
36
37 function __construct($dsn, $username, $password, $driver_options = array()) {
38 $driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; // Because the other methods don't seem to work right.
39 parent::__construct($dsn, $username, $password, $driver_options);
40 $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DatabaseStatement' , array($this)));
41 }
42
43 /**
44 * Return the default query options for any given query.
45 *
46 * @return
47 * An array of default query options.
48 */
49 public function defaultOptions() {
50 return array(
51 'target' => 'default',
52 'fetch' => PDO::FETCH_OBJ,
53 'return_affected' => FALSE,
54 );
55 }
56
57 public function transactions() {
58 return $this->transactionSupport;
59 }
60
61 /**
62 * Append a database prefix to all tables in a query.
63 *
64 * Queries sent to Drupal should wrap all table names in curly brackets. This
65 * function searches for this syntax and adds Drupal's table prefix to all
66 * tables, allowing Drupal to coexist with other systems in the same database if
67 * necessary.
68 *
69 * @param $sql
70 * A string containing a partial or entire SQL query.
71 * @return
72 * The properly-prefixed string.
73 */
74 protected function prefixTables($sql) {
75 global $db_prefix;
76
77 if (is_array($db_prefix)) {
78 if (array_key_exists('default', $db_prefix)) {
79 $tmp = $db_prefix;
80 unset($tmp['default']);
81 foreach ($tmp as $key => $val) {
82 $sql = strtr($sql, array('{' . $key . '}' => $val . $key));
83 }
84 return strtr($sql, array('{' => $db_prefix['default'] , '}' => ''));
85 } else {
86 foreach ($db_prefix as $key => $val) {
87 $sql = strtr($sql, array('{' . $key . '}' => $val . $key));
88 }
89 return strtr($sql, array('{' => '' , '}' => ''));
90 }
91 } else {
92 return strtr($sql, array('{' => $db_prefix , '}' => ''));
93 }
94 }
95
96 protected function runQuery($query, Array $args, $options) {
97
98 static $statements = array();
99
100 $query = self::prefixTables($query);
101
102 // Cache each prepared statement, keyed by the query itself. This way,
103 // we get the benefit of prepared statement caching without any extra
104 // work by the module author.
105 if (! isset($statements[$query])) {
106 $statements[$query] = $this->prepare($query);
107 }
108
109 $options += $this->defaultOptions();
110 $statements[$query]->execute($args, $options);
111
112 if ($options['return_affected']) {
113 return $statements[$query]->rowCount();
114 } else {
115 return $statements[$query];
116 }
117 }
118
119 /**
120 * Execute a raw SELECT query on this database object.
121 *
122 * @param $query
123 * A string containing an SQL query.
124 * @param $args
125 * An array of values to substitute into the query at placeholder markers.
126 * @param $options
127 * An array of options on the query. This array is assumed to have all
128 * values populated.
129 * @return
130 * A database query result resource, or FALSE if the query was not executed
131 * correctly.
132 */
133 public function query($query, Array $args, Array $options = array()) {
134
135 // Backward compatibility hack, temporary.
136 $query = str_replace(array('%d' , '%f' , '%b' , "'%s'"), '?', $query);
137
138 return $this->runQuery($query, $args, $options);
139 }
140
141 public function select($query_id, Array $options = array()) {
142 $class_type = 'SelectQuery_'. $this->driver();
143 return new $class_type($query_id, $this, $options);
144 }
145
146 public function insert($query_id, $table, Array $options = array()) {
147 $class_type = 'InsertQuery_'. $this->driver();
148 return new $class_type($query_id, $this, $table, $options);
149 }
150
151 public function update($query_id, $table, Array $options = array()) {
152 $class_type = 'UpdateQuery_'. $this->driver();
153 return new $class_type($query_id, $this, $table, $options);
154 }
155
156 public function delete($query_id, $table, Array $options = array()) {
157 $class_type = 'DeleteQuery_'. $this->driver();
158 return new $class_type($query_id, $this, $table, $options);
159 }
160
161 public function schema() {
162 static $schema;
163 if (empty($schema)) {
164 require_once('schema.inc');
165 require_once('schema.'. $this->driver() .'.inc');
166 $class_type = 'DatabaseSchema_'. $this->driver();
167 $schema = new $class_type($this);
168 }
169 return $schema;
170 }
171
172 public function startTransaction() {
173 $class_type = 'DatabaseTransaction_'. $this->driver();
174 return new $class_type($this);
175 }
176
177 public function replace($query_id, $table, Array $fields, Array $where) {
178 /*
179 // The following trivial method should be replaced with a database-specific
180 // mechanism if one exists.
181 $num_deleted = $this->delete($table, $where);
182 $fields += $where;
183 $num_inserted = $this->insert($table, $fields);
184 return $num_deleted + $num_inserted;
185 */
186 }
187
188 public function escapeTable($table) {
189 return preg_replace('/[^A-Za-z0-9_]+/', '', $string);
190 }
191
192 /**
193 * Runs a limited-range query on this database object.
194 *
195 * Use this as a substitute for ->query() when a subset of the query is to be
196 * returned.
197 * User-supplied arguments to the query should be passed in as separate parameters
198 * so that they can be properly escaped to avoid SQL injection attacks.
199 *
200 * @param $query
201 * A string containing an SQL query.
202 * @param $args
203 * An array of values to substitute into the query at placeholder markers.
204 * @param $from
205 * The first result row to return.
206 * @param $count
207 * The maximum number of result rows to return.
208 * @param $options
209 * An array of options on the query. This array is assumed to have all
210 * values populated.
211 * @return
212 * A database query result resource, or FALSE if the query was not executed
213 * correctly.
214 */
215 abstract public function queryRange($query, Array $args, $from, $count, Array $options);
216
217 /**
218 * Runs a SELECT query and stores its results in a temporary table.
219 *
220 * Use this as a substitute for db_query() when the results need to stored
221 * in a temporary table. Temporary tables exist for the duration of the page
222 * request.
223 * User-supplied arguments to the query should be passed in as separate parameters
224 * so that they can be properly escaped to avoid SQL injection attacks.
225 *
226 * Note that if you need to know how many results were returned, you should do
227 * a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does
228 * not give consistent result across different database types in this case.
229 *
230 * @param $query
231 * A string containing a normal SELECT SQL query.
232 * @param $args
233 * An array of values to substitute into the query at placeholder markers.
234 * @param $tablename
235 * The name of the temporary table to select into. This name will not be
236 * prefixed as there is no risk of collision.
237 * @return
238 * A database query result resource, or FALSE if the query was not executed
239 * correctly.
240 */
241 abstract function queryTemporary($query, Array $args, $tablename);
242
243 /**
244 * Returns the type of database driver.
245 *
246 * This is not necessarily the same as the type of the database itself.
247 * For instance, there could be two MySQL drivers, mysql and mysql_mock.
248 * This function would return different values for each, but both would
249 * return "mysql" for databaseType().
250 */
251 abstract public function driver();
252
253 /**
254 * Determine if this driver supports transactions.
255 */
256 abstract public function supportsTransactions();
257
258 /**
259 * Returns the type of the database being accessed.
260 */
261 abstract public function databaseType();
262
263 }
264
265 abstract class Database {
266
267 static protected $connections = array();
268
269 static protected $databaseInfo = NULL;
270
271 static protected $activeKey = 'default';
272
273
274 final public static function getActiveConnection($target = 'default') {
275 return self::getConnection(self::$activeKey, $target);
276 }
277
278 final public static function setActiveConnection($key = 'default') {
279 $old_key = self::$activeKey;
280 self::$activeKey = $key;
281 return $old_key;
282 }
283
284 /**
285 * Parse out the database URLs specified in the config file
286 * and specify defaults where necessary.
287 */
288 final protected static function parseUrls() {
289 global $db_url;
290 self::$dbUrls = $db_url;
291 if (! is_array(self::$dbUrls)) {
292 self::$dbUrls = array('default' => self::$dbUrls);
293 }
294 foreach (self::$dbUrls as $index => $url) {
295 if (! is_array(self::$dbUrls[$index])) {
296 self::$dbUrls[$index] = array('default' => $url);
297 }
298 foreach (self::$dbUrls[$index] as $target => $value) {
299 // Each target can also be an array. If it is, we select one entry at random to be
300 // target for this request. That allows us to have multiple slave servers.
301 if (is_array($value)) {
302 self::$dbUrls[$index][$target] = self::$dbUrls[$index][$target][mt_rand(0, count(self::$dbUrls[$index][$target]) - 1)];
303 }
304 }
305 }
306 }
307
308 final protected static function parseConnectionInfo() {
309 $databaseInfo = $GLOBALS['databases'];
310
311 // If no database key is specified, default to default.
312 if (! is_array($databaseInfo)) {
313 $databaseInfo = array('default' => $databaseInfo);
314 }
315
316 foreach ($databaseInfo as $index => $info) {
317 // If no targets are specified, default to one default.
318 if (! is_array($databaseInfo[$index])) {
319 $databaseInfo[$index] = array('default' => $info);
320 }
321
322 foreach ($databaseInfo[$index] as $target => $value) {
323 // If there is no "driver" property, then we assume it's an array of possible connections for
324 // this target. Pick one at random. That allows us to have, for example, multiple slave servers.
325 if (empty($value['driver'])) {
326 $databaseInfo[$index][$target] = $databaseInfo[$index][$target][mt_rand(0, count($databaseInfo[$index][$target]) - 1)];
327 }
328 }
329 }
330
331 self::$databaseInfo = $databaseInfo;
332
333 }
334
335 /**
336 * Open a connection to the server specified by the given
337 * key and target.
338 *
339 * @param $key
340 * @param $target
341 */
342 final protected static function openConnection($key, $target) {
343
344 if (empty(self::$connectionInfo)) {
345 self::parseConnectionInfo();
346 }
347
348 // If the requested database does not exist then it is an unrecoverable error.
349 // If the requested target does not exist, however, we fall back to the default
350 // target. The target is typically either "default" or "slave", indicating to
351 // use a slave SQL server if one is available. If it's not available, then the
352 // default/master server is the correct server to use.
353 if (! isset(self::$databaseInfo[$key])) {
354 throw new Exception('DB does not exist');
355 }
356 if (! isset(self::$databaseInfo[$key][$target])) {
357 $target = 'default';
358 }
359
360 $driver = self::$databaseInfo[$key][$target]['driver'];
361 $driver_class = 'DatabaseConnection_'. $driver;
362 $driver_file ='database.'. $driver .'.inc';
363
364 try {
365 require_once($driver_file);
366 self::$connections[$key][$target] = new $driver_class(self::$databaseInfo[$key][$target]);
367 } catch (PDOException $e) {
368 debug($e->getMessage());
369 //watchdog('database', $e->getMessage());
370 // print some sort of error here.
371 }
372 }
373
374 final public static function getConnection($key = 'default', $target = 'default') {
375
376 if (! isset(self::$connections[$key][$target])) {
377 self::openConnection($key, $target);
378 }
379
380 return self::$connections[$key][$target];
381 }
382 }
383
384 class DatabaseTransaction {
385
386 protected $connection;
387 protected $supportsTransactions;
388 protected $hasRolledBack = FALSE;
389
390 public function __construct(DatabaseConnection $connection) {
391 $this->connection = $connection;
392 $this->supportsTransactions = $connection->supportsTransactions();
393
394 if ($this->supportsTransactions) {
395 $connection->beginTransaction();
396 }
397 }
398
399 public function commit() {
400 if ($this->supportsTransactions) {
401 $this->connection->commit();
402 }
403 }
404
405 public function rollBack() {
406 if ($this->supportsTransactions) {
407 $this->connection->rollBack();
408 $this->hasRolledBack = TRUE;
409 }
410 }
411
412 public function hasRolledBack() {
413 return $this->hasRolledBack;
414 }
415
416 public function __destruct() {
417 if ($this->supportsTransactions) {
418 $this->connection->commit();
419 }
420 }
421
422 }
423
424
425 class DatabaseStatement extends PDOStatement {
426
427 public $dbh;
428
429 protected function __construct($dbh) {
430 $this->dbh = $dbh;
431 $this->setFetchMode(PDO::FETCH_OBJ);
432 }
433
434 public function execute($args, $options) {
435 if (is_string($options['fetch'])) {
436 $this->setFetchMode(PDO::FETCH_CLASS, $options['fetch']);
437 }
438 else {
439 $this->setFetchMode($options['fetch']);
440 }
441 parent::execute($args);
442 }
443
444 public function fetchCol($index = 0) {
445 return $this->fetchAll(PDO::FETCH_COLUMN, $index);
446 }
447
448 public function fetchAllAssoc($key) {
449 $return = array();
450 foreach ($this as $record) {
451 $return[$record->$key] = $record;
452 }
453 return $return;
454 }
455
456 /* This needs a new name.
457 public function fetchAssoc() {
458 $return = array();
459 $this->setFetchMode(PDO::FETCH_NUM);
460 foreach ($this as $record) {
461 $return[$record[0]] = $record[1];
462 }
463 return $return;
464 }
465 */
466
467 public function fetchOne($index = 0) {
468 return $this->fetchColumn($index);
469 }
470
471 /**
472 * Fetches the next row and returns it as an associative array.
473 *
474 * This method corresponds to PDOStatement::fetchObject(),
475 * but for associative arrays. Why objects get a method of their
476 * own and arrays do not, I do not know.
477 *
478 */
479 public function fetchAssoc() {
480 return $this->fetch(PDO::FETCH_ASSOC);
481 }
482 }
483
484 /**
485 * The following utility functions are simply convenience wrappers.
486 * They should never, ever have any database-specific code in them.
487 */
488
489 function db_query($query, Array $args = array(), Array $options = array()) {
490 if (empty($options['target'])) {
491 $options['target'] = 'default';
492 }
493 return Database::getActiveConnection($options['target'])->query($query, $args, $options);
494 }
495
496 function db_query_range($query, Array $args, $from = 0, $count = 0, Array $options = array()) {
497 if (empty($options['target'])) {
498 $options['target'] = 'default';
499 }
500 return Database::getActiveConnection($options['target'])->queryRange($query, $args, $from, $count, $options);
501 }
502
503 function db_query_temporary($query, Array $args, $tablename, Array $options = array()) {
504 if (empty($options['target'])) {
505 $options['target'] = 'default';
506 }
507 return Database::getActiveConnection($options['target'])->queryTemporary($query, $args, $tablename, $options);
508 }
509
510 function db_insert($query_id, $table, Array $options = array()) {
511 if (empty($options['target']) || $options['target'] == 'slave') {
512 $options['target'] = 'default';
513 }
514 return Database::getActiveConnection($options['target'])->insert($query_id, $table, $options);
515 }
516
517 function db_update($query_id, $table, Array $options = array()) {
518 if (empty($options['target']) || $options['target'] == 'slave') {
519 $options['target'] = 'default';
520 }
521 return Database::getActiveConnection($options['target'])->update($query_id, $table, $options);
522 }
523
524 function db_delete($query_id, $table, Array $options = array()) {
525 if (empty($options['target']) || $options['target'] == 'slave') {
526 $options['target'] = 'default';
527 }
528 return Database::getActiveConnection($options['target'])->delete($query_id, $table, $options);
529 }
530
531 function db_select($query_id, Array $options = array()) {
532 if (empty($options['target'])) {
533 $options['target'] = 'default';
534 }
535 return Database::getActiveConnection($options['target'])->select($query_id, $options);
536 }
537
538 function db_replace($query_id, $table, Array $fields = array(), $where = array()) {
539 if (empty($options['target'])) {
540 $options['target'] = 'default';
541 }
542 return Database::getActiveConnection($options['target'])->replace($table, $fields, $where);
543 }
544
545 function db_set_active($key = 'default') {
546 Database::setActiveConnection($key);
547 }
548
549
550
551 /**
552 * Restrict a dynamic table, column or constraint name to safe characters.
553 *
554 * Only keeps alphanumeric and underscores.
555 */
556 function db_escape_table($string) {
557 return Database::getActiveConnection()->escapeTable($table);
558 }
559
560 /**
561 * Perform an SQL query and return success or failure.
562 *
563 * @param $sql
564 * A string containing a complete SQL query. %-substitution
565 * parameters are not supported.
566 * @return
567 * An array containing the keys:
568 * success: a boolean indicating whether the query succeeded
569 * query: the SQL query executed, passed through check_plain()
570 */
571 function update_sql($sql) {
572 $result = Database::getActiveConnection()->query($sql, array(true));
573 return array('success' => $result !== FALSE, 'query' => check_plain($sql));
574 }
575
576 // Just for testing purposes
577 function check_plain($string) { return $string; }
578
579 /**
580 * Returns the last insert id.
581 *
582 * @todo Remove this function when all queries have been ported to db_insert().
583 * @param $table
584 * The name of the table you inserted into.
585 * @param $field
586 * The name of the autoincrement field.
587 */
588 function db_last_insert_id($table, $field) {
589 Database::getActiveConnection()->lastInsertId();
590 }
591
592 /**
593 * Determine the number of rows changed by the preceding query.
594 *
595 * This may not work, actually, without some tricky temp code.
596 *
597 * @todo Remove this function when all queries have been ported to db_update().
598 */
599 function db_affected_rows() {
600 global $active_db;
601 return mysql_affected_rows($active_db);
602 }
603
604 /**
605 * Generate placeholders for an array of query arguments of a single type.
606 *
607 * Given a Schema API field type, return correct %-placeholders to
608 * embed in a query
609 *
610 * @todo This may be possible to remove in favor of db_select().
611 * @param $arguments
612 * An array with at least one element.
613 * @param $type
614 * The Schema API type of a field (e.g. 'int', 'text', or 'varchar').
615 */
616 function db_placeholders($arguments, $type = 'int') {
617 $placeholder = db_type_placeholder($type);
618 return implode(',', array_fill(0, count($arguments), $placeholder));
619 }
620
621 /**
622 * Helper function for db_rewrite_sql.
623 *
624 * Collects JOIN and WHERE statements via hook_db_rewrite_sql()
625 * Decides whether to select primary_key or DISTINCT(primary_key)
626 *
627 * @todo Remove this function when all code has been converted to query_alter.
628 * @param $query
629 * Query to be rewritten.
630 * @param $primary_table
631 * Name or alias of the table which has the primary key field for this query.
632 * Typical table names would be: {blocks}, {comments}, {forum}, {node},
633 * {menu}, {term_data} or {vocabulary}. However, in most cases the usual
634 * table alias (b, c, f, n, m, t or v) is used instead of the table name.
635 * @param $primary_field
636 * Name of the primary field.
637 * @param $args
638 * Array of additional arguments.
639 * @return
640 * An array: join statements, where statements, field or DISTINCT(field).
641 */
642 function _db_rewrite_sql($query = '', $primary_table = 'n', $primary_field = 'nid', $args = array()) {
643 $where = array();
644 $join = array();
645 $distinct = FALSE;
646 foreach (module_implements('db_rewrite_sql') as $module) {
647 $result = module_invoke($module, 'db_rewrite_sql', $query, $primary_table, $primary_field, $args);
648 if (isset($result) && is_array($result)) {
649 if (isset($result['where'])) {
650 $where[] = $result['where'];
651 }
652 if (isset($result['join'])) {
653 $join[] = $result['join'];
654 }
655 if (isset($result['distinct']) && $result['distinct']) {
656 $distinct = TRUE;
657 }
658 }
659 elseif (isset($result)) {
660 $where[] = $result;
661 }
662 }
663
664 $where = empty($where) ? '' : '('. implode(') AND (', $where) .')';
665 $join = empty($join) ? '' : implode(' ', $join);
666
667 return array($join, $where, $distinct);
668 }
669
670 /**
671 * Rewrites node, taxonomy and comment queries. Use it for listing queries. Do not
672 * use FROM table1, table2 syntax, use JOIN instead.
673 *
674 * @todo Remove this function when all code has been converted to query_alter.
675 * @param $query
676 * Query to be rewritten.
677 * @param $primary_table
678 * Name or alias of the table which has the primary key field for this query.
679 * Typical table names would be: {blocks}, {comments}, {forum}, {node},
680 * {menu}, {term_data} or {vocabulary}. However, it is more common to use the
681 * the usual table aliases: b, c, f, n, m, t or v.
682 * @param $primary_field
683 * Name of the primary field.
684 * @param $args
685 * An array of arguments, passed to the implementations of hook_db_rewrite_sql.
686 * @return
687 * The original query with JOIN and WHERE statements inserted from
688 * hook_db_rewrite_sql implementations. nid is rewritten if needed.
689 */
690 function db_rewrite_sql($query, $primary_table = 'n', $primary_field = 'nid', $args = array()) {
691 list($join, $where, $distinct) = _db_rewrite_sql($query, $primary_table, $primary_field, $args);
692
693 if ($distinct) {
694 $query = db_distinct_field($primary_table, $primary_field, $query);
695 }
696
697 if (!empty($where) || !empty($join)) {
698 $pattern = '{
699 # Beginning of the string
700 ^
701 ((?P<anonymous_view>
702 # Everything within this set of parentheses is named "anonymous view"
703 (?:
704 [^()]++ # anything not parentheses
705 |
706 \( (?P>anonymous_view) \) # an open parenthesis, more "anonymous view" and finally a close parenthesis.
707 )*
708 )[^()]+WHERE)
709 }x';
710 preg_match($pattern, $query, $matches);
711 if ($where) {
712 $n = strlen($matches[1]);
713 $second_part = substr($query, $n);
714 $first_part = substr($matches[1], 0, $n - 5) ." $join WHERE $where AND ( ";
715 // PHP 4 does not support strrpos for strings. We emulate it.
716 $haystack_reverse = strrev($second_part);
717 // No need to use strrev on the needle, we supply GROUP, ORDER, LIMIT
718 // reversed.
719 foreach (array('PUORG', 'REDRO', 'TIMIL') as $needle_reverse) {
720 $pos = strpos($haystack_reverse, $needle_reverse);
721 if ($pos !== FALSE) {
722 // All needles are five characters long.
723 $pos += 5;
724 break;
725 }
726 }
727 if ($pos === FALSE) {
728 $query = $first_part . $second_part .')';
729 }
730 else {
731 $query = $first_part . substr($second_part, 0, -$pos) .')'. substr($second_part, -$pos);
732 }
733 }
734 else {
735 $query = $matches[1] ." $join ". substr($query, strlen($matches[1]));
736 }
737 }
738
739 return $query;
740 }
741
742
743
744
745 /**
746 * @ingroup schemaapi
747 * @{
748 */
749
750
751 /**
752 * Create a new table from a Drupal table definition.
753 *
754 * @param $ret
755 * Array to which query results will be added.
756 * @param $name
757 * The name of the table to create.
758 * @param $table
759 * A Schema API table definition array.
760 */
761 function db_create_table(&$ret, $name, $table) {
762 return Database::getActiveConnection()->schema()->createTable($ret, $name, $table);
763 }
764
765 /**
766 * Return an array of field names from an array of key/index column specifiers.
767 *
768 * This is usually an identity function but if a key/index uses a column prefix
769 * specification, this function extracts just the name.
770 *
771 * @param $fields
772 * An array of key/index column specifiers.
773 * @return
774 * An array of field names.
775 */
776 function db_field_names($fields) {
777 return Database::getActiveConnection()->schema()->fieldNames($fields);
778 }
779
780 /**
781 * Check if a table exists.
782 */
783 function db_table_exists($table) {
784 return Database::getActiveConnection()->schema()->tableExists($table);
785 }
786
787 /**
788 * Check if a column exists in the given table.
789 */
790 function db_column_exists($table, $column) {
791 return Database::getActiveConnection()->schema()->columnExists($table, $column);
792 }
793
794
795 /**
796 * Given a Schema API field type, return the correct %-placeholder.
797 *
798 * Embed the placeholder in a query to be passed to db_query and and pass as an
799 * argument to db_query a value of the specified type.
800 *
801 * @todo Remove this after all queries are converted to type-agnostic form.
802 * @param $type
803 * The Schema API type of a field.
804 * @return
805 * The placeholder string to embed in a query for that type.
806 */
807 function db_type_placeholder($type) {
808 switch ($type) {
809 case 'varchar':
810 case 'char':
811 case 'text':
812 case 'datetime':
813 return '\'%s\'';
814
815 case 'numeric':
816 // For 'numeric' values, we use '%s', not '\'%s\'' as with
817 // string types, because numeric values should not be enclosed
818 // in quotes in queries (though they can be, at least on mysql
819 // and pgsql). Numerics should only have [0-9.+-] and
820 // presumably no db's "escape string" function will mess with
821 // those characters.
822 return '%s';
823
824 case 'serial':
825 case 'int':
826 return '%d';
827
828 case 'float':
829 return '%f';
830
831 case 'blob':
832 return '%b';
833 }
834
835 // There is no safe value to return here, so return something that
836 // will cause the query to fail.
837 return 'unsupported type '. $type .'for db_type_placeholder';
838 }
839
840
841 function _db_create_keys_sql($spec) {
842 return Database::getActiveConnection()->schema()->createKeysSql($spec);
843 }
844
845 /**
846 * This maps a generic data type in combination with its data size
847 * to the engine-specific data type.
848 */
849 function db_type_map() {
850 return Database::getActiveConnection()->schema()->getFieldTypeMap();
851 }
852
853 /**
854 * Rename a table.
855 *
856 * @param $ret
857 * Array to which query results will be added.
858 * @param $table
859 * The table to be renamed.
860 * @param $new_name
861 * The new name for the table.
862 */
863 function db_rename_table(&$ret, $table, $new_name) {
864 return Database::getActiveConnection()->schema()->renameTable($ret, $table, $new_name);
865 }
866
867 /**
868 * Drop a table.
869 *
870 * @param $ret
871 * Array to which query results will be added.
872 * @param $table
873 * The table to be dropped.
874 */
875 function db_drop_table(&$ret, $table) {
876 return Database::getActiveConnection()->schema()->dropTable($ret, $table);
877 }
878
879 /**
880 * Add a new field to a table.
881 *
882 * @param $ret
883 * Array to which query results will be added.
884 * @param $table
885 * Name of the table to be altered.
886 * @param $field
887 * Name of the field to be added.
888 * @param $spec
889 * The field specification array, as taken from a schema definition.
890 * The specification may also contain the key 'initial', the newly
891 * created field will be set to the value of the key in all rows.
892 * This is most useful for creating NOT NULL columns with no default
893 * value in existing tables.
894 * @param $keys_new
895 * Optional keys and indexes specification to be created on the
896 * table along with adding the field. The format is the same as a
897 * table specification but without the 'fields' element. If you are
898 * adding a type 'serial' field, you MUST specify at least one key
899 * or index including it in this array. @see db_change_field for more
900 * explanation why.
901 */
902 function db_add_field(&$ret, $table, $field, $spec, $keys_new = array()) {
903 return Database::getActiveConnection()->schema()->addField($ret, $table, $field, $spec, $keys_new);
904 }
905
906 /**
907 * Drop a field.
908 *
909 * @param $ret
910 * Array to which query results will be added.
911 * @param $table
912 * The table to be altered.
913 * @param $field
914 * The field to be dropped.
915 */
916 function db_drop_field(&$ret, $table, $field) {
917 return Database::getActiveConnection()->schema()->dropField($ret, $table, $field);
918 }
919
920 /**
921 * Set the default value for a field.
922 *
923 * @param $ret
924 * Array to which query results will be added.
925 * @param $table
926 * The table to be altered.
927 * @param $field
928 * The field to be altered.
929 * @param $default
930 * Default value to be set. NULL for 'default NULL'.
931 */
932 function db_field_set_default(&$ret, $table, $field, $default) {
933 return Database::getActiveConnection()->schema()->dropField($ret, $table, $field, $default);
934 }
935
936 /**
937 * Set a field to have no default value.
938 *
939 * @param $ret
940 * Array to which query results will be added.
941 * @param $table
942 * The table to be altered.
943 * @param $field
944 * The field to be altered.
945 */
946 function db_field_set_no_default(&$ret, $table, $field) {
947 return Database::getActiveConnection()->schema()->fieldSetNoDefault($ret, $table, $field);
948 }
949
950 /**
951 * Add a primary key.
952 *
953 * @param $ret
954 * Array to which query results will be added.
955 * @param $table
956 * The table to be altered.
957 * @param $fields
958 * Fields for the primary key.
959 */
960 function db_add_primary_key(&$ret, $table, $fields) {
961 return Database::getActiveConnection()->schema()->addPrimaryKey($ret, $table, $field);
962 }
963
964 /**
965 * Drop the primary key.
966 *
967 * @param $ret
968 * Array to which query results will be added.
969 * @param $table
970 * The table to be altered.
971 */
972 function db_drop_primary_key(&$ret, $table) {
973 return Database::getActiveConnection()->schema()->dropPrimaryKey($ret, $table);
974 }
975
976 /**
977 * Add a unique key.
978 *
979 * @param $ret
980 * Array to which query results will be added.
981 * @param $table
982 * The table to be altered.
983 * @param $name
984 * The name of the key.
985 * @param $fields
986 * An array of field names.
987 */
988 function db_add_unique_key(&$ret, $table, $name, $fields) {
989 return Database::getActiveConnection()->schema()->addUniqueKey($ret, $table, $name, $fields);
990 }
991
992 /**
993 * Drop a unique key.
994 *
995 * @param $ret
996 * Array to which query results will be added.
997 * @param $table
998 * The table to be altered.
999 * @param $name
1000 * The name of the key.
1001 */
1002 function db_drop_unique_key(&$ret, $table, $name) {
1003 return Database::getActiveConnection()->schema()->dropUniqueKey($ret, $table, $name);
1004 }
1005
1006 /**
1007 * Add an index.
1008 *
1009 * @param $ret
1010 * Array to which query results will be added.
1011 * @param $table
1012 * The table to be altered.
1013 * @param $name
1014 * The name of the index.
1015 * @param $fields
1016 * An array of field names.
1017 */
1018 function db_add_index(&$ret, $table, $name, $fields) {
1019 return Database::getActiveConnection()->schema()->addIndex($ret, $table, $name, $fields);
1020 }
1021
1022 /**
1023 * Drop an index.
1024 *
1025 * @param $ret
1026 * Array to which query results will be added.
1027 * @param $table
1028 * The table to be altered.
1029 * @param $name
1030 * The name of the index.
1031 */
1032 function db_drop_index(&$ret, $table, $name) {
1033 return Database::getActiveConnection()->schema()->addIndex($ret, $table, $name);
1034 }
1035
1036 /**
1037 * Change a field definition.
1038 *
1039 * IMPORTANT NOTE: To maintain database portability, you have to explicitly
1040 * recreate all indices and primary keys that are using the changed field.
1041 *
1042 * That means that you have to drop all affected keys and indexes with
1043 * db_drop_{primary_key,unique_key,index}() before calling db_change_field().
1044 * To recreate the keys and indices, pass the key definitions as the
1045 * optional $keys_new argument directly to db_change_field().
1046 *
1047 * For example, suppose you have:
1048 * @code
1049 * $schema['foo'] = array(
1050 * 'fields' => array(
1051 * 'bar' => array('type' => 'int', 'not null' => TRUE)
1052 * ),
1053 * 'primary key' => array('bar')
1054 * );
1055 * @endcode
1056 * and you want to change foo.bar to be type serial, leaving it as the
1057 * primary key. The correct sequence is:
1058 * @code
1059 * db_drop_primary_key($ret, 'foo');
1060 * db_change_field($ret, 'foo', 'bar', 'bar',
1061 * array('type' => 'serial', 'not null' => TRUE),
1062 * array('primary key' => array('bar')));
1063 * @endcode
1064 *
1065 * The reasons for this are due to the different database engines:
1066 *
1067 * On PostgreSQL, changing a field definition involves adding a new field
1068 * and dropping an old one which* causes any indices, primary keys and
1069 * sequences (from serial-type fields) that use the changed field to be dropped.
1070 *
1071 * On MySQL, all type 'serial' fields must be part of at least one key
1072 * or index as soon as they are created. You cannot use
1073 * db_add_{primary_key,unique_key,index}() for this purpose because
1074 * the ALTER TABLE command will fail to add the column without a key
1075 * or index specification. The solution is to use the optional
1076 * $keys_new argument to create the key or index at the same time as
1077 * field.
1078 *
1079 * You could use db_add_{primary_key,unique_key,index}() in all cases
1080 * unless you are converting a field to be type serial. You can use
1081 * the $keys_new argument in all cases.
1082 *
1083 * @param $ret
1084 * Array to which query results will be added.
1085 * @param $table
1086 * Name of the table.
1087 * @param $field
1088 * Name of the field to change.
1089 * @param $field_new
1090 * New name for the field (set to the same as $field if you don't want to change the name).
1091 * @param $spec
1092 * The field specification for the new field.
1093 * @param $keys_new
1094 * Optional keys and indexes specification to be created on the
1095 * table along with changing the field. The format is the same as a
1096 * table specification but without the 'fields' element.
1097 */
1098
1099 function db_change_field(&$ret, $table, $field, $field_new, $spec, $keys_new = array()) {
1100 return Database::getActiveConnection()->schema()->changeField($ret, $table, $field, $field_new, $spec, $keys_new);
1101 }
1102
1103 /**
1104 * @} End of "ingroup schemaapi".
1105 */
1106
1107 // For temporary backward compatibility only.
1108 function db_fetch_object(DatabaseStatement $statement) {
1109 return $statement->fetch(PDO::FETCH_OBJ);
1110 }
1111
1112 function db_fetch_array(DatabaseStatement $statement) {
1113 return $statement->fetch(PDO::FETCH_BOTH);
1114 }
1115
1116

  ViewVC Help
Powered by ViewVC 1.1.2