Mercurial > defr > drupal > core
comparison includes/database.pgsql.inc @ 1:c1f4ac30525a 6.0
Drupal 6.0
| author | Franck Deroche <webmaster@defr.org> |
|---|---|
| date | Tue, 23 Dec 2008 14:28:28 +0100 |
| parents | |
| children | fff6d4c8c043 |
comparison
equal
deleted
inserted
replaced
| 0:5a113a1c4740 | 1:c1f4ac30525a |
|---|---|
| 1 <?php | |
| 2 // $Id: database.pgsql.inc,v 1.68.2.1 2008/02/07 10:17:26 goba Exp $ | |
| 3 | |
| 4 /** | |
| 5 * @file | |
| 6 * Database interface code for PostgreSQL database servers. | |
| 7 */ | |
| 8 | |
| 9 /** | |
| 10 * @ingroup database | |
| 11 * @{ | |
| 12 */ | |
| 13 | |
| 14 /** | |
| 15 * Report database status. | |
| 16 */ | |
| 17 function db_status_report() { | |
| 18 $t = get_t(); | |
| 19 | |
| 20 $version = db_version(); | |
| 21 | |
| 22 $form['pgsql'] = array( | |
| 23 'title' => $t('PostgreSQL database'), | |
| 24 'value' => $version, | |
| 25 ); | |
| 26 | |
| 27 if (version_compare($version, DRUPAL_MINIMUM_PGSQL) < 0) { | |
| 28 $form['pgsql']['severity'] = REQUIREMENT_ERROR; | |
| 29 $form['pgsql']['description'] = $t('Your PostgreSQL Server is too old. Drupal requires at least PostgreSQL %version.', array('%version' => DRUPAL_MINIMUM_PGSQL)); | |
| 30 } | |
| 31 | |
| 32 return $form; | |
| 33 } | |
| 34 | |
| 35 /** | |
| 36 * Returns the version of the database server currently in use. | |
| 37 * | |
| 38 * @return Database server version | |
| 39 */ | |
| 40 function db_version() { | |
| 41 return db_result(db_query("SHOW SERVER_VERSION")); | |
| 42 } | |
| 43 | |
| 44 /** | |
| 45 * Initialize a database connection. | |
| 46 */ | |
| 47 function db_connect($url) { | |
| 48 // Check if PostgreSQL support is present in PHP | |
| 49 if (!function_exists('pg_connect')) { | |
| 50 _db_error_page('Unable to use the PostgreSQL database because the PostgreSQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.'); | |
| 51 } | |
| 52 | |
| 53 $url = parse_url($url); | |
| 54 $conn_string = ''; | |
| 55 | |
| 56 // Decode url-encoded information in the db connection string | |
| 57 if (isset($url['user'])) { | |
| 58 $conn_string .= ' user='. urldecode($url['user']); | |
| 59 } | |
| 60 if (isset($url['pass'])) { | |
| 61 $conn_string .= ' password='. urldecode($url['pass']); | |
| 62 } | |
| 63 if (isset($url['host'])) { | |
| 64 $conn_string .= ' host='. urldecode($url['host']); | |
| 65 } | |
| 66 if (isset($url['path'])) { | |
| 67 $conn_string .= ' dbname='. substr(urldecode($url['path']), 1); | |
| 68 } | |
| 69 if (isset($url['port'])) { | |
| 70 $conn_string .= ' port='. urldecode($url['port']); | |
| 71 } | |
| 72 | |
| 73 // pg_last_error() does not return a useful error message for database | |
| 74 // connection errors. We must turn on error tracking to get at a good error | |
| 75 // message, which will be stored in $php_errormsg. | |
| 76 $track_errors_previous = ini_get('track_errors'); | |
| 77 ini_set('track_errors', 1); | |
| 78 | |
| 79 $connection = @pg_connect($conn_string); | |
| 80 if (!$connection) { | |
| 81 require_once './includes/unicode.inc'; | |
| 82 _db_error_page(decode_entities($php_errormsg)); | |
| 83 } | |
| 84 | |
| 85 // Restore error tracking setting | |
| 86 ini_set('track_errors', $track_errors_previous); | |
| 87 | |
| 88 return $connection; | |
| 89 } | |
| 90 | |
| 91 /** | |
| 92 * Runs a basic query in the active database. | |
| 93 * | |
| 94 * User-supplied arguments to the query should be passed in as separate | |
| 95 * parameters so that they can be properly escaped to avoid SQL injection | |
| 96 * attacks. | |
| 97 * | |
| 98 * @param $query | |
| 99 * A string containing an SQL query. | |
| 100 * @param ... | |
| 101 * A variable number of arguments which are substituted into the query | |
| 102 * using printf() syntax. Instead of a variable number of query arguments, | |
| 103 * you may also pass a single array containing the query arguments. | |
| 104 * | |
| 105 * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose | |
| 106 * in '') and %%. | |
| 107 * | |
| 108 * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, | |
| 109 * and TRUE values to decimal 1. | |
| 110 * | |
| 111 * @return | |
| 112 * A database query result resource, or FALSE if the query was not | |
| 113 * executed correctly. | |
| 114 */ | |
| 115 function db_query($query) { | |
| 116 $args = func_get_args(); | |
| 117 array_shift($args); | |
| 118 $query = db_prefix_tables($query); | |
| 119 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax | |
| 120 $args = $args[0]; | |
| 121 } | |
| 122 _db_query_callback($args, TRUE); | |
| 123 $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); | |
| 124 return _db_query($query); | |
| 125 } | |
| 126 | |
| 127 /** | |
| 128 * Helper function for db_query(). | |
| 129 */ | |
| 130 function _db_query($query, $debug = 0) { | |
| 131 global $active_db, $last_result, $queries; | |
| 132 | |
| 133 if (variable_get('dev_query', 0)) { | |
| 134 list($usec, $sec) = explode(' ', microtime()); | |
| 135 $timer = (float)$usec + (float)$sec; | |
| 136 } | |
| 137 | |
| 138 $last_result = pg_query($active_db, $query); | |
| 139 | |
| 140 if (variable_get('dev_query', 0)) { | |
| 141 $bt = debug_backtrace(); | |
| 142 $query = $bt[2]['function'] ."\n". $query; | |
| 143 list($usec, $sec) = explode(' ', microtime()); | |
| 144 $stop = (float)$usec + (float)$sec; | |
| 145 $diff = $stop - $timer; | |
| 146 $queries[] = array($query, $diff); | |
| 147 } | |
| 148 | |
| 149 if ($debug) { | |
| 150 print '<p>query: '. $query .'<br />error:'. pg_last_error($active_db) .'</p>'; | |
| 151 } | |
| 152 | |
| 153 if ($last_result !== FALSE) { | |
| 154 return $last_result; | |
| 155 } | |
| 156 else { | |
| 157 // Indicate to drupal_error_handler that this is a database error. | |
| 158 ${DB_ERROR} = TRUE; | |
| 159 trigger_error(check_plain(pg_last_error($active_db) ."\nquery: ". $query), E_USER_WARNING); | |
| 160 return FALSE; | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 /** | |
| 165 * Fetch one result row from the previous query as an object. | |
| 166 * | |
| 167 * @param $result | |
| 168 * A database query result resource, as returned from db_query(). | |
| 169 * @return | |
| 170 * An object representing the next row of the result, or FALSE. The attributes | |
| 171 * of this object are the table fields selected by the query. | |
| 172 */ | |
| 173 function db_fetch_object($result) { | |
| 174 if ($result) { | |
| 175 return pg_fetch_object($result); | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 /** | |
| 180 * Fetch one result row from the previous query as an array. | |
| 181 * | |
| 182 * @param $result | |
| 183 * A database query result resource, as returned from db_query(). | |
| 184 * @return | |
| 185 * An associative array representing the next row of the result, or FALSE. | |
| 186 * The keys of this object are the names of the table fields selected by the | |
| 187 * query, and the values are the field values for this result row. | |
| 188 */ | |
| 189 function db_fetch_array($result) { | |
| 190 if ($result) { | |
| 191 return pg_fetch_assoc($result); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 /** | |
| 196 * Return an individual result field from the previous query. | |
| 197 * | |
| 198 * Only use this function if exactly one field is being selected; otherwise, | |
| 199 * use db_fetch_object() or db_fetch_array(). | |
| 200 * | |
| 201 * @param $result | |
| 202 * A database query result resource, as returned from db_query(). | |
| 203 * @return | |
| 204 * The resulting field or FALSE. | |
| 205 */ | |
| 206 function db_result($result) { | |
| 207 if ($result && pg_num_rows($result) > 0) { | |
| 208 $array = pg_fetch_row($result); | |
| 209 return $array[0]; | |
| 210 } | |
| 211 return FALSE; | |
| 212 } | |
| 213 | |
| 214 /** | |
| 215 * Determine whether the previous query caused an error. | |
| 216 */ | |
| 217 function db_error() { | |
| 218 global $active_db; | |
| 219 return pg_last_error($active_db); | |
| 220 } | |
| 221 | |
| 222 /** | |
| 223 * Returns the last insert id. This function is thread safe. | |
| 224 * | |
| 225 * @param $table | |
| 226 * The name of the table you inserted into. | |
| 227 * @param $field | |
| 228 * The name of the autoincrement field. | |
| 229 */ | |
| 230 function db_last_insert_id($table, $field) { | |
| 231 return db_result(db_query("SELECT CURRVAL('{". db_escape_table($table) ."}_". db_escape_table($field) ."_seq')")); | |
| 232 } | |
| 233 | |
| 234 /** | |
| 235 * Determine the number of rows changed by the preceding query. | |
| 236 */ | |
| 237 function db_affected_rows() { | |
| 238 global $last_result; | |
| 239 return empty($last_result) ? 0 : pg_affected_rows($last_result); | |
| 240 } | |
| 241 | |
| 242 /** | |
| 243 * Runs a limited-range query in the active database. | |
| 244 * | |
| 245 * Use this as a substitute for db_query() when a subset of the query | |
| 246 * is to be returned. | |
| 247 * User-supplied arguments to the query should be passed in as separate | |
| 248 * parameters so that they can be properly escaped to avoid SQL injection | |
| 249 * attacks. | |
| 250 * | |
| 251 * @param $query | |
| 252 * A string containing an SQL query. | |
| 253 * @param ... | |
| 254 * A variable number of arguments which are substituted into the query | |
| 255 * using printf() syntax. Instead of a variable number of query arguments, | |
| 256 * you may also pass a single array containing the query arguments. | |
| 257 * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose | |
| 258 * in '') and %%. | |
| 259 * | |
| 260 * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, | |
| 261 * and TRUE values to decimal 1. | |
| 262 * | |
| 263 * @param $from | |
| 264 * The first result row to return. | |
| 265 * @param $count | |
| 266 * The maximum number of result rows to return. | |
| 267 * @return | |
| 268 * A database query result resource, or FALSE if the query was not executed | |
| 269 * correctly. | |
| 270 */ | |
| 271 function db_query_range($query) { | |
| 272 $args = func_get_args(); | |
| 273 $count = array_pop($args); | |
| 274 $from = array_pop($args); | |
| 275 array_shift($args); | |
| 276 | |
| 277 $query = db_prefix_tables($query); | |
| 278 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax | |
| 279 $args = $args[0]; | |
| 280 } | |
| 281 _db_query_callback($args, TRUE); | |
| 282 $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); | |
| 283 $query .= ' LIMIT '. (int)$count .' OFFSET '. (int)$from; | |
| 284 return _db_query($query); | |
| 285 } | |
| 286 | |
| 287 /** | |
| 288 * Runs a SELECT query and stores its results in a temporary table. | |
| 289 * | |
| 290 * Use this as a substitute for db_query() when the results need to stored | |
| 291 * in a temporary table. Temporary tables exist for the duration of the page | |
| 292 * request. | |
| 293 * User-supplied arguments to the query should be passed in as separate parameters | |
| 294 * so that they can be properly escaped to avoid SQL injection attacks. | |
| 295 * | |
| 296 * Note that if you need to know how many results were returned, you should do | |
| 297 * a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does | |
| 298 * not give consistent result across different database types in this case. | |
| 299 * | |
| 300 * @param $query | |
| 301 * A string containing a normal SELECT SQL query. | |
| 302 * @param ... | |
| 303 * A variable number of arguments which are substituted into the query | |
| 304 * using printf() syntax. The query arguments can be enclosed in one | |
| 305 * array instead. | |
| 306 * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose | |
| 307 * in '') and %%. | |
| 308 * | |
| 309 * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, | |
| 310 * and TRUE values to decimal 1. | |
| 311 * | |
| 312 * @param $table | |
| 313 * The name of the temporary table to select into. This name will not be | |
| 314 * prefixed as there is no risk of collision. | |
| 315 * @return | |
| 316 * A database query result resource, or FALSE if the query was not executed | |
| 317 * correctly. | |
| 318 */ | |
| 319 function db_query_temporary($query) { | |
| 320 $args = func_get_args(); | |
| 321 $tablename = array_pop($args); | |
| 322 array_shift($args); | |
| 323 | |
| 324 $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' AS SELECT', db_prefix_tables($query)); | |
| 325 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax | |
| 326 $args = $args[0]; | |
| 327 } | |
| 328 _db_query_callback($args, TRUE); | |
| 329 $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); | |
| 330 return _db_query($query); | |
| 331 } | |
| 332 | |
| 333 /** | |
| 334 * Returns a properly formatted Binary Large OBject value. | |
| 335 * In case of PostgreSQL encodes data for insert into bytea field. | |
| 336 * | |
| 337 * @param $data | |
| 338 * Data to encode. | |
| 339 * @return | |
| 340 * Encoded data. | |
| 341 */ | |
| 342 function db_encode_blob($data) { | |
| 343 return "'". pg_escape_bytea($data) ."'"; | |
| 344 } | |
| 345 | |
| 346 /** | |
| 347 * Returns text from a Binary Large OBject value. | |
| 348 * In case of PostgreSQL decodes data after select from bytea field. | |
| 349 * | |
| 350 * @param $data | |
| 351 * Data to decode. | |
| 352 * @return | |
| 353 * Decoded data. | |
| 354 */ | |
| 355 function db_decode_blob($data) { | |
| 356 return pg_unescape_bytea($data); | |
| 357 } | |
| 358 | |
| 359 /** | |
| 360 * Prepare user input for use in a database query, preventing SQL injection attacks. | |
| 361 * Note: This function requires PostgreSQL 7.2 or later. | |
| 362 */ | |
| 363 function db_escape_string($text) { | |
| 364 return pg_escape_string($text); | |
| 365 } | |
| 366 | |
| 367 /** | |
| 368 * Lock a table. | |
| 369 * This function automatically starts a transaction. | |
| 370 */ | |
| 371 function db_lock_table($table) { | |
| 372 db_query('BEGIN; LOCK TABLE {'. db_escape_table($table) .'} IN EXCLUSIVE MODE'); | |
| 373 } | |
| 374 | |
| 375 /** | |
| 376 * Unlock all locked tables. | |
| 377 * This function automatically commits a transaction. | |
| 378 */ | |
| 379 function db_unlock_tables() { | |
| 380 db_query('COMMIT'); | |
| 381 } | |
| 382 | |
| 383 /** | |
| 384 * Check if a table exists. | |
| 385 */ | |
| 386 function db_table_exists($table) { | |
| 387 return (bool) db_result(db_query("SELECT COUNT(*) FROM pg_class WHERE relname = '{". db_escape_table($table) ."}'")); | |
| 388 } | |
| 389 | |
| 390 /** | |
| 391 * Check if a column exists in the given table. | |
| 392 */ | |
| 393 function db_column_exists($table, $column) { | |
| 394 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) ."'")); | |
| 395 } | |
| 396 | |
| 397 /** | |
| 398 * Verify if the database is set up correctly. | |
| 399 */ | |
| 400 function db_check_setup() { | |
| 401 $t = get_t(); | |
| 402 | |
| 403 $encoding = db_result(db_query('SHOW server_encoding')); | |
| 404 if (!in_array(strtolower($encoding), array('unicode', 'utf8'))) { | |
| 405 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'); | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 /** | |
| 410 * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to | |
| 411 * the SELECT list entry of the given query and the resulting query is returned. | |
| 412 * This function only applies the wrapper if a DISTINCT doesn't already exist in | |
| 413 * the query. | |
| 414 * | |
| 415 * @param $table Table containing the field to set as DISTINCT | |
| 416 * @param $field Field to set as DISTINCT | |
| 417 * @param $query Query to apply the wrapper to | |
| 418 * @return SQL query with the DISTINCT wrapper surrounding the given table.field. | |
| 419 */ | |
| 420 function db_distinct_field($table, $field, $query) { | |
| 421 $field_to_select = 'DISTINCT ON ('. $table .'.'. $field .") $table.$field"; | |
| 422 // (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT). | |
| 423 $query = preg_replace('/(SELECT.*)(?:'. $table .'\.|\s)(?<!DISTINCT\()(?<!DISTINCT\('. $table .'\.)'. $field .'(.*FROM )/AUsi', '\1 '. $field_to_select .'\2', $query); | |
| 424 $query = preg_replace('/(ORDER BY )(?!'. $table .'\.'. $field .')/', '\1'."$table.$field, ", $query); | |
| 425 return $query; | |
| 426 } | |
| 427 | |
| 428 /** | |
| 429 * @} End of "ingroup database". | |
| 430 */ | |
| 431 | |
| 432 /** | |
| 433 * @ingroup schemaapi | |
| 434 * @{ | |
| 435 */ | |
| 436 | |
| 437 /** | |
| 438 * This maps a generic data type in combination with its data size | |
| 439 * to the engine-specific data type. | |
| 440 */ | |
| 441 function db_type_map() { | |
| 442 // Put :normal last so it gets preserved by array_flip. This makes | |
| 443 // it much easier for modules (such as schema.module) to map | |
| 444 // database types back into schema types. | |
| 445 $map = array( | |
| 446 'varchar:normal' => 'varchar', | |
| 447 'char:normal' => 'character', | |
| 448 | |
| 449 'text:tiny' => 'text', | |
| 450 'text:small' => 'text', | |
| 451 'text:medium' => 'text', | |
| 452 'text:big' => 'text', | |
| 453 'text:normal' => 'text', | |
| 454 | |
| 455 'int:tiny' => 'smallint', | |
| 456 'int:small' => 'smallint', | |
| 457 'int:medium' => 'int', | |
| 458 'int:big' => 'bigint', | |
| 459 'int:normal' => 'int', | |
| 460 | |
| 461 'float:tiny' => 'real', | |
| 462 'float:small' => 'real', | |
| 463 'float:medium' => 'real', | |
| 464 'float:big' => 'double precision', | |
| 465 'float:normal' => 'real', | |
| 466 | |
| 467 'numeric:normal' => 'numeric', | |
| 468 | |
| 469 'blob:big' => 'bytea', | |
| 470 'blob:normal' => 'bytea', | |
| 471 | |
| 472 'datetime:normal' => 'timestamp', | |
| 473 | |
| 474 'serial:tiny' => 'serial', | |
| 475 'serial:small' => 'serial', | |
| 476 'serial:medium' => 'serial', | |
| 477 'serial:big' => 'bigserial', | |
| 478 'serial:normal' => 'serial', | |
| 479 ); | |
| 480 return $map; | |
| 481 } | |
| 482 | |
| 483 /** | |
| 484 * Generate SQL to create a new table from a Drupal schema definition. | |
| 485 * | |
| 486 * @param $name | |
| 487 * The name of the table to create. | |
| 488 * @param $table | |
| 489 * A Schema API table definition array. | |
| 490 * @return | |
| 491 * An array of SQL statements to create the table. | |
| 492 */ | |
| 493 function db_create_table_sql($name, $table) { | |
| 494 $sql_fields = array(); | |
| 495 foreach ($table['fields'] as $field_name => $field) { | |
| 496 $sql_fields[] = _db_create_field_sql($field_name, _db_process_field($field)); | |
| 497 } | |
| 498 | |
| 499 $sql_keys = array(); | |
| 500 if (isset($table['primary key']) && is_array($table['primary key'])) { | |
| 501 $sql_keys[] = 'PRIMARY KEY ('. implode(', ', $table['primary key']) .')'; | |
| 502 } | |
| 503 if (isset($table['unique keys']) && is_array($table['unique keys'])) { | |
| 504 foreach ($table['unique keys'] as $key_name => $key) { | |
| 505 $sql_keys[] = 'CONSTRAINT {'. $name .'}_'. $key_name .'_key UNIQUE ('. implode(', ', $key) .')'; | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 $sql = "CREATE TABLE {". $name ."} (\n\t"; | |
| 510 $sql .= implode(",\n\t", $sql_fields); | |
| 511 if (count($sql_keys) > 0) { | |
| 512 $sql .= ",\n\t"; | |
| 513 } | |
| 514 $sql .= implode(",\n\t", $sql_keys); | |
| 515 $sql .= "\n)"; | |
| 516 $statements[] = $sql; | |
| 517 | |
| 518 if (isset($table['indexes']) && is_array($table['indexes'])) { | |
| 519 foreach ($table['indexes'] as $key_name => $key) { | |
| 520 $statements[] = _db_create_index_sql($name, $key_name, $key); | |
| 521 } | |
| 522 } | |
| 523 | |
| 524 return $statements; | |
| 525 } | |
| 526 | |
| 527 function _db_create_index_sql($table, $name, $fields) { | |
| 528 $query = 'CREATE INDEX {'. $table .'}_'. $name .'_idx ON {'. $table .'} ('; | |
| 529 $query .= _db_create_key_sql($fields) .')'; | |
| 530 return $query; | |
| 531 } | |
| 532 | |
| 533 function _db_create_key_sql($fields) { | |
| 534 $ret = array(); | |
| 535 foreach ($fields as $field) { | |
| 536 if (is_array($field)) { | |
| 537 $ret[] = 'substr('. $field[0] .', 1, '. $field[1] .')'; | |
| 538 } | |
| 539 else { | |
| 540 $ret[] = $field; | |
| 541 } | |
| 542 } | |
| 543 return implode(', ', $ret); | |
| 544 } | |
| 545 | |
| 546 function _db_create_keys(&$ret, $table, $new_keys) { | |
| 547 if (isset($new_keys['primary key'])) { | |
| 548 db_add_primary_key($ret, $table, $new_keys['primary key']); | |
| 549 } | |
| 550 if (isset($new_keys['unique keys'])) { | |
| 551 foreach ($new_keys['unique keys'] as $name => $fields) { | |
| 552 db_add_unique_key($ret, $table, $name, $fields); | |
| 553 } | |
| 554 } | |
| 555 if (isset($new_keys['indexes'])) { | |
| 556 foreach ($new_keys['indexes'] as $name => $fields) { | |
| 557 db_add_index($ret, $table, $name, $fields); | |
| 558 } | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 /** | |
| 563 * Set database-engine specific properties for a field. | |
| 564 * | |
| 565 * @param $field | |
| 566 * A field description array, as specified in the schema documentation. | |
| 567 */ | |
| 568 function _db_process_field($field) { | |
| 569 if (!isset($field['size'])) { | |
| 570 $field['size'] = 'normal'; | |
| 571 } | |
| 572 // Set the correct database-engine specific datatype. | |
| 573 if (!isset($field['pgsql_type'])) { | |
| 574 $map = db_type_map(); | |
| 575 $field['pgsql_type'] = $map[$field['type'] .':'. $field['size']]; | |
| 576 } | |
| 577 if ($field['type'] == 'serial') { | |
| 578 unset($field['not null']); | |
| 579 } | |
| 580 return $field; | |
| 581 } | |
| 582 | |
| 583 /** | |
| 584 * Create an SQL string for a field to be used in table creation or alteration. | |
| 585 * | |
| 586 * Before passing a field out of a schema definition into this function it has | |
| 587 * to be processed by _db_process_field(). | |
| 588 * | |
| 589 * @param $name | |
| 590 * Name of the field. | |
| 591 * @param $spec | |
| 592 * The field specification, as per the schema data structure format. | |
| 593 */ | |
| 594 function _db_create_field_sql($name, $spec) { | |
| 595 $sql = $name .' '. $spec['pgsql_type']; | |
| 596 | |
| 597 if ($spec['type'] == 'serial') { | |
| 598 unset($spec['not null']); | |
| 599 } | |
| 600 if (!empty($spec['unsigned'])) { | |
| 601 if ($spec['type'] == 'serial') { | |
| 602 $sql .= " CHECK ($name >= 0)"; | |
| 603 } | |
| 604 else { | |
| 605 $sql .= '_unsigned'; | |
| 606 } | |
| 607 } | |
| 608 | |
| 609 if (!empty($spec['length'])) { | |
| 610 $sql .= '('. $spec['length'] .')'; | |
| 611 } | |
| 612 elseif (isset($spec['precision']) && isset($spec['scale'])) { | |
| 613 $sql .= '('. $spec['precision'] .', '. $spec['scale'] .')'; | |
| 614 } | |
| 615 | |
| 616 if (isset($spec['not null']) && $spec['not null']) { | |
| 617 $sql .= ' NOT NULL'; | |
| 618 } | |
| 619 if (isset($spec['default'])) { | |
| 620 $default = is_string($spec['default']) ? "'". $spec['default'] ."'" : $spec['default']; | |
| 621 $sql .= " default $default"; | |
| 622 } | |
| 623 | |
| 624 return $sql; | |
| 625 } | |
| 626 | |
| 627 /** | |
| 628 * Rename a table. | |
| 629 * | |
| 630 * @param $ret | |
| 631 * Array to which query results will be added. | |
| 632 * @param $table | |
| 633 * The table to be renamed. | |
| 634 * @param $new_name | |
| 635 * The new name for the table. | |
| 636 */ | |
| 637 function db_rename_table(&$ret, $table, $new_name) { | |
| 638 $ret[] = update_sql('ALTER TABLE {'. $table .'} RENAME TO {'. $new_name .'}'); | |
| 639 } | |
| 640 | |
| 641 /** | |
| 642 * Drop a table. | |
| 643 * | |
| 644 * @param $ret | |
| 645 * Array to which query results will be added. | |
| 646 * @param $table | |
| 647 * The table to be dropped. | |
| 648 */ | |
| 649 function db_drop_table(&$ret, $table) { | |
| 650 $ret[] = update_sql('DROP TABLE {'. $table .'}'); | |
| 651 } | |
| 652 | |
| 653 /** | |
| 654 * Add a new field to a table. | |
| 655 * | |
| 656 * @param $ret | |
| 657 * Array to which query results will be added. | |
| 658 * @param $table | |
| 659 * Name of the table to be altered. | |
| 660 * @param $field | |
| 661 * Name of the field to be added. | |
| 662 * @param $spec | |
| 663 * The field specification array, as taken from a schema definition. | |
| 664 * The specification may also contain the key 'initial', the newly | |
| 665 * created field will be set to the value of the key in all rows. | |
| 666 * This is most useful for creating NOT NULL columns with no default | |
| 667 * value in existing tables. | |
| 668 * @param $keys_new | |
| 669 * Optional keys and indexes specification to be created on the | |
| 670 * table along with adding the field. The format is the same as a | |
| 671 * table specification but without the 'fields' element. If you are | |
| 672 * adding a type 'serial' field, you MUST specify at least one key | |
| 673 * or index including it in this array. @see db_change_field for more | |
| 674 * explanation why. | |
| 675 */ | |
| 676 function db_add_field(&$ret, $table, $field, $spec, $new_keys = array()) { | |
| 677 $fixnull = FALSE; | |
| 678 if (!empty($spec['not null']) && !isset($spec['default'])) { | |
| 679 $fixnull = TRUE; | |
| 680 $spec['not null'] = FALSE; | |
| 681 } | |
| 682 $query = 'ALTER TABLE {'. $table .'} ADD COLUMN '; | |
| 683 $query .= _db_create_field_sql($field, _db_process_field($spec)); | |
| 684 $ret[] = update_sql($query); | |
| 685 if (isset($spec['initial'])) { | |
| 686 // All this because update_sql does not support %-placeholders. | |
| 687 $sql = 'UPDATE {'. $table .'} SET '. $field .' = '. db_type_placeholder($spec['type']); | |
| 688 $result = db_query($sql, $spec['initial']); | |
| 689 $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')')); | |
| 690 } | |
| 691 if ($fixnull) { | |
| 692 $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field SET NOT NULL"); | |
| 693 } | |
| 694 if (isset($new_keys)) { | |
| 695 _db_create_keys($ret, $table, $new_keys); | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 /** | |
| 700 * Drop a field. | |
| 701 * | |
| 702 * @param $ret | |
| 703 * Array to which query results will be added. | |
| 704 * @param $table | |
| 705 * The table to be altered. | |
| 706 * @param $field | |
| 707 * The field to be dropped. | |
| 708 */ | |
| 709 function db_drop_field(&$ret, $table, $field) { | |
| 710 $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP COLUMN '. $field); | |
| 711 } | |
| 712 | |
| 713 /** | |
| 714 * Set the default value for a field. | |
| 715 * | |
| 716 * @param $ret | |
| 717 * Array to which query results will be added. | |
| 718 * @param $table | |
| 719 * The table to be altered. | |
| 720 * @param $field | |
| 721 * The field to be altered. | |
| 722 * @param $default | |
| 723 * Default value to be set. NULL for 'default NULL'. | |
| 724 */ | |
| 725 function db_field_set_default(&$ret, $table, $field, $default) { | |
| 726 if ($default == NULL) { | |
| 727 $default = 'NULL'; | |
| 728 } | |
| 729 else { | |
| 730 $default = is_string($default) ? "'$default'" : $default; | |
| 731 } | |
| 732 | |
| 733 $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' SET DEFAULT '. $default); | |
| 734 } | |
| 735 | |
| 736 /** | |
| 737 * Set a field to have no default value. | |
| 738 * | |
| 739 * @param $ret | |
| 740 * Array to which query results will be added. | |
| 741 * @param $table | |
| 742 * The table to be altered. | |
| 743 * @param $field | |
| 744 * The field to be altered. | |
| 745 */ | |
| 746 function db_field_set_no_default(&$ret, $table, $field) { | |
| 747 $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' DROP DEFAULT'); | |
| 748 } | |
| 749 | |
| 750 /** | |
| 751 * Add a primary key. | |
| 752 * | |
| 753 * @param $ret | |
| 754 * Array to which query results will be added. | |
| 755 * @param $table | |
| 756 * The table to be altered. | |
| 757 * @param $fields | |
| 758 * Fields for the primary key. | |
| 759 */ | |
| 760 function db_add_primary_key(&$ret, $table, $fields) { | |
| 761 $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD PRIMARY KEY ('. | |
| 762 implode(',', $fields) .')'); | |
| 763 } | |
| 764 | |
| 765 /** | |
| 766 * Drop the primary key. | |
| 767 * | |
| 768 * @param $ret | |
| 769 * Array to which query results will be added. | |
| 770 * @param $table | |
| 771 * The table to be altered. | |
| 772 */ | |
| 773 function db_drop_primary_key(&$ret, $table) { | |
| 774 $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT {'. $table .'}_pkey'); | |
| 775 } | |
| 776 | |
| 777 /** | |
| 778 * Add a unique key. | |
| 779 * | |
| 780 * @param $ret | |
| 781 * Array to which query results will be added. | |
| 782 * @param $table | |
| 783 * The table to be altered. | |
| 784 * @param $name | |
| 785 * The name of the key. | |
| 786 * @param $fields | |
| 787 * An array of field names. | |
| 788 */ | |
| 789 function db_add_unique_key(&$ret, $table, $name, $fields) { | |
| 790 $name = '{'. $table .'}_'. $name .'_key'; | |
| 791 $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD CONSTRAINT '. | |
| 792 $name .' UNIQUE ('. implode(',', $fields) .')'); | |
| 793 } | |
| 794 | |
| 795 /** | |
| 796 * Drop a unique key. | |
| 797 * | |
| 798 * @param $ret | |
| 799 * Array to which query results will be added. | |
| 800 * @param $table | |
| 801 * The table to be altered. | |
| 802 * @param $name | |
| 803 * The name of the key. | |
| 804 */ | |
| 805 function db_drop_unique_key(&$ret, $table, $name) { | |
| 806 $name = '{'. $table .'}_'. $name .'_key'; | |
| 807 $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT '. $name); | |
| 808 } | |
| 809 | |
| 810 /** | |
| 811 * Add an index. | |
| 812 * | |
| 813 * @param $ret | |
| 814 * Array to which query results will be added. | |
| 815 * @param $table | |
| 816 * The table to be altered. | |
| 817 * @param $name | |
| 818 * The name of the index. | |
| 819 * @param $fields | |
| 820 * An array of field names. | |
| 821 */ | |
| 822 function db_add_index(&$ret, $table, $name, $fields) { | |
| 823 $ret[] = update_sql(_db_create_index_sql($table, $name, $fields)); | |
| 824 } | |
| 825 | |
| 826 /** | |
| 827 * Drop an index. | |
| 828 * | |
| 829 * @param $ret | |
| 830 * Array to which query results will be added. | |
| 831 * @param $table | |
| 832 * The table to be altered. | |
| 833 * @param $name | |
| 834 * The name of the index. | |
| 835 */ | |
| 836 function db_drop_index(&$ret, $table, $name) { | |
| 837 $name = '{'. $table .'}_'. $name .'_idx'; | |
| 838 $ret[] = update_sql('DROP INDEX '. $name); | |
| 839 } | |
| 840 | |
| 841 /** | |
| 842 * Change a field definition. | |
| 843 * | |
| 844 * IMPORTANT NOTE: To maintain database portability, you have to explicitly | |
| 845 * recreate all indices and primary keys that are using the changed field. | |
| 846 * | |
| 847 * That means that you have to drop all affected keys and indexes with | |
| 848 * db_drop_{primary_key,unique_key,index}() before calling db_change_field(). | |
| 849 * To recreate the keys and indices, pass the key definitions as the | |
| 850 * optional $new_keys argument directly to db_change_field(). | |
| 851 * | |
| 852 * For example, suppose you have: | |
| 853 * @code | |
| 854 * $schema['foo'] = array( | |
| 855 * 'fields' => array( | |
| 856 * 'bar' => array('type' => 'int', 'not null' => TRUE) | |
| 857 * ), | |
| 858 * 'primary key' => array('bar') | |
| 859 * ); | |
| 860 * @endcode | |
| 861 * and you want to change foo.bar to be type serial, leaving it as the | |
| 862 * primary key. The correct sequence is: | |
| 863 * @code | |
| 864 * db_drop_primary_key($ret, 'foo'); | |
| 865 * db_change_field($ret, 'foo', 'bar', 'bar', | |
| 866 * array('type' => 'serial', 'not null' => TRUE), | |
| 867 * array('primary key' => array('bar'))); | |
| 868 * @endcode | |
| 869 * | |
| 870 * The reasons for this are due to the different database engines: | |
| 871 * | |
| 872 * On PostgreSQL, changing a field definition involves adding a new field | |
| 873 * and dropping an old one which* causes any indices, primary keys and | |
| 874 * sequences (from serial-type fields) that use the changed field to be dropped. | |
| 875 * | |
| 876 * On MySQL, all type 'serial' fields must be part of at least one key | |
| 877 * or index as soon as they are created. You cannot use | |
| 878 * db_add_{primary_key,unique_key,index}() for this purpose because | |
| 879 * the ALTER TABLE command will fail to add the column without a key | |
| 880 * or index specification. The solution is to use the optional | |
| 881 * $new_keys argument to create the key or index at the same time as | |
| 882 * field. | |
| 883 * | |
| 884 * You could use db_add_{primary_key,unique_key,index}() in all cases | |
| 885 * unless you are converting a field to be type serial. You can use | |
| 886 * the $new_keys argument in all cases. | |
| 887 * | |
| 888 * @param $ret | |
| 889 * Array to which query results will be added. | |
| 890 * @param $table | |
| 891 * Name of the table. | |
| 892 * @param $field | |
| 893 * Name of the field to change. | |
| 894 * @param $field_new | |
| 895 * New name for the field (set to the same as $field if you don't want to change the name). | |
| 896 * @param $spec | |
| 897 * The field specification for the new field. | |
| 898 * @param $new_keys | |
| 899 * Optional keys and indexes specification to be created on the | |
| 900 * table along with changing the field. The format is the same as a | |
| 901 * table specification but without the 'fields' element. | |
| 902 */ | |
| 903 function db_change_field(&$ret, $table, $field, $field_new, $spec, $new_keys = array()) { | |
| 904 $ret[] = update_sql("ALTER TABLE {". $table ."} RENAME $field TO ". $field ."_old"); | |
| 905 $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE; | |
| 906 unset($spec['not null']); | |
| 907 | |
| 908 db_add_field($ret, $table, "$field_new", $spec); | |
| 909 | |
| 910 $ret[] = update_sql("UPDATE {". $table ."} SET $field_new = ". $field ."_old"); | |
| 911 | |
| 912 if ($not_null) { | |
| 913 $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field_new SET NOT NULL"); | |
| 914 } | |
| 915 | |
| 916 db_drop_field($ret, $table, $field .'_old'); | |
| 917 | |
| 918 if (isset($new_keys)) { | |
| 919 _db_create_keys($ret, $table, $new_keys); | |
| 920 } | |
| 921 } | |
| 922 | |
| 923 /** | |
| 924 * @} End of "ingroup schemaapi". | |
| 925 */ | |
| 926 |
