diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
index 5391da2..1f3b68e 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
@@ -1159,6 +1159,7 @@ protected function loadFieldItems(array $entities) {
     // Collect impacted fields.
     $storage_definitions = array();
     $definitions = array();
+    $table_mapping = $this->getTableMapping();
     foreach ($bundles as $bundle => $v) {
       $definitions[$bundle] = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle);
       foreach ($definitions[$bundle] as $field_name => $field_definition) {
@@ -1172,7 +1173,7 @@ protected function loadFieldItems(array $entities) {
     // Load field data.
     $langcodes = array_keys(language_list(LanguageInterface::STATE_ALL));
     foreach ($storage_definitions as $field_name => $storage_definition) {
-      $table = $load_current ? static::_fieldTableName($storage_definition) : static::_fieldRevisionTableName($storage_definition);
+      $table = $load_current ? $table_mapping->getDedicatedDataTableName($storage_definition) : $table_mapping->getDedicatedRevisionTableName($storage_definition);
 
       // Ensure that only values having valid languages are retrieved. Since we
       // are loading values for multiple entities, we cannot limit the query to
@@ -1201,7 +1202,7 @@ protected function loadFieldItems(array $entities) {
             // For each column declared by the field, populate the item from the
             // prefixed database column.
             foreach ($storage_definition->getColumns() as $column => $attributes) {
-              $column_name = static::_fieldColumnName($storage_definition, $column);
+              $column_name = $table_mapping->getFieldColumnName($storage_definition, $column);
               // Unserialize the value if specified in the column schema.
               $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
             }
@@ -1230,6 +1231,7 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
     $entity_type = $entity->getEntityTypeId();
     $default_langcode = $entity->getUntranslated()->language()->id;
     $translation_langcodes = array_keys($entity->getTranslationLanguages());
+    $table_mapping = $this->getTableMapping();
 
     if (!isset($vid)) {
       $vid = $id;
@@ -1240,8 +1242,8 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
       if (!$this->usesDedicatedTable($storage_definition)) {
         continue;
       }
-      $table_name = static::_fieldTableName($storage_definition);
-      $revision_name = static::_fieldRevisionTableName($storage_definition);
+      $table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
+      $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition);
 
       // Delete and insert, rather than update, in case a value was added.
       if ($update) {
@@ -1262,7 +1264,7 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
       $do_insert = FALSE;
       $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
       foreach ($storage_definition->getColumns() as $column => $attributes) {
-        $columns[] = static::_fieldColumnName($storage_definition, $column);
+        $columns[] = $table_mapping->getFieldColumnName($storage_definition, $column);
       }
       $query = $this->database->insert($table_name)->fields($columns);
       $revision_query = $this->database->insert($revision_name)->fields($columns);
@@ -1283,7 +1285,7 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
             'langcode' => $langcode,
           );
           foreach ($storage_definition->getColumns() as $column => $attributes) {
-            $column_name = static::_fieldColumnName($storage_definition, $column);
+            $column_name = $table_mapping->getFieldColumnName($storage_definition, $column);
             // Serialize the value if specified in the column schema.
             $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
           }
@@ -1315,13 +1317,14 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) {
    *   The entity.
    */
   protected function deleteFieldItems(EntityInterface $entity) {
+    $table_mapping = $this->getTableMapping();
     foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) {
       $storage_definition = $field_definition->getFieldStorageDefinition();
       if (!$this->usesDedicatedTable($storage_definition)) {
         continue;
       }
-      $table_name = static::_fieldTableName($storage_definition);
-      $revision_name = static::_fieldRevisionTableName($storage_definition);
+      $table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
+      $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition);
       $this->database->delete($table_name)
         ->condition('entity_id', $entity->id())
         ->execute();
@@ -1340,12 +1343,13 @@ protected function deleteFieldItems(EntityInterface $entity) {
   protected function deleteFieldItemsRevision(EntityInterface $entity) {
     $vid = $entity->getRevisionId();
     if (isset($vid)) {
+      $table_mapping = $this->getTableMapping();
       foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) {
         $storage_definition = $field_definition->getFieldStorageDefinition();
         if (!$this->usesDedicatedTable($storage_definition)) {
           continue;
         }
-        $revision_name = static::_fieldRevisionTableName($storage_definition);
+        $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition);
         $this->database->delete($revision_name)
           ->condition('entity_id', $entity->id())
           ->condition('revision_id', $vid)
@@ -1425,8 +1429,9 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
       // 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 = static::_fieldTableName($original);
-      $revision_table = static::_fieldRevisionTableName($original);
+      $table_mapping = $this->getTableMapping();
+      $table = $table_mapping->getDedicatedDataTableName($original);
+      $revision_table = $table_mapping->getDedicatedRevisionTableName($original);
 
       $schema = $storage_definition->getSchema();
       $original_schema = $original->getSchema();
@@ -1438,8 +1443,8 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
           $this->database->schema()->dropIndex($revision_table, $real_name);
         }
       }
-      $table = static::_fieldTableName($storage_definition);
-      $revision_table = static::_fieldRevisionTableName($storage_definition);
+      $table = $table_mapping->getDedicatedDataTableName($storage_definition);
+      $revision_table = $table_mapping->getDedicatedRevisionTableName($storage_definition);
       foreach ($schema['indexes'] as $name => $columns) {
         if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
           $real_name = static::_fieldIndexName($storage_definition, $name);
@@ -1449,12 +1454,12 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
             // column name and length. Allow for either case.
             if (is_array($column_name)) {
               $real_columns[] = array(
-                static::_fieldColumnName($storage_definition, $column_name[0]),
+                $table_mapping->getFieldColumnName($storage_definition, $column_name[0]),
                 $column_name[1],
               );
             }
             else {
-              $real_columns[] = static::_fieldColumnName($storage_definition, $column_name);
+              $real_columns[] = $table_mapping->getFieldColumnName($storage_definition, $column_name);
             }
           }
           $this->database->schema()->addIndex($table, $real_name, $real_columns);
@@ -1468,17 +1473,19 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
    * {@inheritdoc}
    */
   public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
+    $table_mapping = $this->getTableMapping();
+
     // Mark all data associated with the field for deletion.
-    $table = static::_fieldTableName($storage_definition);
-    $revision_table = static::_fieldRevisionTableName($storage_definition);
+    $table = $table_mapping->getDedicatedDataTableName($storage_definition);
+    $revision_table = $table_mapping->getDedicatedRevisionTableName($storage_definition);
     $this->database->update($table)
       ->fields(array('deleted' => 1))
       ->execute();
 
     // Move the table to a unique name while the table contents are being
     // deleted.
-    $new_table = static::_fieldTableName($storage_definition, TRUE);
-    $revision_new_table = static::_fieldRevisionTableName($storage_definition, TRUE);
+    $new_table = $table_mapping->getDedicatedDataTableName($storage_definition, TRUE);
+    $revision_new_table = $table_mapping->getDedicatedRevisionTableName($storage_definition, TRUE);
     $this->database->schema()->renameTable($table, $new_table);
     $this->database->schema()->renameTable($revision_table, $revision_new_table);
   }
@@ -1487,11 +1494,11 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
    * {@inheritdoc}
    */
   public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) {
+    $table_mapping = $this->getTableMapping();
     $storage_definition = $field_definition->getFieldStorageDefinition();
-    $table_name = static::_fieldTableName($storage_definition);
-    $revision_name = static::_fieldRevisionTableName($storage_definition);
-
     // Mark field data as deleted.
+    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
+    $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition);
     $this->database->update($table_name)
       ->fields(array('deleted' => 1))
       ->condition('bundle', $field_definition->getBundle())
@@ -1514,13 +1521,14 @@ public function onBundleRename($bundle, $bundle_new) {
     // @todo Use the unified store of deleted field definitions instead in
     //   https://www.drupal.org/node/2282119
     $field_definitions += entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $this->entityTypeId, 'bundle' => $bundle, 'deleted' => TRUE, 'include_deleted' => TRUE));
+    $table_mapping = $this->getTableMapping();
 
     foreach ($field_definitions as $field_definition) {
       $storage_definition = $field_definition->getFieldStorageDefinition();
       if ($this->usesDedicatedTable($storage_definition)) {
         $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
-        $table_name = static::_fieldTableName($storage_definition, $is_deleted);
-        $revision_name = static::_fieldRevisionTableName($storage_definition, $is_deleted);
+        $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
+        $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
         $this->database->update($table_name)
           ->fields(array('bundle' => $bundle_new))
           ->condition('bundle', $bundle)
@@ -1541,13 +1549,14 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit
     // bundle fields.
     $storage_definition = $field_definition->getFieldStorageDefinition();
     $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
-    $table_name = static::_fieldTableName($storage_definition, $is_deleted);
+    $table_mapping = $this->getTableMapping();
+    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
 
     // Get the entities which we want to purge first.
     $entity_query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC));
     $or = $entity_query->orConditionGroup();
     foreach ($storage_definition->getColumns() as $column_name => $data) {
-      $or->isNotNull(static::_fieldColumnName($storage_definition, $column_name));
+      $or->isNotNull($table_mapping->getFieldColumnName($storage_definition, $column_name));
     }
     $entity_query
       ->distinct(TRUE)
@@ -1558,7 +1567,7 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit
     // Create a map of field data table column names to field column names.
     $column_map = array();
     foreach ($storage_definition->getColumns() as $column_name => $data) {
-      $column_map[static::_fieldColumnName($storage_definition, $column_name)] = $column_name;
+      $column_map[$table_mapping->getFieldColumnName($storage_definition, $column_name)] = $column_name;
     }
 
     $entities = array();
@@ -1598,23 +1607,27 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit
   protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) {
     $storage_definition = $field_definition->getFieldStorageDefinition();
     $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
-    $table_name = static::_fieldTableName($storage_definition, $is_deleted);
-    $revision_name = static::_fieldRevisionTableName($storage_definition, $is_deleted);
+    $table_mapping = $this->getTableMapping();
+    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
+    $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
     $revision_id = $this->entityType->isRevisionable() ? $entity->getRevisionId() : $entity->id();
     $this->database->delete($table_name)
       ->condition('revision_id', $revision_id)
       ->execute();
-    $this->database->delete($revision_name)
-      ->condition('revision_id', $revision_id)
-      ->execute();
+    if ($this->entityType->isRevisionable()) {
+      $this->database->delete($revision_name)
+        ->condition('revision_id', $revision_id)
+        ->execute();
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) {
-    $table_name = static::_fieldTableName($storage_definition, TRUE);
-    $revision_name = static::_fieldRevisionTableName($storage_definition, TRUE);
+    $table_mapping = $this->getTableMapping();
+    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, TRUE);
+    $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, TRUE);
     $this->database->schema()->dropTable($table_name);
     $this->database->schema()->dropTable($revision_name);
   }
