Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Entity / Sql / SqlContentEntityStorage.php
index 22238d4bf50548377308ab9e840598354b599ee1..48545d22ded94b4a7f6b651200196095f18458a5 100644 (file)
@@ -3,12 +3,14 @@
 namespace Drupal\Core\Entity\Sql;
 
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\DatabaseExceptionWrapper;
 use Drupal\Core\Database\SchemaException;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\ContentEntityStorageBase;
+use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\EntityBundleListenerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
@@ -19,7 +21,6 @@ use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Language\LanguageInterface;
-use Drupal\field\FieldStorageConfigInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -133,7 +134,8 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
       $container->get('database'),
       $container->get('entity.manager'),
       $container->get('cache.entity'),
-      $container->get('language_manager')
+      $container->get('language_manager'),
+      $container->get('entity.memory_cache')
     );
   }
 
@@ -161,9 +163,11 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    *   The cache backend to be used.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
+   * @param \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface|null $memory_cache
+   *   The memory cache backend to be used.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager) {
-    parent::__construct($entity_type, $entity_manager, $cache);
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache = NULL) {
+    parent::__construct($entity_type, $entity_manager, $cache, $memory_cache);
     $this->database = $database;
     $this->languageManager = $language_manager;
     $this->initTableLayout();
@@ -181,22 +185,21 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
     $this->dataTable = NULL;
     $this->revisionDataTable = NULL;
 
-    // @todo Remove table names from the entity type definition in
-    //   https://www.drupal.org/node/2232465.
-    $this->baseTable = $this->entityType->getBaseTable() ?: $this->entityTypeId;
+    $table_mapping = $this->getTableMapping();
+    $this->baseTable = $table_mapping->getBaseTable();
     $revisionable = $this->entityType->isRevisionable();
     if ($revisionable) {
       $this->revisionKey = $this->entityType->getKey('revision') ?: 'revision_id';
-      $this->revisionTable = $this->entityType->getRevisionTable() ?: $this->entityTypeId . '_revision';
+      $this->revisionTable = $table_mapping->getRevisionTable();
     }
     $translatable = $this->entityType->isTranslatable();
     if ($translatable) {
-      $this->dataTable = $this->entityType->getDataTable() ?: $this->entityTypeId . '_field_data';
+      $this->dataTable = $table_mapping->getDataTable();
       $this->langcodeKey = $this->entityType->getKey('langcode');
       $this->defaultLangcodeKey = $this->entityType->getKey('default_langcode');
     }
     if ($revisionable && $translatable) {
-      $this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision';
+      $this->revisionDataTable = $table_mapping->getRevisionDataTable();
     }
   }
 
