/**
  * Updates the schema for a field stored in a shared table.
  *
  * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  *   The storage definition of the field being updated.
  * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
  *   The original storage definition; i.e., the definition before the update.
  *
  * @throws \Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException
  *   Thrown when the update to the field is forbidden.
  * @throws \Exception
  *   Rethrown exception if the table recreation fails.
  */
 protected function updateSharedTableSchema(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original)
 {
     if (!$this->storage->countFieldData($original, TRUE)) {
         if ($this->database->supportsTransactionalDDL()) {
             // If the database supports transactional DDL, we can go ahead and rely
             // on it. If not, we will have to rollback manually if something fails.
             $transaction = $this->database->startTransaction();
         }
         try {
             // Since there is no data we may be switching from a dedicated table
             // to a schema table schema, hence we should use the proper API.
             $this->performFieldSchemaOperation('delete', $original);
             $this->performFieldSchemaOperation('create', $storage_definition);
         } catch (\Exception $e) {
             if ($this->database->supportsTransactionalDDL()) {
                 $transaction->rollback();
             } else {
                 // Recreate original schema.
                 $this->createSharedTableSchema($original);
             }
             throw $e;
         }
     } else {
         if ($this->hasColumnChanges($storage_definition, $original)) {
             throw new FieldStorageDefinitionUpdateForbiddenException('The SQL storage cannot change the schema for an existing field (' . $storage_definition->getName() . ' in ' . $storage_definition->getTargetEntityTypeId() . ' entity) with data.');
         }
         $updated_field_name = $storage_definition->getName();
         $table_mapping = $this->storage->getTableMapping();
         $column_names = $table_mapping->getColumnNames($updated_field_name);
         $schema_handler = $this->database->schema();
         // Iterate over the mapped table to find the ones that host the deleted
         // field schema.
         $original_schema = $this->loadFieldSchemaData($original);
         $schema = array();
         foreach ($table_mapping->getTableNames() as $table_name) {
             foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
                 if ($field_name == $updated_field_name) {
                     $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);
                     // Handle NOT NULL constraints.
                     foreach ($schema[$table_name]['fields'] as $column_name => $specifier) {
                         $not_null = !empty($specifier['not null']);
                         $original_not_null = !empty($original_schema[$table_name]['fields'][$column_name]['not null']);
                         if ($not_null !== $original_not_null) {
                             if ($not_null && $this->hasNullFieldPropertyData($table_name, $column_name)) {
                                 throw new EntityStorageException("The {$column_name} column cannot have NOT NULL constraints as it holds NULL values.");
                             }
                             $column_schema = $original_schema[$table_name]['fields'][$column_name];
                             $column_schema['not null'] = $not_null;
                             $schema_handler->changeField($table_name, $field_name, $field_name, $column_schema);
                         }
                     }
                     // Drop original indexes and unique keys.
                     if (!empty($original_schema[$table_name]['indexes'])) {
                         foreach ($original_schema[$table_name]['indexes'] as $name => $specifier) {
                             $schema_handler->dropIndex($table_name, $name);
                         }
                     }
                     if (!empty($original_schema[$table_name]['unique keys'])) {
                         foreach ($original_schema[$table_name]['unique keys'] as $name => $specifier) {
                             $schema_handler->dropUniqueKey($table_name, $name);
                         }
                     }
                     // Create new indexes and unique keys.
                     if (!empty($schema[$table_name]['indexes'])) {
                         foreach ($schema[$table_name]['indexes'] as $name => $specifier) {
                             // Check if the index exists because it might already have been
                             // created as part of the earlier entity type update event.
                             $this->addIndex($table_name, $name, $specifier, $schema[$table_name]);
                         }
                     }
                     if (!empty($schema[$table_name]['unique keys'])) {
                         foreach ($schema[$table_name]['unique keys'] as $name => $specifier) {
                             $schema_handler->addUniqueKey($table_name, $name, $specifier);
                         }
                     }
                     // After deleting the field schema skip to the next table.
                     break;
                 }
             }
         }
         $this->saveFieldSchemaData($storage_definition, $schema);
     }
 }
 /**
  * Updates the schema for a field stored in a shared table.
  *
  * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  *   The storage definition of the field being updated.
  * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
  *   The original storage definition; i.e., the definition before the update.
  *
  * @throws \Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException
  *   Thrown when the update to the field is forbidden.
  * @throws \Exception
  *   Rethrown exception if the table recreation fails.
  */
 protected function updateSharedTableSchema(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original)
 {
     if (!$this->storage->countFieldData($original, TRUE)) {
         if ($this->database->supportsTransactionalDDL()) {
             // If the database supports transactional DDL, we can go ahead and rely
             // on it. If not, we will have to rollback manually if something fails.
             $transaction = $this->database->startTransaction();
         }
         try {
             // Since there is no data we may be switching from a dedicated table
             // to a schema table schema, hence we should use the proper API.
             $this->performFieldSchemaOperation('delete', $original);
             $this->performFieldSchemaOperation('create', $storage_definition);
         } catch (\Exception $e) {
             if ($this->database->supportsTransactionalDDL()) {
                 $transaction->rollback();
             } else {
                 // Recreate original schema.
                 $this->createSharedTableSchema($original);
             }
             throw $e;
         }
     } else {
         if ($storage_definition->getColumns() != $original->getColumns()) {
             throw new FieldStorageDefinitionUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
         }
         $updated_field_name = $storage_definition->getName();
         $table_mapping = $this->storage->getTableMapping();
         $column_names = $table_mapping->getColumnNames($updated_field_name);
         $schema_handler = $this->database->schema();
         // Iterate over the mapped table to find the ones that host the deleted
         // field schema.
         $original_schema = $this->loadFieldSchemaData($original);
         $schema = array();
         foreach ($table_mapping->getTableNames() as $table_name) {
             foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
                 if ($field_name == $updated_field_name) {
                     $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);
                     // Drop original indexes and unique keys.
                     if (!empty($original_schema[$table_name]['indexes'])) {
                         foreach ($original_schema[$table_name]['indexes'] as $name => $specifier) {
                             $schema_handler->dropIndex($table_name, $name);
                         }
                     }
                     if (!empty($original_schema[$table_name]['unique keys'])) {
                         foreach ($original_schema[$table_name]['unique keys'] as $name => $specifier) {
                             $schema_handler->dropUniqueKey($table_name, $name);
                         }
                     }
                     // Create new indexes and unique keys.
                     if (!empty($schema[$table_name]['indexes'])) {
                         foreach ($schema[$table_name]['indexes'] as $name => $specifier) {
                             $schema_handler->addIndex($table_name, $name, $specifier);
                         }
                     }
                     if (!empty($schema[$table_name]['unique keys'])) {
                         foreach ($schema[$table_name]['unique keys'] as $name => $specifier) {
                             $schema_handler->addUniqueKey($table_name, $name, $specifier);
                         }
                     }
                     // After deleting the field schema skip to the next table.
                     break;
                 }
             }
         }
         $this->saveFieldSchemaData($storage_definition, $schema);
     }
 }