@@ -1623,13 +1636,14 @@ public function finalizePurge(FieldStorageDefinitionInterface $storage_definitio
    * {@inheritdoc}
    */
   public function countFieldData($storage_definition, $as_bool = FALSE) {
-    $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
-    $table_name = static::_fieldTableName($storage_definition, $is_deleted);
+    $table_mapping = $this->getTableMapping();
 
+    $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
+    $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
     $query = $this->database->select($table_name, 't');
     $or = $query->orConditionGroup();
     foreach ($storage_definition->getColumns() as $column_name => $data) {
-      $or->isNotNull(static::_fieldColumnName($storage_definition, $column_name));
+      $or->isNotNull($table_mapping->getFieldColumnName($storage_definition, $column_name));
     }
     $query
       ->condition($or)
@@ -1679,6 +1693,8 @@ protected function storageDefinitionIsDeleted(FieldStorageDefinitionInterface $s
    * @see hook_schema()
    */
   public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_definition, array $schema = NULL, $deleted = FALSE) {
+    $table_mapping = new DefaultTableMapping(array());
+
     $description_current = "Data storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}.";
     $description_revision = "Revision archive storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}.";
 
@@ -1775,7 +1791,7 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_
 
     // Add field columns.
     foreach ($schema['columns'] as $column_name => $attributes) {
-      $real_name = static::_fieldColumnName($storage_definition, $column_name);
+      $real_name = $table_mapping->getFieldColumnName($storage_definition, $column_name);
       $current['fields'][$real_name] = $attributes;
     }
 
@@ -1783,7 +1799,7 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_
     foreach ($schema['unique keys'] as $unique_key_name => $columns) {
       $real_name = static::_fieldIndexName($storage_definition, $unique_key_name);
       foreach ($columns as $column_name) {
-        $current['unique keys'][$real_name][] = static::_fieldColumnName($storage_definition, $column_name);
+        $current['unique keys'][$real_name][] = $table_mapping->getFieldColumnName($storage_definition, $column_name);
       }
     }
 
@@ -1795,12 +1811,12 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_
         // column name and length. Allow for either case.
         if (is_array($column_name)) {
           $current['indexes'][$real_name][] = array(
-            static::_fieldColumnName($storage_definition, $column_name[0]),
+            $table_mapping->getFieldColumnName($storage_definition, $column_name[0]),
             $column_name[1],
           );
         }
         else {
-          $current['indexes'][$real_name][] = static::_fieldColumnName($storage_definition, $column_name);
+          $current['indexes'][$real_name][] = $table_mapping->getFieldColumnName($storage_definition, $column_name);
         }
       }
     }
@@ -1810,7 +1826,7 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_
       $real_name = static::_fieldIndexName($storage_definition, $specifier);
       $current['foreign keys'][$real_name]['table'] = $specification['table'];
       foreach ($specification['columns'] as $column_name => $referenced) {
-        $sql_storage_column = static::_fieldColumnName($storage_definition, $column_name);
+        $sql_storage_column = $table_mapping->getFieldColumnName($storage_definition, $column_name);
         $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
       }
     }