@@ -284,6 +287,11 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    */
   public function setTableMapping(TableMappingInterface $table_mapping) {
     $this->tableMapping = $table_mapping;
+
+    $this->baseTable = $table_mapping->getBaseTable();
+    $this->revisionTable = $table_mapping->getRevisionTable();
+    $this->dataTable = $table_mapping->getDataTable();
+    $this->revisionDataTable = $table_mapping->getRevisionDataTable();
   }
 
   /**
@@ -302,117 +310,41 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    * {@inheritdoc}
    */
   public function getTableMapping(array $storage_definitions = NULL) {
-    $table_mapping = $this->tableMapping;
+    // If a new set of field storage definitions is passed, for instance when
+    // comparing old and new storage schema, we compute the table mapping
+    // without caching.
+    if ($storage_definitions) {
+      return $this->getCustomTableMapping($this->entityType, $storage_definitions);
+    }
 
     // If we are using our internal storage definitions, which is our main use
-    // case, we can statically cache the computed table mapping. If a new set
-    // of field storage definitions is passed, for instance when comparing old
-    // and new storage schema, we compute the table mapping without caching.
-    // @todo Clean-up this in https://www.drupal.org/node/2274017 so we can
-    //   easily instantiate a new table mapping whenever needed.
-    if (!isset($this->tableMapping) || $storage_definitions) {
-      $table_mapping_class = $this->temporary ? TemporaryTableMapping::class : DefaultTableMapping::class;
-      $definitions = $storage_definitions ?: $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
-      /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping|\Drupal\Core\Entity\Sql\TemporaryTableMapping $table_mapping */
-      $table_mapping = new $table_mapping_class($this->entityType, $definitions);
-
-      $shared_table_definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
-        return $table_mapping->allowsSharedTableStorage($definition);
-      });
-
-      $key_fields = array_values(array_filter([$this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey]));
-      $all_fields = array_keys($shared_table_definitions);
-      $revisionable_fields = array_keys(array_filter($shared_table_definitions, function (FieldStorageDefinitionInterface $definition) {
-        return $definition->isRevisionable();
-      }));
-      // Make sure the key fields come first in the list of fields.
-      $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields));
-
-      // If the entity is revisionable, gather the fields that need to be put
-      // in the revision table.
-      $revisionable = $this->entityType->isRevisionable();
-      $revision_metadata_fields = $revisionable ? array_values($this->entityType->getRevisionMetadataKeys()) : [];
-
-      $translatable = $this->entityType->isTranslatable();
-      if (!$revisionable && !$translatable) {
-        // The base layout stores all the base field values in the base table.
-        $table_mapping->setFieldNames($this->baseTable, $all_fields);
-      }
-      elseif ($revisionable && !$translatable) {
-        // The revisionable layout stores all the base field values in the base
-        // table, except for revision metadata fields. Revisionable fields
-        // denormalized in the base table but also stored in the revision table
-        // together with the entity ID and the revision ID as identifiers.
-        $table_mapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields));
-        $revision_key_fields = [$this->idKey, $this->revisionKey];
-        $table_mapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields));
-      }
-      elseif (!$revisionable && $translatable) {
-        // Multilingual layouts store key field values in the base table. The
-        // other base field values are stored in the data table, no matter
-        // whether they are translatable or not. The data table holds also a
-        // denormalized copy of the bundle field value to allow for more
-        // performant queries. This means that only the UUID is not stored on
-        // the data table.
-        $table_mapping
-          ->setFieldNames($this->baseTable, $key_fields)
-          ->setFieldNames($this->dataTable, array_values(array_diff($all_fields, [$this->uuidKey])));
-      }
-      elseif ($revisionable && $translatable) {
-        // The revisionable multilingual layout stores key field values in the
-        // base table, except for language, which is stored in the revision
-        // table along with revision metadata. The revision data table holds
-        // data field values for all the revisionable fields and the data table
-        // holds the data field values for all non-revisionable fields. The data
-        // field values of revisionable fields are denormalized in the data
-        // table, as well.
-        $table_mapping->setFieldNames($this->baseTable, array_values($key_fields));
-
-        // Like in the multilingual, non-revisionable case the UUID is not
-        // in the data table. Additionally, do not store revision metadata
-        // fields in the data table.
-        $data_fields = array_values(array_diff($all_fields, [$this->uuidKey], $revision_metadata_fields));
-        $table_mapping->setFieldNames($this->dataTable, $data_fields);
-
-        $revision_base_fields = array_merge([$this->idKey, $this->revisionKey, $this->langcodeKey], $revision_metadata_fields);
-        $table_mapping->setFieldNames($this->revisionTable, $revision_base_fields);
-
-        $revision_data_key_fields = [$this->idKey, $this->revisionKey, $this->langcodeKey];
-        $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, [$this->langcodeKey]);
-        $table_mapping->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields));
-      }
-
-      // Add dedicated tables.
-      $dedicated_table_definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
-        return $table_mapping->requiresDedicatedTableStorage($definition);
-      });
-      $extra_columns = [
-        'bundle',
-        'deleted',
-        'entity_id',
-        'revision_id',
-        'langcode',
-        'delta',
-      ];
-      foreach ($dedicated_table_definitions as $field_name => $definition) {
-        $tables = [$table_mapping->getDedicatedDataTableName($definition)];
-        if ($revisionable && $definition->isRevisionable()) {
-          $tables[] = $table_mapping->getDedicatedRevisionTableName($definition);
-        }
-        foreach ($tables as $table_name) {
-          $table_mapping->setFieldNames($table_name, [$field_name]);
-          $table_mapping->setExtraColumns($table_name, $extra_columns);
-        }
-      }
+    // case, we can statically cache the computed table mapping.
+    if (!isset($this->tableMapping)) {
+      $storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
 
-      // Cache the computed table mapping only if we are using our internal
-      // storage definitions.
-      if (!$storage_definitions) {
-        $this->tableMapping = $table_mapping;
-      }
+      $this->tableMapping = $this->getCustomTableMapping($this->entityType, $storage_definitions);
     }
 
-    return $table_mapping;
+    return $this->tableMapping;
+  }
+
+  /**
+   * Gets a table mapping for the specified entity type and storage definitions.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
+   *   An entity type definition.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $storage_definitions
+   *   An array of field storage definitions to be used to compute the table
+   *   mapping.
+   *
+   * @return \Drupal\Core\Entity\Sql\TableMappingInterface
+   *   A table mapping object for the entity's tables.
+   *
+   * @internal
+   */
+  public function getCustomTableMapping(ContentEntityTypeInterface $entity_type, array $storage_definitions) {
+    $table_mapping_class = $this->temporary ? TemporaryTableMapping::class : DefaultTableMapping::class;
+    return $table_mapping_class::create($entity_type, $storage_definitions);
   }
 
   /**
@@ -469,9 +401,11 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    * Maps from storage records to entity objects, and attaches fields.
    *
    * @param array $records
-   *   Associative array of query results, keyed on the entity ID.
+   *   Associative array of query results, keyed on the entity ID or revision
+   *   ID.
    * @param bool $load_from_revision
-   *   Flag to indicate whether revisions should be loaded or not.
+   *   (optional) Flag to indicate whether revisions should be loaded or not.
+   *   Defaults to FALSE.
    *
    * @return array
    *   An array of entity objects implementing the EntityInterface.
@@ -481,32 +415,53 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
       return [];
     }
 
+    // Get the names of the fields that are stored in the base table and, if
+    // applicable, the revision table. Other entity data will be loaded in
+    // loadFromSharedTables() and loadFromDedicatedTables().
+    $field_names = $this->tableMapping->getFieldNames($this->baseTable);
+    if ($this->revisionTable) {
+      $field_names = array_unique(array_merge($field_names, $this->tableMapping->getFieldNames($this->revisionTable)));
+    }
+
     $values = [];
     foreach ($records as $id => $record) {
       $values[$id] = [];
       // Skip the item delta and item value levels (if possible) but let the
       // field assign the value as suiting. This avoids unnecessary array
       // hierarchies and saves memory here.
-      foreach ($record as $name => $value) {
-        // Handle columns named [field_name]__[column_name] (e.g for field types
-        // that store several properties).
-        if ($field_name = strstr($name, '__', TRUE)) {
-          $property_name = substr($name, strpos($name, '__') + 2);
-          $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT][$property_name] = $value;
+      foreach ($field_names as $field_name) {
+        $field_columns = $this->tableMapping->getColumnNames($field_name);
+        // Handle field types that store several properties.
+        if (count($field_columns) > 1) {
+          foreach ($field_columns as $property_name => $column_name) {
+            if (property_exists($record, $column_name)) {
+              $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT][$property_name] = $record->{$column_name};
+              unset($record->{$column_name});
+            }
+          }
         }
+        // Handle field types that store only one property.
         else {
-          // Handle columns named directly after the field (e.g if the field
-          // type only stores one property).
-          $values[$id][$name][LanguageInterface::LANGCODE_DEFAULT] = $value;
+          $column_name = reset($field_columns);
+          if (property_exists($record, $column_name)) {
+            $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT] = $record->{$column_name};
+            unset($record->{$column_name});
+          }
         }
       }
+
+      // Handle additional record entries that are not provided by an entity
+      // field, such as 'isDefaultRevision'.
+      foreach ($record as $name => $value) {
+        $values[$id][$name][LanguageInterface::LANGCODE_DEFAULT] = $value;
+      }
     }
 
     // Initialize translations array.
     $translations = array_fill_keys(array_keys($values), []);
 
     // Load values from shared and dedicated tables.
-    $this->loadFromSharedTables($values, $translations);
+    $this->loadFromSharedTables($values, $translations, $load_from_revision);
     $this->loadFromDedicatedTables($values, $load_from_revision);
 
     $entities = [];
@@ -523,11 +478,15 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    * Loads values for fields stored in the shared data tables.
    *
    * @param array &$values
-   *   Associative array of entities values, keyed on the entity ID.
+   *   Associative array of entities values, keyed on the entity ID or the
+   *   revision ID.
    * @param array &$translations
    *   List of translations, keyed on the entity ID.
+   * @param bool $load_from_revision
+   *   Flag to indicate whether revisions should be loaded or not.
    */
-  protected function loadFromSharedTables(array &$values, array &$translations) {
+  protected function loadFromSharedTables(array &$values, array &$translations, $load_from_revision) {
+    $record_key = !$load_from_revision ? $this->idKey : $this->revisionKey;
     if ($this->dataTable) {
       // If a revision table is available, we need all the properties of the
       // latest revision. Otherwise we fall back to the data table.
@@ -535,8 +494,8 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
       $alias = $this->revisionDataTable ? 'revision' : 'data';
       $query = $this->database->select($table, $alias, ['fetch' => \PDO::FETCH_ASSOC])
         ->fields($alias)
-        ->condition($alias . '.' . $this->idKey, array_keys($values), 'IN')
-        ->orderBy($alias . '.' . $this->idKey);
+        ->condition($alias . '.' . $record_key, array_keys($values), 'IN')
+        ->orderBy($alias . '.' . $record_key);
 
       $table_mapping = $this->getTableMapping();
       if ($this->revisionDataTable) {
@@ -559,7 +518,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
           // Some fields can have more then one columns in the data table so
           // column names are needed.
           foreach ($data_fields as $data_field) {
-            // \Drupal\Core\Entity\Sql\TableMappingInterface:: getColumNames()
+            // \Drupal\Core\Entity\Sql\TableMappingInterface::getColumnNames()
             // returns an array keyed by property names so remove the keys
             // before array_merge() to avoid losing data with fields having the
             // same columns i.e. value.
@@ -581,7 +540,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
 
       $result = $query->execute();
       foreach ($result as $row) {
-        $id = $row[$this->idKey];
+        $id = $row[$record_key];
 
         // Field values in default language are stored with
         // LanguageInterface::LANGCODE_DEFAULT as key.
@@ -609,19 +568,35 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    * {@inheritdoc}
    */
   protected function doLoadRevisionFieldItems($revision_id) {
-    $revision = NULL;
+    @trigger_error('"\Drupal\Core\Entity\ContentEntityStorageBase::doLoadRevisionFieldItems()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. "\Drupal\Core\Entity\ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems()" should be implemented instead. See https://www.drupal.org/node/2924915.', E_USER_DEPRECATED);
+
+    $revisions = $this->doLoadMultipleRevisionsFieldItems([$revision_id]);
+
+    return !empty($revisions) ? reset($revisions) : NULL;
+  }
 
-    // Build and execute the query.
-    $query_result = $this->buildQuery([], $revision_id)->execute();
-    $records = $query_result->fetchAllAssoc($this->idKey);
+  /**
+   * {@inheritdoc}
+   */
+  protected function doLoadMultipleRevisionsFieldItems($revision_ids) {
+    $revisions = [];
+
+    // Sanitize IDs. Before feeding ID array into buildQuery, check whether
+    // it is empty as this would load all entity revisions.
+    $revision_ids = $this->cleanIds($revision_ids, 'revision');
 
-    if (!empty($records)) {
-      // Convert the raw records to entity objects.
-      $entities = $this->mapFromStorageRecords($records, TRUE);
-      $revision = reset($entities) ?: NULL;
+    if (!empty($revision_ids)) {
+      // Build and execute the query.
+      $query_result = $this->buildQuery(NULL, $revision_ids)->execute();
+      $records = $query_result->fetchAllAssoc($this->revisionKey);
+
+      // Map the loaded records into entity objects and according fields.
+      if ($records) {
+        $revisions = $this->mapFromStorageRecords($records, TRUE);
+      }
     }
 
-    return $revision;
+    return $revisions;
   }
 
   /**
@@ -677,20 +652,23 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    *
    * @param array|null $ids
    *   An array of entity IDs, or NULL to load all entities.
-   * @param $revision_id
-   *   The ID of the revision to load, or FALSE if this query is asking for the
-   *   most current revision(s).
+   * @param array|bool $revision_ids
+   *   The IDs of the revisions to load, or FALSE if this query is asking for
+   *   the default revisions. Defaults to FALSE.
    *
    * @return \Drupal\Core\Database\Query\Select
    *   A SelectQuery object for loading the entity.
    */
-  protected function buildQuery($ids, $revision_id = FALSE) {
-    $query = $this->database->select($this->entityType->getBaseTable(), 'base');
+  protected function buildQuery($ids, $revision_ids = FALSE) {
+    $query = $this->database->select($this->baseTable, 'base');
 
     $query->addTag($this->entityTypeId . '_load_multiple');
 
-    if ($revision_id) {
-      $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", [':revisionId' => $revision_id]);
+    if ($revision_ids) {
+      if (!is_array($revision_ids)) {
+        @trigger_error('Passing a single revision ID to "\Drupal\Core\Entity\Sql\SqlContentEntityStorage::buildQuery()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. An array of revision IDs should be given instead. See https://www.drupal.org/node/2924915.', E_USER_DEPRECATED);
+      }
+      $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} IN (:revisionIds[])", [':revisionIds[]' => (array) $revision_ids]);
     }
     elseif ($this->revisionTable) {
       $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
@@ -760,7 +738,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
   protected function doDeleteFieldItems($entities) {
     $ids = array_keys($entities);
 
-    $this->database->delete($this->entityType->getBaseTable())
+    $this->database->delete($this->baseTable)
       ->condition($this->idKey, $ids, 'IN')
       ->execute();
 
@@ -843,10 +821,13 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
       if ($update) {
         $default_revision = $entity->isDefaultRevision();
         if ($default_revision) {
+          // Remove the ID from the record to enable updates on SQL variants
+          // that prevent updating serial columns, for example, mssql.
+          unset($record->{$this->idKey});
           $this->database
             ->update($this->baseTable)
             ->fields((array) $record)
-            ->condition($this->idKey, $record->{$this->idKey})
+            ->condition($this->idKey, $entity->get($this->idKey)->value)
             ->execute();
         }
         if ($this->revisionTable) {
@@ -855,11 +836,15 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
           }
           else {
             $record = $this->mapToStorageRecord($entity->getUntranslated(), $this->revisionTable);
+            // Remove the revision ID from the record to enable updates on SQL
+            // variants that prevent updating serial columns, for example,
+            // mssql.
+            unset($record->{$this->revisionKey});
             $entity->preSaveRevision($this, $record);
             $this->database
               ->update($this->revisionTable)
               ->fields((array) $record)
-              ->condition($this->revisionKey, $record->{$this->revisionKey})
+              ->condition($this->revisionKey, $entity->getRevisionId())
               ->execute();
           }
         }
@@ -1081,24 +1066,26 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
         $record->{$this->revisionKey} = $insert_id;
       }
       if ($entity->isDefaultRevision()) {
-        $this->database->update($this->entityType->getBaseTable())
+        $this->database->update($this->baseTable)
           ->fields([$this->revisionKey => $record->{$this->revisionKey}])
           ->condition($this->idKey, $record->{$this->idKey})
           ->execute();
       }
+      // Make sure to update the new revision key for the entity.
+      $entity->{$this->revisionKey}->value = $record->{$this->revisionKey};
     }
     else {
+      // Remove the revision ID from the record to enable updates on SQL
+      // variants that prevent updating serial columns, for example,
+      // mssql.
+      unset($record->{$this->revisionKey});
       $this->database
         ->update($this->revisionTable)
         ->fields((array) $record)
-        ->condition($this->revisionKey, $record->{$this->revisionKey})
+        ->condition($this->revisionKey, $entity->getRevisionId())
         ->execute();
     }
-
-    // Make sure to update the new revision key for the entity.
-    $entity->{$this->revisionKey}->value = $record->{$this->revisionKey};
-
-    return $record->{$this->revisionKey};
+    return $entity->getRevisionId();
   }
 
   /**
@@ -1114,8 +1101,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    * @param array &$values
    *   An array of values keyed by entity ID.
    * @param bool $load_from_revision
-   *   (optional) Flag to indicate whether revisions should be loaded or not,
-   *   defaults to FALSE.
+   *   Flag to indicate whether revisions should be loaded or not.
    */
   protected function loadFromDedicatedTables(array &$values, $load_from_revision) {
     if (empty($values)) {
@@ -1167,21 +1153,22 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
       foreach ($results as $row) {
         $bundle = $row->bundle;
 
+        $value_key = !$load_from_revision ? $row->entity_id : $row->revision_id;
         // Field values in default language are stored with
         // LanguageInterface::LANGCODE_DEFAULT as key.
         $langcode = LanguageInterface::LANGCODE_DEFAULT;
-        if ($this->langcodeKey && isset($default_langcodes[$row->entity_id]) && $row->langcode != $default_langcodes[$row->entity_id]) {
+        if ($this->langcodeKey && isset($default_langcodes[$value_key]) && $row->langcode != $default_langcodes[$value_key]) {
           $langcode = $row->langcode;
         }
 
-        if (!isset($values[$row->entity_id][$field_name][$langcode])) {
-          $values[$row->entity_id][$field_name][$langcode] = [];
+        if (!isset($values[$value_key][$field_name][$langcode])) {
+          $values[$value_key][$field_name][$langcode] = [];
         }
 
         // Ensure that records for non-translatable fields having invalid
         // languages are skipped.
         if ($langcode == LanguageInterface::LANGCODE_DEFAULT || $definitions[$bundle][$field_name]->isTranslatable()) {
-          if ($storage_definition->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($values[$row->entity_id][$field_name][$langcode]) < $storage_definition->getCardinality()) {
+          if ($storage_definition->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($values[$value_key][$field_name][$langcode]) < $storage_definition->getCardinality()) {
             $item = [];
             // For each column declared by the field, populate the item from the
             // prefixed database column.
@@ -1192,7 +1179,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
             }
 
             // Add the item to the field values for the entity.
-            $values[$row->entity_id][$field_name][$langcode][] = $item;
+            $values[$value_key][$field_name][$langcode][] = $item;
           }
         }
       }
@@ -1438,14 +1425,6 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    * {@inheritdoc}
    */
   public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
-    // If we are adding a field stored in a shared table we need to recompute
-    // the table mapping.
-    // @todo This does not belong here. Remove it once we are able to generate a
-    //   fresh table mapping in the schema handler. See
-    //   https://www.drupal.org/node/2274017.
-    if ($this->getTableMapping()->allowsSharedTableStorage($storage_definition)) {
-      $this->tableMapping = NULL;
-    }
     $this->wrapSchemaException(function () use ($storage_definition) {
       $this->getStorageSchema()->onFieldStorageDefinitionCreate($storage_definition);
     });
@@ -1468,9 +1447,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
       $this->entityManager->getLastInstalledFieldStorageDefinitions($this->entityType->id())
     );
 
-    // @todo Remove the FieldStorageConfigInterface check when non-configurable
-    //   fields support purging: https://www.drupal.org/node/2282119.
-    if ($storage_definition instanceof FieldStorageConfigInterface && $table_mapping->requiresDedicatedTableStorage($storage_definition)) {
+    if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
       // Mark all data associated with the field for deletion.
       $table = $table_mapping->getDedicatedDataTableName($storage_definition);
       $revision_table = $table_mapping->getDedicatedRevisionTableName($storage_definition);
@@ -1554,9 +1531,8 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
     // Check whether the whole field storage definition is gone, or just some
     // bundle fields.
     $storage_definition = $field_definition->getFieldStorageDefinition();
-    $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
     $table_mapping = $this->getTableMapping();
-    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
+    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $storage_definition->isDeleted());
 
     // Get the entities which we want to purge first.
     $entity_query = $this->database->select($table_name, 't', ['fetch' => \PDO::FETCH_ASSOC]);
@@ -1614,7 +1590,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    */
   protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) {
     $storage_definition = $field_definition->getFieldStorageDefinition();
-    $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
+    $is_deleted = $storage_definition->isDeleted();
     $table_mapping = $this->getTableMapping();
     $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
     $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
@@ -1651,7 +1627,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
     $table_mapping = $this->getTableMapping($storage_definitions);
 
     if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
-      $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
+      $is_deleted = $storage_definition->isDeleted();
       if ($this->entityType->isRevisionable()) {
         $table_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
       }
@@ -1673,16 +1649,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
     elseif ($table_mapping->allowsSharedTableStorage($storage_definition)) {
       // Ascertain the table this field is mapped too.
       $field_name = $storage_definition->getName();
-      try {
-        $table_name = $table_mapping->getFieldTableName($field_name);
-      }
-      catch (SqlContentEntityStorageException $e) {
-        // This may happen when changing field storage schema, since we are not
-        // able to use a table mapping matching the passed storage definition.
-        // @todo Revisit this once we are able to instantiate the table mapping
-        //   properly. See https://www.drupal.org/node/2274017.
-        $table_name = $this->dataTable ?: $this->baseTable;
-      }
+      $table_name = $table_mapping->getFieldTableName($field_name);
       $query = $this->database->select($table_name, 't');
       $or = $query->orConditionGroup();
       foreach (array_keys($storage_definition->getColumns()) as $property_name) {
@@ -1724,15 +1691,14 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    *
    * @return bool
    *   Whether the field has been already deleted.
+   *
+   * @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use
+   *   \Drupal\Core\Field\FieldStorageDefinitionInterface::isDeleted() instead.
+   *
+   * @see https://www.drupal.org/node/2907785
    */
   protected function storageDefinitionIsDeleted(FieldStorageDefinitionInterface $storage_definition) {
-    // Configurable fields are marked for deletion.
-    if ($storage_definition instanceof FieldStorageConfigInterface) {
-      return $storage_definition->isDeleted();
-    }
-    // For non configurable fields check whether they are still in the last
-    // installed schema repository.
-    return !array_key_exists($storage_definition->getName(), $this->entityManager->getLastInstalledFieldStorageDefinitions($this->entityTypeId));
+    return $storage_definition->isDeleted();
   }
 
 }