Problem
When drupal_write_record() receives an object, it will check for the presence of fields defined in hook_schema().
If a field is not present as properties on the object, drupal_write_record() skips to the next field.
if (!property_exists($object, $field)) {
// Skip fields that are not provided, default values are already known
// by the database.
continue;
}
Now, when a property does exist but is NULL and the field definition does not allow for a NULL value, drupal_write_record() casts it to the correct data type. Meaning NULL becomes '', 0 or 0.0 depending on the expected data type.
// 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 (isset($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];
}
}
This is where drupal_write_record() errs: If a field does not allow a NULL value but does specify a default value, we should not overwrite that default with our own default of '', 0 or 0.0.
Solution
We should check for a default in hook_schema() and fill that in when we receive a NULL property for a non-NULL field.
I can easily write a patch for this, but I'd like to await some comments first.
Use case: Entity API
Exportable entities defined with Entity API expect a default 'status' of 0x01 (ENTITY_CUSTOM). If you document this parameter by adding its declaration to your Entity class extension, all of your entities will be saved with status 0.
This happens because if you use $cake = new Cake();
, the status property will be set to NULL. NULL will then eventually be run through drupal_write_record() and end up as 0. This is wrong, it should respect hook_schema() and be 1 (ENTITY_CUSTOM) instead.