@@ -1823,109 +1839,12 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_
     $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
 
     return array(
-      static::_fieldTableName($storage_definition) => $current,
-      static::_fieldRevisionTableName($storage_definition) => $revision,
+      $table_mapping->getDedicatedDataTableName($storage_definition) => $current,
+      $table_mapping->getDedicatedRevisionTableName($storage_definition) => $revision,
     );
   }
 
   /**
-   * Generates a table name for a field data table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases. Only
-   * call this function to write a query that \Drupal::entityQuery() does not
-   * support. Always call entity_load() before using the data found in the
-   * table.
-   *
-   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
-   *   The field storage definition.
-   * @param bool $is_deleted
-   *   (optional) Whether the table name holding the values of a deleted field
-   *   should be returned.
-   *
-   * @return string
-   *   A string containing the generated name for the database table.
-   */
-  public static function _fieldTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
-    if ($is_deleted) {
-      // When a field is a deleted, the table is renamed to
-      // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
-      // table names longer than 64 characters, we hash the unique storage
-      // identifier and return the first 10 characters so we end up with a short
-      // unique ID.
-      return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
-    }
-    else {
-      return static::_generateFieldTableName($storage_definition, FALSE);
-    }
-  }
-
-  /**
-   * Generates a table name for a field revision archive table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases. Only
-   * call this function to write a query that \Drupal::entityQuery() does not
-   * support. Always call entity_load() before using the data found in the
-   * table.
-   *
-   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
-   *   The field storage definition.
-   * @param bool $is_deleted
-   *   (optional) Whether the table name holding the values of a deleted field
-   *   should be returned.
-   *
-   * @return string
-   *   A string containing the generated name for the database table.
-   */
-  public static function _fieldRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
-    if ($is_deleted) {
-      // When a field is a deleted, the table is renamed to
-      // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
-      // table names longer than 64 characters, we hash the unique storage
-      // identifier and return the first 10 characters so we end up with a short
-      // unique ID.
-      return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
-    }
-    else {
-      return static::_generateFieldTableName($storage_definition, TRUE);
-    }
-  }
-
-  /**
-   * Generates a safe and unanbiguous field table name.
-   *
-   * The method accounts for a maximum table name length of 64 characters, and
-   * takes care of disambiguation.
-   *
-   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
-   *   The field storage definition.
-   * @param bool $revision
-   *   TRUE for revision table, FALSE otherwise.
-   *
-   * @return string
-   *   The final table name.
-   */
-  protected static function _generateFieldTableName(FieldStorageDefinitionInterface $storage_definition, $revision) {
-    $separator = $revision ? '_revision__' : '__';
-    $table_name = $storage_definition->getTargetEntityTypeId() . $separator .  $storage_definition->getName();
-    // Limit the string to 48 characters, keeping a 16 characters margin for db
-    // prefixes.
-    if (strlen($table_name) > 48) {
-      // Use a shorter separator, a truncated entity_type, and a hash of the
-      // field UUID.
-      $separator = $revision ? '_r__' : '__';
-      // Truncate to the same length for the current and revision tables.
-      $entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34);
-      $field_hash = substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
-      $table_name = $entity_type . $separator . $field_hash;
-    }
-    return $table_name;
-  }
-
-  /**
    * Generates an index name for a field data table.
    *
    * @private Calling this function circumvents the entity system and is
@@ -1941,31 +1860,8 @@ protected static function _generateFieldTableName(FieldStorageDefinitionInterfac
    *   A string containing a generated index name for a field data table that is
    *   unique among all other fields.
    */
