Skip to content

Instantly share code, notes, and snippets.

@lxbarth
Created October 14, 2010 22:49
Show Gist options
  • Save lxbarth/627239 to your computer and use it in GitHub Desktop.
Save lxbarth/627239 to your computer and use it in GitHub Desktop.
Updated patch for http://drupal.org/node/937442, requires patch from http://drupal.org/node/937554
? .git
? .gitignore
? 937442-25_maintain_field_schema.patch
? 937442-2_maintain_field_schema.patch
? 937554-1_cleanup_field_data.patch
? sites/default/files
? sites/default/settings.php
Index: modules/field/field.crud.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.crud.inc,v
retrieving revision 1.71
diff -u -p -r1.71 field.crud.inc
--- modules/field/field.crud.inc 29 Sep 2010 01:37:02 -0000 1.71
+++ modules/field/field.crud.inc 14 Oct 2010 22:47:52 -0000
@@ -289,8 +289,8 @@ function field_create_field($field) {
$field += array(
'entity_types' => array(),
'cardinality' => 1,
- 'translatable' => FALSE,
- 'locked' => FALSE,
+ 'translatable' => 0,
+ 'locked' => 0,
'settings' => array(),
'storage' => array(),
'deleted' => 0,
Index: modules/field/field.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.install,v
retrieving revision 1.22
diff -u -p -r1.22 field.install
--- modules/field/field.install 29 Sep 2010 19:46:40 -0000 1.22
+++ modules/field/field.install 14 Oct 2010 22:47:52 -0000
@@ -334,6 +334,76 @@ function _update_7000_field_read_fields(
}
/**
+ * Utility function: Update a field.
+ *
+ * @param $prior_field
+ * A field structure containing the previous field schema definition.
+ * @param $field
+ * A field structure containing the new field schema definition.
+ *
+ * @return
+ * Throws a FieldException if the update cannot be performed.
+ *
+ * @see field_update_field()
+ */
+function _update_7000_field_update_field($prior_field, $field) {
+ // Some updates are always disallowed.
+ if ($field['storage']['type'] != $prior_field['storage']['type']) {
+ throw new FieldException("Cannot change an existing field's storage type.");
+ }
+
+ $has_data = field_has_data($field);
+
+ // Tell the storage engine to update the field. Do this before
+ // saving the new definition since it still might fail.
+ $storage_type = field_info_storage_types($field['storage']['type']);
+ module_invoke($storage_type['module'], 'field_storage_update_field', $field, $prior_field, $has_data);
+
+ // Save the new field definition.
+ // The serialized 'data' column contains everything from $field that does not
+ // have its own column and is not automatically populated when the field is
+ // read.
+ $keys = array(
+ // Ignore {field_config} keys.
+ 'id', 'field_name', 'type', 'module', 'active', 'locked', 'cardinality',
+ 'translatable', 'deleted',
+ // Ignore all possible storage engine keys.
+ 'storage', 'storage_type', 'storage_module', 'storage_active',
+ // Ignore the data key itself.
+ 'data',
+ // Ignore field schema keys.
+ // @todo Right now, this is the only way to make this function work like it
+ // is supposed to work. I.e., without copying field schema information
+ // into the serialized 'data' column, subsequent invocations of
+ // field_update_field() are failing, since field_read_fields() retrieves
+ // the current hook_field_schema(), so there is *nothing* to update.
+ //'columns', 'primary key', 'unique keys', 'indexes', 'foreign keys',
+ // Ignore field_info_field() properties.
+ 'bundles',
+ );
+ $data = array_diff_key($field, array_flip($keys));
+ $field['data'] = $data;
+
+ // Store the field and create the id.
+ db_update('field_config')
+ ->fields(array(
+ 'field_name' => $field['field_name'],
+ 'type' => $field['type'],
+ 'module' => $field['module'],
+ 'active' => $field['active'],
+ 'storage_type' => $field['storage']['type'],
+ 'storage_module' => $field['storage']['module'],
+ 'storage_active' => $field['storage']['active'],
+ 'locked' => $field['locked'],
+ 'cardinality' => $field['cardinality'],
+ 'translatable' => $field['translatable'],
+ 'data' => serialize($field['data']),
+ ))
+ ->condition('id', $field['id'])
+ ->execute();
+}
+
+/**
* Utility function: write a field instance directly to the database.
*
* This function can be used for databases whose schema is at field module
Index: modules/field/modules/field_sql_storage/field_sql_storage.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module,v
retrieving revision 1.56
diff -u -p -r1.56 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module 6 Oct 2010 13:57:47 -0000 1.56
+++ modules/field/modules/field_sql_storage/field_sql_storage.module 14 Oct 2010 22:47:52 -0000
@@ -267,29 +267,61 @@ function field_sql_storage_field_storage
}
}
else {
- // There is data, so there are no column changes. Drop all the
- // prior indexes and create all the new ones, except for all the
- // priors that exist unchanged.
- $table = _field_sql_storage_tablename($prior_field);
- $revision_table = _field_sql_storage_revision_tablename($prior_field);
- foreach ($prior_field['indexes'] as $name => $columns) {
- if (!isset($field['indexes'][$name]) || $columns != $field['indexes'][$name]) {
- $real_name = _field_sql_storage_indexname($field['field_name'], $name);
- db_drop_index($table, $real_name);
- db_drop_index($revision_table, $real_name);
- }
- }
+ // There is data.
$table = _field_sql_storage_tablename($field);
$revision_table = _field_sql_storage_revision_tablename($field);
- foreach ($field['indexes'] as $name => $columns) {
- if (!isset($prior_field['indexes'][$name]) || $columns != $prior_field['indexes'][$name]) {
- $real_name = _field_sql_storage_indexname($field['field_name'], $name);
- $real_columns = array();
- foreach ($columns as $column_name) {
- $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+
+ // Drop prior indexes, except for all unchanged.
+ if (isset($prior_field['indexes'])) {
+ foreach ($prior_field['indexes'] as $name => $columns) {
+ if (!isset($field['indexes'][$name]) || $columns != $field['indexes'][$name]) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $name);
+ db_drop_index($table, $real_name);
+ db_drop_index($revision_table, $real_name);
+ }
+ }
+ }
+
+ // Perform field schema column updates, if any.
+ if (isset($prior_field['columns'])) {
+ foreach ($prior_field['columns'] as $name => $spec) {
+ // Remove a field column.
+ if (!isset($field['columns'][$name])) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $name);
+ db_drop_field($table, $real_name);
+ db_drop_field($revision_table, $real_name);
+ }
+ }
+ }
+ if (isset($field['columns'])) {
+ foreach ($field['columns'] as $name => $spec) {
+ // Add a field column.
+ if (!isset($prior_field['columns'][$name])) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $name);
+ db_add_field($table, $real_name, $spec);
+ db_add_field($revision_table, $real_name, $spec);
+ }
+ // Change a field column.
+ if ($prior_field['columns'][$name] != $spec) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $name);
+ db_change_field($table, $real_name, $real_name, $spec);
+ db_change_field($revision_table, $real_name, $real_name, $spec);
+ }
+ }
+ }
+
+ // Create new indexes, except for all unchanged.
+ if (isset($field['indexes'])) {
+ foreach ($field['indexes'] as $name => $columns) {
+ if (!isset($prior_field['indexes'][$name]) || $columns != $prior_field['indexes'][$name]) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $name);
+ $real_columns = array();
+ foreach ($columns as $column_name) {
+ $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+ }
+ db_add_index($table, $real_name, $real_columns);
+ db_add_index($revision_table, $real_name, $real_columns);
}
- db_add_index($table, $real_name, $real_columns);
- db_add_index($revision_table, $real_name, $real_columns);
}
}
}
Index: modules/field/modules/field_sql_storage/field_sql_storage.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.test,v
retrieving revision 1.22
diff -u -p -r1.22 field_sql_storage.test
--- modules/field/modules/field_sql_storage/field_sql_storage.test 29 Sep 2010 01:37:02 -0000 1.22
+++ modules/field/modules/field_sql_storage/field_sql_storage.test 14 Oct 2010 22:47:52 -0000
@@ -13,6 +13,8 @@
* Tests field storage.
*/
class FieldSqlStorageTestCase extends DrupalWebTestCase {
+ protected $profile = 'testing';
+
public static function getInfo() {
return array(
'name' => 'Field SQL storage tests',
@@ -301,16 +303,34 @@ class FieldSqlStorageTestCase extends Dr
*/
function testUpdateFieldSchemaWithData() {
// Create a decimal 5.2 field and add some data.
- $field = array('field_name' => 'decimal52', 'type' => 'number_decimal', 'settings' => array('precision' => 5, 'scale' => 2));
+ $field_name = 'decimal52';
+ $field = array(
+ 'field_name' => $field_name,
+ 'type' => 'number_decimal',
+ 'settings' => array(
+ 'precision' => 5,
+ 'scale' => 2,
+ ));
$field = field_create_field($field);
$instance = array('field_name' => 'decimal52', 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
$instance = field_create_instance($instance);
$entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
- $entity->decimal52[LANGUAGE_NONE][0]['value'] = '1.235';
- field_attach_insert('test_entity', $entity);
+ $entity->decimal52[LANGUAGE_NONE][0]['value'] = '1.12345';
+ field_test_entity_save($entity);
- // Attempt to update the field in a way that would work without data.
- $field['settings']['scale'] = 3;
+ $entity = field_test_entity_test_load($entity->ftid);
+ $this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], '1.12');
+
+ // Modify the field schema.
+ $prior_field = $field;
+ $field['settings']['precision'] = 2;
+ $field['settings']['scale'] = 1;
+
+ // Attempt to update the field:
+ // - The CRUD API function field_update_field() must fail as field instances
+ // contain data.
+ // - The schema maintenance helper _update_7000_field_update_field() must
+ // not fail for a simple schema modification like the one at hand.
try {
field_update_field($field);
$this->fail(t('Cannot update field schema with data.'));
@@ -318,6 +338,11 @@ class FieldSqlStorageTestCase extends Dr
catch (FieldException $e) {
$this->pass(t('Cannot update field schema with data.'));
}
+
+ _update_7000_field_update_field($prior_field, $field);
+ field_cache_clear();
+ $entity = field_test_entity_test_load($entity->ftid);
+ $this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], '1.1');
}
/**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment