summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/database.inc42
-rw-r--r--includes/database.mysql-common.inc40
-rw-r--r--includes/database.pgsql.inc34
3 files changed, 100 insertions, 16 deletions
diff --git a/includes/database.inc b/includes/database.inc
index 053228310..ee60b80d9 100644
--- a/includes/database.inc
+++ b/includes/database.inc
@@ -445,5 +445,47 @@ function db_field_names($fields) {
}
/**
+ * Given a Schema API field type, return the correct %-placeholder to
+ * embed in a query to be passed to db_query along with a value from a
+ * column of the specified type.
+ *
+ * @param $type
+ * The Schema API type of a field.
+ * @return
+ * The placeholder string to embed in a query for that type.
+ */
+function _db_type_placeholder($type) {
+ 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';
+
+ case 'serial':
+ case 'int':
+ return '%d';
+
+ case 'float':
+ return '%f';
+
+ case 'blob':
+ return '%b';
+ }
+
+ // There is no safe value to return here, so return something that
+ // will cause the query to fail.
+ return 'unsupported type '. $type . 'for _db_type_placeholder';
+}
+
+/**
* @} End of "defgroup schemaapi".
*/
diff --git a/includes/database.mysql-common.inc b/includes/database.mysql-common.inc
index 7bb4580c6..ce96918e8 100644
--- a/includes/database.mysql-common.inc
+++ b/includes/database.mysql-common.inc
@@ -257,12 +257,31 @@ function db_drop_table(&$ret, $table) {
* @param $field
* Name of the field to be added.
* @param $spec
- * The field specification array, as taken from a schema definition
+ * The field specification array, as taken from a schema definition.
+ * The specification may also contain the key 'initial', the newly
+ * created field will be set to the value of the key in all rows.
+ * This is most useful for creating NOT NULL columns with no default
+ * value in existing tables.
*/
function db_add_field(&$ret, $table, $field, $spec) {
+ $fixnull = FALSE;
+ if (!empty($spec['not null']) && !isset($spec['default'])) {
+ $fixnull = TRUE;
+ $spec['not null'] = FALSE;
+ }
$query = 'ALTER TABLE {'. $table .'} ADD ';
$query .= _db_create_field_sql($field, _db_process_field($spec));
$ret[] = update_sql($query);
+ if (isset($spec['initial'])) {
+ // All this because update_sql does not support %-placeholders.
+ $sql = 'UPDATE {'. $table .'} SET '. $field .' = '. _db_type_placeholder($spec['type']);
+ $result = db_query($sql, $spec['initial']);
+ $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')'));
+ }
+ if ($fixnull) {
+ $spec['not null'] = TRUE;
+ db_change_field($ret, $table, $field, $field, $spec);
+ }
}
/**
@@ -387,15 +406,7 @@ function db_drop_unique_key(&$ret, $table, $name) {
* An array of field names.
*/
function db_add_index(&$ret, $table, $name, $fields) {
- $query = 'ALTER TABLE {'. $table .'} ADD INDEX '. $name .' (';
-
- foreach ($fields as $current) {
- $query .= $current .', ';
- }
-
- // Remove the last comma, add a closing bracket.
- $query = substr($query, 0, -2) .')';
-
+ $query = 'ALTER TABLE {'. $table .'} ADD INDEX '. $name .' ('. _db_create_key_sql($fields) . ')';
$ret[] = update_sql($query);
}
@@ -416,6 +427,15 @@ function db_drop_index(&$ret, $table, $name) {
/**
* Change a field definition.
*
+ * IMPORTANT NOTE: On some database systems (notably PostgreSQL),
+ * changing a field definition involves adding a new field and
+ * dropping an old one. This means that any indices, primary keys and
+ * sequences (from serial-type fields) that use the field to be
+ * changed get dropped. For database portability, you MUST drop them
+ * explicitly before calling db_change_field() and then re-create them
+ * afterwards. Use db_{add,drop}_{primary_key,unique_key,index} for
+ * this purpose.
+ *
* @param $ret
* Array to which query results will be added.
* @param $table
diff --git a/includes/database.pgsql.inc b/includes/database.pgsql.inc
index c5fd4401b..23fd2b8d6 100644
--- a/includes/database.pgsql.inc
+++ b/includes/database.pgsql.inc
@@ -251,7 +251,7 @@ function db_last_insert_id($table, $field) {
*/
function db_affected_rows() {
global $last_result;
- return pg_affected_rows($last_result);
+ return empty($last_result) ? 0 : pg_affected_rows($last_result);
}
/**
@@ -658,12 +658,30 @@ function db_drop_table(&$ret, $table) {
* @param $field
* Name of the field to be added.
* @param $spec
- * The field specification array, as taken from a schema definition
+ * The field specification array, as taken from a schema definition.
+ * The specification may also contain the key 'initial', the newly
+ * created field will be set to the value of the key in all rows.
+ * This is most useful for creating NOT NULL columns with no default
+ * value in existing tables.
*/
function db_add_field(&$ret, $table, $field, $spec) {
+ $fixnull = FALSE;
+ if (!empty($spec['not null']) && !isset($spec['default'])) {
+ $fixnull = TRUE;
+ $spec['not null'] = FALSE;
+ }
$query = 'ALTER TABLE {'. $table .'} ADD COLUMN ';
$query .= _db_create_field_sql($field, _db_process_field($spec));
$ret[] = update_sql($query);
+ if (isset($spec['initial'])) {
+ // All this because update_sql does not support %-placeholders.
+ $sql = 'UPDATE {'. $table .'} SET '. $field .' = '. _db_type_placeholder($spec['type']);
+ $result = db_query($sql, $spec['initial']);
+ $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')'));
+ }
+ if ($fixnull) {
+ $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field SET NOT NULL");
+ }
}
/**
@@ -811,10 +829,14 @@ function db_drop_index(&$ret, $table, $name) {
/**
* Change a field definition.
*
- * Remember that changing a field definition involves adding a new field
- * and dropping an old one. This means that any indices, primary keys and
- * sequences from serial-type fields are dropped and might need to be
- * recreated.
+ * IMPORTANT NOTE: On some database systems (notably PostgreSQL),
+ * changing a field definition involves adding a new field and
+ * dropping an old one. This means that any indices, primary keys and
+ * sequences (from serial-type fields) that use the field to be
+ * changed get dropped. For database portability, you MUST drop them
+ * explicitly before calling db_change_field() and then re-create them
+ * afterwards. Use db_{add,drop}_{primary_key,unique_key,index} for
+ * this purpose.
*
* @param $ret
* Array to which query results will be added.