#227677 by c960657, yched, cha0s, Dave Reid, et al: Fixed drupal_write_record() can't update a column to NULL. (with tests)

merge-requests/26/head
Angie Byron 2010-01-08 06:07:03 +00:00
parent 09e6a714a3
commit e07b9d35a1
2 changed files with 112 additions and 57 deletions

View File

@ -5773,19 +5773,18 @@ function drupal_schema_fields_sql($table, $prefix = NULL) {
* @param $table
* The name of the table; this must exist in schema API.
* @param $object
* The object to write. This is a reference, as defaults according to
* the schema may be filled in on the object, as well as ID on the serial
* type(s). Both array and object types may be passed.
* The object to write. This is a reference, as defaults according to the
* schema may be filled in on the object, as well as ID on the serial type(s).
* Both array and object types may be passed.
* @param $primary_keys
* If this is an update, specify the primary keys' field names. It is the
* caller's responsibility to know if a record for this object already
* exists in the database. If there is only 1 key, you may pass a simple string.
* caller's responsibility to know if a record for this object already exists
* in the database. If there is only 1 key, you may pass a simple string.
* @return
* Failure to write a record will return FALSE. Otherwise SAVED_NEW or
* SAVED_UPDATED is returned depending on the operation performed. The
* $object parameter contains values for any serial fields defined by
* the $table. For example, $object->nid will be populated after inserting
* a new node.
* SAVED_UPDATED is returned depending on the operation performed. The $object
* parameter contains values for any serial fields defined by the $table. For
* example, $object->nid will be populated after inserting a new a new node.
*/
function drupal_write_record($table, &$object, $primary_keys = array()) {
// Standardize $primary_keys to an array.
@ -5812,54 +5811,52 @@ function drupal_write_record($table, &$object, $primary_keys = array()) {
// Go through our schema, build SQL, and when inserting, fill in defaults for
// fields that are not set.
foreach ($schema['fields'] as $field => $info) {
// Special case -- skip serial types if we are updating.
if ($info['type'] == 'serial' && !empty($primary_keys)) {
continue;
}
// For inserts, populate defaults from schema if not already provided.
if (!isset($object->$field) && empty($primary_keys) && isset($info['default'])) {
$object->$field = $info['default'];
}
// Track serial field so we can helpfully populate them after the query.
// NOTE: Each table should come with one serial field only.
if ($info['type'] == 'serial') {
// Skip serial types if we are updating.
if (!empty($primary_keys)) {
continue;
}
// Track serial field so we can helpfully populate them after the query.
// NOTE: Each table should come with one serial field only.
$serial = $field;
}
// Build arrays for the fields and values in our query.
if (isset($object->$field)) {
if (empty($info['serialize'])) {
$fields[$field] = $object->$field;
}
elseif (!empty($object->$field)) {
$fields[$field] = serialize($object->$field);
}
else {
$fields[$field] = '';
if (!property_exists($object, $field)) {
// Skip fields that are not provided, unless we are inserting and a
// default value is provided by the schema.
if (!empty($primary_keys) || !isset($info['default'])) {
continue;
}
$object->$field = $info['default'];
}
// We don't need to care about type casting if value does not exist.
if (!isset($fields[$field])) {
continue;
// Build array of fields to update or insert.
if (empty($info['serialize'])) {
$fields[$field] = $object->$field;
}
// Special case -- skip null value if field allows null.
if ($fields[$field] == NULL && $info['not null'] == FALSE) {
continue;
}
// Type cast if field does not allow null. Required by DB API.
if ($info['type'] == 'int' || $info['type'] == 'serial') {
$fields[$field] = (int) $fields[$field];
}
elseif ($info['type'] == 'float') {
$fields[$field] = (float) $fields[$field];
elseif (!empty($object->$field)) {
$fields[$field] = serialize($object->$field);
}
else {
$fields[$field] = (string) $fields[$field];
$fields[$field] = '';
}
// Type cast to proper datatype, except when the value is NULL and the
// column allows this.
//
// MySQL PDO silently casts e.g. FALSE and '' to 0 when inserting the value
// into an integer column, but PostgreSQL PDO does not. Also type cast NULL
// when the column does not allow this.
if (!is_null($object->$field) || !empty($info['not null'])) {
if ($info['type'] == 'int' || $info['type'] == 'serial') {
$fields[$field] = (int) $fields[$field];
}
elseif ($info['type'] == 'float') {
$fields[$field] = (float) $fields[$field];
}
else {
$fields[$field] = (string) $fields[$field];
}
}
}

View File

@ -1460,29 +1460,87 @@ class DrupalDataApiTest extends DrupalWebTestCase {
}
function setUp() {
parent::setUp('taxonomy');
parent::setUp('database_test');
}
/**
* Test the drupal_write_record() API function.
*/
function testDrupalWriteRecord() {
// Insert an object record for a table with a single-field primary key.
$vocabulary = new stdClass();
$vocabulary->name = 'test';
$insert_result = drupal_write_record('taxonomy_vocabulary', $vocabulary);
// Insert a record - no columns allow NULL values.
$person = new stdClass();
$person->name = 'John';
$person->unknown_column = 123;
$insert_result = drupal_write_record('test', $person);
$this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.'));
$this->assertTrue(isset($vocabulary->vid), t('Primary key is set on record created with drupal_write_record().'));
$this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
$this->assertIdentical($person->age, 0, t('Age field set to default value.'));
$this->assertIdentical($person->job, 'Undefined', t('Job field set to default value.'));
// Update the initial record after changing a property.
$vocabulary->name = 'testing';
$update_result = drupal_write_record('taxonomy_vocabulary', $vocabulary, array('vid'));
// Verify that the record was inserted.
$result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'John', t('Name field set.'));
$this->assertIdentical($result->age, '0', t('Age field set to default value.'));
$this->assertIdentical($result->job, 'Undefined', t('Job field set to default value.'));
$this->assertFalse(isset($result->unknown_column), t('Unknown column was ignored.'));
// Update the newly created record.
$person->name = 'Peter';
$person->age = 27;
$person->job = NULL;
$update_result = drupal_write_record('test', $person, array('id'));
$this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.'));
// Verify that the record was updated.
$result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Peter', t('Name field set.'));
$this->assertIdentical($result->age, '27', t('Age field set.'));
$this->assertIdentical($result->job, '', t('Job field set and cast to string.'));
// Try to insert NULL in columns that does not allow this.
$person = new stdClass();
$person->name = 'Ringo';
$person->age = NULL;
$person->job = NULL;
$insert_result = drupal_write_record('test', $person);
$this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
$result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Ringo', t('Name field set.'));
$this->assertIdentical($result->age, '0', t('Age field set.'));
$this->assertIdentical($result->job, '', t('Job field set.'));
// Insert a record - the "age" column allows NULL.
$person = new stdClass();
$person->name = 'Paul';
$person->age = NULL;
$insert_result = drupal_write_record('test_null', $person);
$this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
$result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Paul', t('Name field set.'));
$this->assertIdentical($result->age, NULL, t('Age field set.'));
// Insert a record - do not specify the value of a column that allows NULL.
$person = new stdClass();
$person->name = 'Meredith';
$insert_result = drupal_write_record('test_null', $person);
$this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
$this->assertIdentical($person->age, 0, t('Age field set to default value.'));
$result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Meredith', t('Name field set.'));
$this->assertIdentical($result->age, '0', t('Age field set to default value.'));
// Update the newly created record.
$person->name = 'Mary';
$person->age = NULL;
$update_result = drupal_write_record('test_null', $person, array('id'));
$result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Mary', t('Name field set.'));
$this->assertIdentical($result->age, NULL, t('Age field set.'));
// Run an update query where no field values are changed. The database
// layer should return zero for number of affected rows, but
// db_write_record() should still return SAVED_UPDATED.
$update_result = drupal_write_record('taxonomy_vocabulary', $vocabulary, array('vid'));
$update_result = drupal_write_record('test_null', $person, array('id'));
$this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a valid update is run without changing any values.'));
// Insert an object record for a table with a multi-field primary key.