-  public static function _fieldIndexName(FieldStorageDefinitionInterface $storage_definition, $index) {
+  protected static function _fieldIndexName(FieldStorageDefinitionInterface $storage_definition, $index) {
     return $storage_definition->getName() . '_' . $index;
   }
 
-  /**
-   * Generates a column name for a field data table.
-   *
-   * @private Calling this function circumvents the entity system and is
-   * strongly discouraged. This function is not considered part of the public
-   * API and modules relying on it might break even in minor releases. Only
-   * call this function to write a query that \Drupal::entityQuery() does not
-   * support. Always call entity_load() before using the data found in the
-   * table.
-   *
-   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
-   *   The field storage definition.
-   * @param string $column
-   *   The name of the column.
-   *
-   * @return string
-   *   A string containing a generated column name for a field data table that is
-   *   unique among all other fields.
-   */
-  public static function _fieldColumnName(FieldStorageDefinitionInterface $storage_definition, $column) {
-    return in_array($column, FieldStorageConfig::getReservedColumns()) ? $column : $storage_definition->getName() . '_' . $column;
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 23ce020..c2bc6d8 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -10,10 +10,8 @@
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\Query\QueryException;
 use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
-use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\FieldStorageConfigInterface;
 
 /**
@@ -116,11 +114,14 @@ public function addField($field, $type, $langcode) {
       if ($field_storage instanceof FieldStorageConfigInterface) {
         // Find the field column.
         $column = $field_storage->getMainPropertyName();
+        /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+        $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
+
         if ($key < $count) {
           $next = $specifiers[$key + 1];
           // Is this a field column?
           $columns = $field_storage->getColumns();
-          if (isset($columns[$next]) || in_array($next, FieldStorageConfig::getReservedColumns())) {
+          if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) {
             // Use it.
             $column = $next;
             // Do not process it again.
@@ -142,7 +143,7 @@ public function addField($field, $type, $langcode) {
           }
         }
         $table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
-        $sql_column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $column);
+        $sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
       }
       // This is an entity base field (non-configurable field).
       else {
@@ -220,11 +221,13 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode,
   protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) {
     $field_name = $field->getName();
     if (!isset($this->fieldTables[$index_prefix . $field_name])) {
-      $table = $this->sqlQuery->getMetaData('age') == EntityStorageInterface::FIELD_LOAD_CURRENT ? ContentEntityDatabaseStorage::_fieldTableName($field) : ContentEntityDatabaseStorage::_fieldRevisionTableName($field);
+      $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
+      /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+      $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
+      $table = $this->sqlQuery->getMetaData('age') == EntityStorageInterface::FIELD_LOAD_CURRENT ? $table_mapping->getDedicatedDataTableName($field) : $table_mapping->getDedicatedRevisionTableName($field);
       if ($field->getCardinality() != 1) {
         $this->sqlQuery->addMetaData('simple_query', FALSE);
       }
-      $entity_type = $this->sqlQuery->getMetaData('entity_type');
       $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field", $langcode);
     }
     return $this->fieldTables[$index_prefix . $field_name];
diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
index e872d57..31fbdc2 100644
--- a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
+++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\Core\Entity\Sql;
 
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+
 /**
  * Defines a default table mapping class.
  */
-class DefaultTableMapping implements TableMappingInterface {
+class DefaultTableMapping implements DefaultTableMappingInterface {
 
   /**
    * A list of field storage definitions that are available for this mapping.
@@ -177,4 +179,83 @@ public function setExtraColumns($table_name, array $column_names) {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getReservedColumns() {
+    return array('deleted');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDedicatedDataTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
+    if ($is_deleted) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the unique storage
+      // identifier and return the first 10 characters so we end up with a short
+      // unique ID.
+      return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
+    }
+    else {
+      return $this->generateFieldTableName($storage_definition, FALSE);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDedicatedRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
+    if ($is_deleted) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the unique storage
+      // identifier and return the first 10 characters so we end up with a short
+      // unique ID.
+      return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
+    }
+    else {
+      return $this->generateFieldTableName($storage_definition, TRUE);
+    }
+  }
+
+  /**
+   * Generates a safe and unambiguous field table name.
+   *
+   * The method accounts for a maximum table name length of 64 characters, and
+   * takes care of disambiguation.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
+   *   The field storage definition.
+   * @param bool $revision
+   *   TRUE for revision table, FALSE otherwise.
+   *
+   * @return string
+   *   The final table name.
+   */
+  protected function generateFieldTableName(FieldStorageDefinitionInterface $storage_definition, $revision) {
+    $separator = $revision ? '_revision__' : '__';
+    $table_name = $storage_definition->getTargetEntityTypeId() . $separator . $storage_definition->getName();
+    // Limit the string to 48 characters, keeping a 16 characters margin for db
+    // prefixes.
+    if (strlen($table_name) > 48) {
+      // Use a shorter separator, a truncated entity_type, and a hash of the
+      // field UUID.
+      $separator = $revision ? '_r__' : '__';
+      // Truncate to the same length for the current and revision tables.
+      $entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34);
+      $field_hash = substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
+      $table_name = $entity_type . $separator . $field_hash;
+    }
+    return $table_name;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldColumnName(FieldStorageDefinitionInterface $storage_definition, $column) {
+    return in_array($column, $this->getReservedColumns()) ? $column : $storage_definition->getName() . '_' . $column;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php
new file mode 100644
index 0000000..36be892
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Sql\DefaultTableMappingInterface.
+ */
+
+namespace Drupal\Core\Entity\Sql;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+
+/**
+ * Provides an common interface for mapping field columns to SQL tables.
+ */
+interface DefaultTableMappingInterface extends TableMappingInterface {
+
+  /**
+   * A list of columns that can not be used as field type columns.
+   *
+   * @return array
+   */
+  public function getReservedColumns();
+
+  /**
+   * Generates a table name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
+   *   The field storage definition.
+   * @param bool $is_deleted
+   *   (optional) Whether the table name holding the values of a deleted field
+   *   should be returned.
+   *
+   * @return string
+   *   A string containing the generated name for the database table.
+   */
+  public function getDedicatedDataTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE);
+
+  /**
+   * Generates a table name for a field revision archive table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
+   *   The field storage definition.
+   * @param bool $is_deleted
+   *   (optional) Whether the table name holding the values of a deleted field
+   *   should be returned.
+   *
+   * @return string
+   *   A string containing the generated name for the database table.
+   */
+  public function getDedicatedRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE);
+
+
+  /**
+   * Generates a column name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
+   *   The field storage definition.
+   * @param string $column
+   *   The name of the column.
+   *
+   * @return string
+   *   A string containing a generated column name for a field data table that is
+   *   unique among all other fields.
+   */
+  public function getFieldColumnName(FieldStorageDefinitionInterface $storage_definition, $column);
+
+}
diff --git a/core/modules/contact/src/Tests/Views/ContactFieldsTest.php b/core/modules/contact/src/Tests/Views/ContactFieldsTest.php
index 63c7d1a..a4afee7 100644
--- a/core/modules/contact/src/Tests/Views/ContactFieldsTest.php
+++ b/core/modules/contact/src/Tests/Views/ContactFieldsTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\contact\Tests\Views;
 
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\views\Tests\ViewTestBase;
 
 /**
@@ -60,7 +59,7 @@ protected function setUp() {
   public function testViewsData() {
     // Test that the field is not exposed to views, since contact_message
     // entities have no storage.
-    $table_name = ContentEntityDatabaseStorage::_fieldTableName($this->field_storage);
+    $table_name = 'contact_message__' .  $this->field_storage->getName();
     $data = $this->container->get('views.views_data')->get($table_name);
     $this->assertFalse($data, 'The field is not exposed to Views.');
   }
diff --git a/core/modules/entity_reference/entity_reference.views.inc b/core/modules/entity_reference/entity_reference.views.inc
index e35884b..4580bce 100644
--- a/core/modules/entity_reference/entity_reference.views.inc
+++ b/core/modules/entity_reference/entity_reference.views.inc
@@ -5,7 +5,6 @@
  * Provides views data for the entity_reference module.
  */
 
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\field\FieldStorageConfigInterface;
 
 /**
@@ -14,6 +13,7 @@
 function entity_reference_field_views_data(FieldStorageConfigInterface $field_storage) {
   $data = field_views_field_default_views_data($field_storage);
   $entity_manager = \Drupal::entityManager();
+  $table_mapping = $entity_manager->getStorage($field_storage->getTargetEntityTypeId())->getTableMapping();
   foreach ($data as $table_name => $table_data) {
     // Add a relationship to the target entity type.
     $target_entity_type_id = $field_storage->getSetting('target_type');
@@ -38,13 +38,16 @@ function entity_reference_field_views_data(FieldStorageConfigInterface $field_st
 
     // Provide a reverse relationship for the entity type that is referenced by
     // the field.
-    $pseudo_field_name = 'reverse__' . $field_storage->getTargetEntityTypeId() . '__' . $field_storage->getName();
+    $entity_type_id = $field_storage->getTargetEntityTypeId();
+    $pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_storage->getName();
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = \Drupal::entityManager()->getStorage($entity_type_id)->getTableMapping();
     $data[$target_base_table][$pseudo_field_name]['relationship'] = array(
       'title' => t('@label using @field_name', $args),
       'help' => t('Relate each @label with a @field_name.', $args),
       'id' => 'entity_reverse',
       'field_name' => $field_storage->getName(),
-      'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage),
+      'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
       'field field' => $field_storage->getName() . '_target_id',
       'base' => $target_entity_type->getBaseTable(),
       'base field' => $target_entity_type->getKey('id'),
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index 3f68419..95e360e 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -22,7 +22,7 @@ function field_views_data() {
   $module_handler = \Drupal::moduleHandler();
 
   foreach (\Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple() as $field) {
-    if (_field_views_is_sql_entity_type($field)) {
+    if (_field_views_get_entity_type_storage($field)) {
       $result = (array) $module_handler->invoke($field->module, 'field_views_data', array($field));
       if (empty($result)) {
         $result = field_views_field_default_views_data($field);
@@ -48,7 +48,7 @@ function field_views_data() {
  */
 function field_views_data_alter(&$data) {
   foreach (\Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple() as $field) {
-    if (_field_views_is_sql_entity_type($field)) {
+    if (_field_views_get_entity_type_storage($field)) {
       $function = $field->module . '_field_views_data_views_data_alter';
       if (function_exists($function)) {
         $function($data, $field);
@@ -63,12 +63,17 @@ function field_views_data_alter(&$data) {
  * @param \Drupal\field\FieldStorageConfigInterface $field_storage
  *   The field storage definition.
  *
- * @return bool
- *   True if the entity type uses ContentEntityDatabaseStorage.
+ * @return \Drupal\Core\Entity\ContentEntityDatabaseStorage
+ *   Returns the entity type storage if supported.
  */
-function _field_views_is_sql_entity_type(FieldStorageConfigInterface $field_storage) {
+function _field_views_get_entity_type_storage(FieldStorageConfigInterface $field_storage) {
+  $result = FALSE;
   $entity_manager = \Drupal::entityManager();
-  return $entity_manager->hasDefinition($field_storage->entity_type) && $entity_manager->getStorage($field_storage->entity_type) instanceof ContentEntityDatabaseStorage;
+  if ($entity_manager->hasDefinition($field_storage->getTargetEntityTypeId())) {
+    $storage = $entity_manager->getStorage($field_storage->getTargetEntityTypeId());
+    $result = $storage instanceof ContentEntityDatabaseStorage ? $storage : FALSE;
+  }
+  return $result;
 }
 
 /**
@@ -120,6 +125,11 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
   if (!$field_storage->getBundles()) {
     return $data;
   }
+  // Check whether the entity type storage is supported.
+  $storage = _field_views_get_entity_type_storage($field_storage);
+  if (!$storage) {
+    return $data;
+  }
 
   $field_name = $field_storage->getName();
   $field_columns = $field_storage->getColumns();
@@ -127,7 +137,7 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
   // Grab information about the entity type tables.
   // We need to join to both the base table and the data table, if available.
   $entity_manager = \Drupal::entityManager();
-  $entity_type_id = $field_storage->entity_type;
+  $entity_type_id = $field_storage->getTargetEntityTypeId();
   $entity_type = $entity_manager->getDefinition($entity_type_id);
   if (!$base_table = $entity_type->getBaseTable()) {
     // We cannot do anything if for some reason there is no base table.
@@ -150,15 +160,18 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
   }
 
   // Description of the field tables.
+  // @todo Generalize this code to make it work with any table layout. See
+  //   https://drupal.org/node/2079019.
+  $table_mapping = $storage->getTableMapping();
   $field_tables = array(
     EntityStorageInterface::FIELD_LOAD_CURRENT => array(
-      'table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage),
+      'table' => $table_mapping->getDedicatedDataTableName($field_storage),
       'alias' => "{$entity_type_id}__{$field_name}",
     ),
   );
   if ($supports_revisions) {
     $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION] = array(
-      'table' => ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage),
+      'table' => $table_mapping->getDedicatedRevisionTableName($field_storage),
       'alias' => "{$entity_type_id}_revision__{$field_name}",
     );
   }
@@ -220,7 +233,7 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
   // Build the list of additional fields to add to queries.
   $add_fields = array('delta', 'langcode', 'bundle');
   foreach (array_keys($field_columns) as $column) {
-    $add_fields[] = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $column);
+    $add_fields[] = $table_mapping->getFieldColumnName($field_storage, $column);
   }
   // Determine the label to use for the field. We don't have a label available
   // at the field level, so we just go through all instances and take the one
@@ -344,11 +357,10 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
       else {
         $group = t('@group (historical data)', array('@group' => $group_name));
       }
-      $column_real_name = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $column);
+      $column_real_name = $table_mapping->getFieldColumnName($field_storage, $column);
 
       // Load all the fields from the table by default.
-      $field_sql_schema = ContentEntityDatabaseStorage::_fieldSqlSchema($field_storage);
-      $additional_fields = array_keys($field_sql_schema[$table]['fields']);
+      $additional_fields = $table_mapping->getAllColumns($table);
 
       $data[$table_alias][$column_real_name] = array(
         'group' => $group,
diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php
index ad3a99e..0610843 100644
--- a/core/modules/field/src/Plugin/views/field/Field.php
+++ b/core/modules/field/src/Plugin/views/field/Field.php
@@ -9,7 +9,6 @@
 
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
@@ -353,9 +352,12 @@ public function clickSort($order) {
     }
 
     $this->ensureMyTable();
-    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->definition['entity_type']);
+    $entity_type_id = $this->definition['entity_type'];
+    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
     $field_storage = $field_storage_definitions[$this->definition['field_name']];
-    $column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $this->options['click_sort_column']);
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
+    $column = $table_mapping->getFieldColumnName($field_storage, $this->options['click_sort_column']);
     if (!isset($this->aliases[$column])) {
       // Column is not in query; add a sort on it (without adding the column).
       $this->aliases[$column] = $this->tableAlias . '.' . $column;
diff --git a/core/modules/field/src/Tests/BulkDeleteTest.php b/core/modules/field/src/Tests/BulkDeleteTest.php
index ee099a8..30d8e4b 100644
--- a/core/modules/field/src/Tests/BulkDeleteTest.php
+++ b/core/modules/field/src/Tests/BulkDeleteTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\field\Tests;
 
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\field\Entity\FieldInstanceConfig;
 
@@ -178,11 +177,13 @@ function testDeleteFieldInstance() {
     $this->assertEqual($instance->bundle, $bundle, 'The deleted instance is for the correct bundle');
 
     // Check that the actual stored content did not change during delete.
-    $schema = ContentEntityDatabaseStorage::_fieldSqlSchema($field_storage);
-    $table = ContentEntityDatabaseStorage::_fieldTableName($field_storage);
-    $column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, 'value');
+    $storage = \Drupal::entityManager()->getStorage($this->entity_type);
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = $storage->getTableMapping();
+    $table = $table_mapping->getDedicatedDataTableName($field_storage);
+    $column = $table_mapping->getFieldColumnName($field_storage, 'value');
     $result = db_select($table, 't')
-      ->fields('t', array_keys($schema[$table]['fields']))
+      ->fields('t')
       ->execute();
     foreach ($result as $row) {
       $this->assertEqual($this->entities[$row->entity_id]->{$field_storage->name}->value, $row->$column);
diff --git a/core/modules/field/src/Tests/FieldDataCountTest.php b/core/modules/field/src/Tests/FieldDataCountTest.php
index 1a57fc9..66e60db 100644
--- a/core/modules/field/src/Tests/FieldDataCountTest.php
+++ b/core/modules/field/src/Tests/FieldDataCountTest.php
@@ -76,7 +76,8 @@ public function testEntityCountAndHasData() {
     $storage = \Drupal::entityManager()->getStorage('entity_test');
     if ($storage instanceof ContentEntityDatabaseStorage) {
       // Count the actual number of rows in the field table.
-      $field_table_name = $storage->_fieldTableName($field_storage);
+      $table_mapping = $storage->getTableMapping();
+      $field_table_name = $table_mapping->getDedicatedDataTableName($field_storage);
       $result = db_select($field_table_name, 't')
         ->fields('t')
         ->countQuery()
diff --git a/core/modules/field/src/Tests/Views/ApiDataTest.php b/core/modules/field/src/Tests/Views/ApiDataTest.php
index 7631e29..5ad8a53 100644
--- a/core/modules/field/src/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/src/Tests/Views/ApiDataTest.php
@@ -6,7 +6,6 @@
  */
 
 namespace Drupal\field\Tests\Views;
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 
 /**
  * Tests the Field Views data.
@@ -51,8 +50,10 @@ function testViewsData() {
     // Check the table and the joins of the first field.
     // Attached to node only.
     $field_storage = $this->fieldStorages[0];
-    $current_table = ContentEntityDatabaseStorage::_fieldTableName($field_storage);
-    $revision_table = ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage);
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = \Drupal::entityManager()->getStorage('node')->getTableMapping();
+    $current_table = $table_mapping->getDedicatedDataTableName($field_storage);
+    $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage);
     $data[$current_table] = $views_data->get($current_table);
     $data[$revision_table] = $views_data->get($revision_table);
 
diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc
index bfa3d15..eeb16fd 100644
--- a/core/modules/file/file.views.inc
+++ b/core/modules/file/file.views.inc
@@ -5,7 +5,6 @@
  * Provide views data for file.module.
  */
 
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\field\FieldStorageConfigInterface;
 
 /**
@@ -39,9 +38,12 @@ function file_field_views_data(FieldStorageConfigInterface $field_storage) {
  */
 function file_field_views_data_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) {
   $entity_type_id = $field_storage->entity_type;
-  $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
+  $entity_manager = \Drupal::entityManager();
+  $entity_type = $entity_manager->getDefinition($entity_type_id);
   $field_name = $field_storage->getName();
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;
+  /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+  $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping();
 
   list($label) = field_views_field_label($entity_type_id, $field_name);
 
@@ -51,7 +53,7 @@ function file_field_views_data_views_data_alter(array &$data, FieldStorageConfig
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type_id,
-    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage),
+    'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
     'field field' => $field_name . '_target_id',
     'base' => $entity_type->getBaseTable(),
     'base field' => $entity_type->getKey('id'),
diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc
index 5120350..04c32b9 100644
--- a/core/modules/image/image.views.inc
+++ b/core/modules/image/image.views.inc
@@ -5,7 +5,6 @@
  * Provide views data for image.module.
  */
 
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\field\FieldStorageConfigInterface;
 
 /**
@@ -37,10 +36,13 @@ function image_field_views_data(FieldStorageConfigInterface $field_storage) {
  * Views integration to provide reverse relationships on image fields.
  */
 function image_field_views_data_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) {
-  $entity_type_id = $field_storage->entity_type;
+  $entity_type_id = $field_storage->getTargetEntityTypeId();
   $field_name = $field_storage->getName();
-  $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
+  $entity_manager = \Drupal::entityManager();
+  $entity_type = $entity_manager->getDefinition($entity_type_id);
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;
+  /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+  $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping();
 
   list($label) = field_views_field_label($entity_type_id, $field_name);
 
@@ -50,7 +52,7 @@ function image_field_views_data_views_data_alter(array &$data, FieldStorageConfi
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type_id,
-    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage),
+    'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
     'field field' => $field_name . '_target_id',
     'base' => $entity_type->getBaseTable(),
     'base field' => $entity_type->getKey('id'),
diff --git a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php
index 377312a..4ed1598 100644
--- a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php
@@ -49,7 +49,9 @@ public function testCustomBundleFieldCreateDelete() {
     $this->assertNotNull($definition, 'Field definition found.');
 
     // Make sure the table has been created.
-    $table = $this->entityManager->getStorage('entity_test')->_fieldTableName($definition);
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = $this->entityManager->getStorage('entity_test')->getTableMapping();
+    $table = $table_mapping->getDedicatedDataTableName($definition);
     $this->assertTrue($this->database->schema()->tableExists($table), 'Table created');
     $this->moduleHandler->uninstall(array('entity_bundle_field_test'), FALSE);
     $this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped');
@@ -87,7 +89,9 @@ public function testCustomBundleFieldUsage() {
     $this->assertEqual($entity->custom_field->value, 'cozy', 'Entity was updated correctly.');
 
     $entity->delete();
-    $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field'));
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = $storage->getTableMapping();
+    $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_field'));
     $result = $this->database->select($table, 'f')
       ->fields('f')
       ->condition('f.entity_id', $entity->id())
@@ -100,7 +104,7 @@ public function testCustomBundleFieldUsage() {
     $entity->save();
     entity_test_delete_bundle('custom');
 
-    $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field'));
+    $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_field'));
     $result = $this->database->select($table, 'f')
       ->condition('f.entity_id', $entity->id())
       ->condition('deleted', 1)
diff --git a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php
index 924164c..375012c 100644
--- a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php
@@ -8,7 +8,6 @@
 namespace Drupal\system\Tests\Entity;
 
 use Drupal\Core\Database\Database;
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
 use Drupal\field\Entity\FieldStorageConfig;
 
@@ -56,6 +55,8 @@ class FieldSqlStorageTest extends EntityUnitTestBase {
   protected $instance;
 
   /**
+   * Name of the data table of the field.
+   *
    * @var string
    */
   protected $table;
@@ -67,6 +68,13 @@ class FieldSqlStorageTest extends EntityUnitTestBase {
    */
   protected $revision_table;
 
+  /**
+   * The table mapping for the tested entity type.
+   *
+   * @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping
+   */
+  protected $table_mapping;
+
   protected function setUp() {
     parent::setUp();
 
@@ -88,8 +96,11 @@ protected function setUp() {
     ));
     $this->instance->save();
 
-    $this->table = ContentEntityDatabaseStorage::_fieldTableName($this->fieldStorage);
-    $this->revision_table = ContentEntityDatabaseStorage::_fieldRevisionTableName($this->fieldStorage);
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping();
+    $this->table_mapping = $table_mapping;
+    $this->table = $table_mapping->getDedicatedDataTableName($this->fieldStorage);
+    $this->revision_table = $table_mapping->getDedicatedRevisionTableName($this->fieldStorage);
   }
 
   /**
@@ -99,7 +110,7 @@ function testFieldLoad() {
     $entity_type = $bundle = 'entity_test_rev';
     $storage = $this->container->get('entity.manager')->getStorage($entity_type);
 
-    $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', ContentEntityDatabaseStorage::_fieldColumnName($this->fieldStorage, 'value'));
+    $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', $this->table_mapping->getFieldColumnName($this->fieldStorage, 'value'));
 
     // Create an entity with four revisions.
     $revision_ids = array();
@@ -337,7 +348,7 @@ function testFieldUpdateFailure() {
     // Create a text field.
     $field_storage = entity_create('field_storage_config', array(
       'name' => 'test_text',
-      'entity_type' => 'entity_test',
+      'entity_type' => 'entity_test_rev',
       'type' => 'text',
       'settings' => array('max_length' => 255),
     ));
@@ -355,7 +366,11 @@ function testFieldUpdateFailure() {
     }
 
     // Ensure that the field tables are still there.
-    foreach (ContentEntityDatabaseStorage::_fieldSqlSchema($prior_field_storage) as $table_name => $table_info) {
+    $tables = array(
+      $this->table_mapping->getDedicatedDataTableName($prior_field_storage),
+      $this->table_mapping->getDedicatedRevisionTableName($prior_field_storage),
+    );
+    foreach ($tables as $table_name) {
       $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name)));
     }
   }
@@ -378,7 +393,7 @@ function testFieldUpdateIndexesWithData() {
       'bundle' => $entity_type,
     ));
     $instance->save();
-    $tables = array(ContentEntityDatabaseStorage::_fieldTableName($field_storage), ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage));
+    $tables = array($this->table_mapping->getDedicatedDataTableName($field_storage), $this->table_mapping->getDedicatedRevisionTableName($field_storage));
 
     // Verify the indexes we will create do not exist yet.
     foreach ($tables as $table) {
@@ -444,19 +459,9 @@ function testFieldSqlStorageForeignKeys() {
     // Reload the field schema after the update.
     $schema = $field_storage->getSchema();
 
-    // Retrieve the field definition and check that the foreign key is in place.
-    $field_storage = FieldStorageConfig::loadByName('entity_test', $field_name);
+    // Check that the foreign key is in place.
     $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update');
     $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
-
-    // Verify the SQL schema.
-    $schemas = ContentEntityDatabaseStorage::_fieldSqlSchema($field_storage);
-    $schema = $schemas[ContentEntityDatabaseStorage::_fieldTableName($field_storage)];
-    $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
-    $foreign_key = reset($schema['foreign keys']);
-    $foreign_key_column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $foreign_key_name);
-    $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
-    $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
   }
 
   /**
@@ -504,9 +509,9 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'short_entity_type__short_field_name';
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'short_entity_type_revision__short_field_name';
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected);
 
     // Short entity type, long field name
     $entity_type = 'short_entity_type';
@@ -517,9 +522,9 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'short_entity_type__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'short_entity_type_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected);
 
     // Long entity type, short field name
     $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz';
@@ -530,9 +535,9 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected);
 
     // Long entity type and field name.
     $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz';
@@ -543,17 +548,17 @@ public function testTableNames() {
       'type' => 'test_field',
     ));
     $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected);
     // Try creating a second field and check there are no clashes.
     $field_storage2 = entity_create('field_storage_config', array(
       'entity_type' => $entity_type,
       'name' => $field_name . '2',
       'type' => 'test_field',
     ));
-    $this->assertNotEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), ContentEntityDatabaseStorage::_fieldTableName($field_storage2));
-    $this->assertNotEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage2));
+    $this->assertNotEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $this->table_mapping->getDedicatedDataTableName($field_storage2));
+    $this->assertNotEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $this->table_mapping->getDedicatedRevisionTableName($field_storage2));
 
     // Deleted field.
     $field_storage = entity_create('field_storage_config', array(
@@ -563,9 +568,9 @@ public function testTableNames() {
       'deleted' => TRUE,
     ));
     $expected = 'field_deleted_data_' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage, TRUE), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage, TRUE), $expected);
     $expected = 'field_deleted_revision_' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
-    $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage, TRUE), $expected);
+    $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage, TRUE), $expected);
   }
 
 }
diff --git a/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php
index 511eeec..d23d75f 100644
--- a/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php
@@ -8,7 +8,6 @@
 namespace Drupal\system\Tests\Entity;
 
 use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\field\Entity\FieldStorageConfig;
 
@@ -83,27 +82,24 @@ protected function assertFieldStorageLangcode(ContentEntityInterface $entity, $m
     $id = $entity->id();
     $langcode = $entity->getUntranslated()->language()->id;
     $fields = array($this->field_name, $this->untranslatable_field_name);
+    /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+    $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping();
 
     foreach ($fields as $field_name) {
       $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);
-      $tables = array(
-        ContentEntityDatabaseStorage::_fieldTableName($field_storage),
-        ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage),
-      );
+      $table = $table_mapping->getDedicatedDataTableName($field_storage);
 
-      foreach ($tables as $table) {
-        $record = \Drupal::database()
-          ->select($table, 'f')
-          ->fields('f')
-          ->condition('f.entity_id', $id)
-          ->condition('f.revision_id', $id)
-          ->execute()
-          ->fetchObject();
+      $record = \Drupal::database()
+        ->select($table, 'f')
+        ->fields('f')
+        ->condition('f.entity_id', $id)
+        ->condition('f.revision_id', $id)
+        ->execute()
+        ->fetchObject();
 
-        if ($record->langcode != $langcode) {
-          $status = FALSE;
-          break;
-        }
+      if ($record->langcode != $langcode) {
+        $status = FALSE;
+        break;
       }
     }
 
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index 0f8eaef..0c15085 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -169,7 +169,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       // Save new terms with no parents by default.
       ->setDefaultValue(0)
       ->setSetting('unsigned', TRUE)
-      ->addConstraint('TermParent', array());
+      ->addConstraint('TermParent', array())
+      ->setCustomStorage(TRUE);
 
     $fields['changed'] = BaseFieldDefinition::create('changed')
       ->setLabel(t('Changed'))
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index 383075f..42e0374 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -5,7 +5,6 @@
  * Provides views data for taxonomy.module.
  */
 
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\field\FieldStorageConfigInterface;
 
 /**
@@ -92,9 +91,12 @@ function taxonomy_field_views_data(FieldStorageConfigInterface $field_storage) {
  */
 function taxonomy_field_views_data_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) {
   $field_name = $field_storage->getName();
-  $entity_type_id = $field_storage->entity_type;
-  $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
+  $entity_type_id = $field_storage->getTargetEntityTypeId();
+  $entity_manager = \Drupal::entityManager();
+  $entity_type = $entity_manager->getDefinition($entity_type_id);
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;
+  /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */
+  $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping();
 
   list($label) = field_views_field_label($entity_type_id, $field_name);
 
@@ -104,7 +106,7 @@ function taxonomy_field_views_data_views_data_alter(array &$data, FieldStorageCo
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type_id,
-    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage),
+    'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
     'field field' => $field_name . '_target_id',
     'base' => $entity_type->getBaseTable(),
     'base field' => $entity_type->getKey('id'),
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index e5c8c50..a54d562 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -474,6 +474,7 @@ function hook_field_views_data_alter(array &$data, \Drupal\field\FieldStorageCon
   $field_name = $field_storage->getName();
   $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;
+  $table_mapping = \Drupal::entityManager()->getStorage($entity_type_id)->getTableMapping();
 
   list($label) = field_views_field_label($entity_type_id, $field_name);
 
@@ -483,7 +484,7 @@ function hook_field_views_data_alter(array &$data, \Drupal\field\FieldStorageCon
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type_id,
-    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage),
+    'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
     'field field' => $field_name . '_target_id',
     'base' => $entity_type->getBaseTable(),
     'base field' => $entity_type->getKey('id'),
@@ -531,6 +532,7 @@ function hook_field_views_data_views_data_alter(array &$data, \Drupal\field\Fiel
   $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
   $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;
   list($label) = field_views_field_label($entity_type_id, $field_name);
+  $table_mapping = \Drupal::entityManager()->getStorage($entity_type_id)->getTableMapping();
 
   // Views data for this field is in $data[$data_key].
   $data[$data_key][$pseudo_field_name]['relationship'] = array(
@@ -539,7 +541,7 @@ function hook_field_views_data_views_data_alter(array &$data, \Drupal\field\Fiel
     'id' => 'entity_reverse',
     'field_name' => $field_name,
     'entity_type' => $entity_type_id,
-    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field),
+    'field table' => $table_mapping->getDedicatedDataTableName($field),
     'field field' => $field_name . '_target_id',
     'base' => $entity_type->getBaseTable(),
     'base field' => $entity_type->getKey('id'),
