diff --git a/core/lib/Drupal/Core/Command/DbDumpCommand.php b/core/lib/Drupal/Core/Command/DbDumpCommand.php
index 264bd3f..4f5ef30 100644
--- a/core/lib/Drupal/Core/Command/DbDumpCommand.php
+++ b/core/lib/Drupal/Core/Command/DbDumpCommand.php
@@ -4,6 +4,7 @@
 
 use Drupal\Component\Utility\Variable;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\SchemaIntrospectionInterface;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
@@ -130,140 +131,19 @@ protected function getTables(Connection $connection) {
    *
    * @return array
    *   A schema array (as defined by hook_schema()).
-   *
-   * @todo This implementation is hard-coded for MySQL.
    */
   protected function getTableSchema(Connection $connection, $table) {
-    // Check this is MySQL.
-    if ($connection->databaseType() !== 'mysql') {
-      throw new \RuntimeException('This script can only be used with MySQL database backends.');
-    }
-
-    $query = $connection->query("SHOW FULL COLUMNS FROM {" . $table . "}");
-    $definition = [];
-    while (($row = $query->fetchAssoc()) !== FALSE) {
-      $name = $row['Field'];
-      // Parse out the field type and meta information.
-      preg_match('@([a-z]+)(?:\((\d+)(?:,(\d+))?\))?\s*(unsigned)?@', $row['Type'], $matches);
-      $type  = $this->fieldTypeMap($connection, $matches[1]);
-      if ($row['Extra'] === 'auto_increment') {
-        // If this is an auto increment, then the type is 'serial'.
-        $type = 'serial';
-      }
-      $definition['fields'][$name] = [
-        'type' => $type,
-        'not null' => $row['Null'] === 'NO',
-      ];
-      if ($size = $this->fieldSizeMap($connection, $matches[1])) {
-        $definition['fields'][$name]['size'] = $size;
-      }
-      if (isset($matches[2]) && $type === 'numeric') {
-        // Add precision and scale.
-        $definition['fields'][$name]['precision'] = $matches[2];
-        $definition['fields'][$name]['scale'] = $matches[3];
-      }
-      elseif ($type === 'time' || $type === 'datetime') {
-        // @todo Core doesn't support these, but copied from `migrate-db.sh` for now.
-        // Convert to varchar.
-        $definition['fields'][$name]['type'] = 'varchar';
-        $definition['fields'][$name]['length'] = '100';
-      }
-      elseif (!isset($definition['fields'][$name]['size'])) {
-        // Try use the provided length, if it doesn't exist default to 100. It's
-        // not great but good enough for our dumps at this point.
-        $definition['fields'][$name]['length'] = isset($matches[2]) ? $matches[2] : 100;
-      }
-
-      if (isset($row['Default'])) {
-        $definition['fields'][$name]['default'] = $row['Default'];
-      }
-
-      if (isset($matches[4])) {
-        $definition['fields'][$name]['unsigned'] = TRUE;
-      }
-
-      // Check for the 'varchar_ascii' type that should be 'binary'.
-      if (isset($row['Collation']) && $row['Collation'] == 'ascii_bin') {
-        $definition['fields'][$name]['type'] = 'varchar_ascii';
-        $definition['fields'][$name]['binary'] = TRUE;
-      }
-
-      // Check for the non-binary 'varchar_ascii'.
-      if (isset($row['Collation']) && $row['Collation'] == 'ascii_general_ci') {
-        $definition['fields'][$name]['type'] = 'varchar_ascii';
-      }
+    $schema = $connection->schema();
 
-      // Check for the 'utf8_bin' collation.
-      if (isset($row['Collation']) && $row['Collation'] == 'utf8_bin') {
-        $definition['fields'][$name]['binary'] = TRUE;
-      }
+    if ($schema instanceof SchemaIntrospectionInterface) {
+      return $schema->getTableSchema($table);
     }
-
-    // Set primary key, unique keys, and indexes.
-    $this->getTableIndexes($connection, $table, $definition);
-
-    // Set table collation.
-    $this->getTableCollation($connection, $table, $definition);
-
-    return $definition;
-  }
-
-  /**
-   * Adds primary key, unique keys, and index information to the schema.
-   *
-   * @param \Drupal\Core\Database\Connection $connection
-   *   The database connection to use.
-   * @param string $table
-   *   The table to find indexes for.
-   * @param array &$definition
-   *   The schema definition to modify.
-   */
-  protected function getTableIndexes(Connection $connection, $table, &$definition) {
-    // Note, this query doesn't support ordering, so that is worked around
-    // below by keying the array on Seq_in_index.
-    $query = $connection->query("SHOW INDEX FROM {" . $table . "}");
-    while (($row = $query->fetchAssoc()) !== FALSE) {
-      $index_name = $row['Key_name'];
-      $column = $row['Column_name'];
-      // Key the arrays by the index sequence for proper ordering (start at 0).
-      $order = $row['Seq_in_index'] - 1;
-
-      // If specified, add length to the index.
-      if ($row['Sub_part']) {
-        $column = [$column, $row['Sub_part']];
-      }
-
-      if ($index_name === 'PRIMARY') {
-        $definition['primary key'][$order] = $column;
-      }
-      elseif ($row['Non_unique'] == 0) {
-        $definition['unique keys'][$index_name][$order] = $column;
-      }
-      else {
-        $definition['indexes'][$index_name][$order] = $column;
-      }
+    else {
+      throw new \RuntimeException('This script is not compatible with the current database backend.');
     }
   }
 
   /**
-   * Set the table collation.
-   *
-   * @param \Drupal\Core\Database\Connection $connection
-   *   The database connection to use.
-   * @param string $table
-   *   The table to find indexes for.
-   * @param array &$definition
-   *   The schema definition to modify.
-   */
-  protected function getTableCollation(Connection $connection, $table, &$definition) {
-    $query = $connection->query("SHOW TABLE STATUS LIKE '{" . $table . "}'");
-    $data = $query->fetchAssoc();
-
-    // Set `mysql_character_set`. This will be ignored by other backends.
-    $definition['mysql_character_set'] = str_replace('_general_ci', '', $data['Collation']);
-  }
-
-  /**
    * Gets all data from a given table.
    *
    * If a table is set to be schema only, and empty array is returned.
@@ -287,51 +167,6 @@ protected function getTableData(Connection $connection, $table) {
   }
 
   /**
-   * Given a database field type, return a Drupal type.
-   *
-   * @param \Drupal\Core\Database\Connection $connection
-   *   The database connection to use.
-   * @param string $type
-   *   The MySQL field type.
-   *
-   * @return string
-   *   The Drupal schema field type. If there is no mapping, the original field
-   *   type is returned.
-   */
-  protected function fieldTypeMap(Connection $connection, $type) {
-    // Convert everything to lowercase.
-    $map = array_map('strtolower', $connection->schema()->getFieldTypeMap());
-    $map = array_flip($map);
-
-    // The MySql map contains type:size. Remove the size part.
-    return isset($map[$type]) ? explode(':', $map[$type])[0] : $type;
-  }
-
-  /**
-   * Given a database field type, return a Drupal size.
-   *
-   * @param \Drupal\Core\Database\Connection $connection
-   *   The database connection to use.
-   * @param string $type
-   *   The MySQL field type.
-   *
-   * @return string
-   *   The Drupal schema field size.
-   */
-  protected function fieldSizeMap(Connection $connection, $type) {
-    // Convert everything to lowercase.
-    $map = array_map('strtolower', $connection->schema()->getFieldTypeMap());
-    $map = array_flip($map);
-
-    $schema_type = explode(':', $map[$type])[0];
-    // Only specify size on these types.
-    if (in_array($schema_type, ['blob', 'float', 'int', 'text'])) {
-      // The MySql map contains type:size. Remove the type part.
-      return explode(':', $map[$type])[1];
-    }
-  }
-
-  /**
    * Gets field ordering for a given table.
    *
    * @param \Drupal\Core\Database\Connection $connection
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
index c952630..766427f 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Database\Query\Condition;
 use Drupal\Core\Database\SchemaException;
+use Drupal\Core\Database\SchemaIntrospectionInterface;
 use Drupal\Core\Database\SchemaObjectExistsException;
 use Drupal\Core\Database\SchemaObjectDoesNotExistException;
 use Drupal\Core\Database\Schema as DatabaseSchema;
@@ -17,7 +18,7 @@
 /**
  * MySQL implementation of \Drupal\Core\Database\Schema.
  */
-class Schema extends DatabaseSchema {
+class Schema extends DatabaseSchema implements SchemaIntrospectionInterface {
 
   /**
    * Maximum length of a table comment in MySQL.
@@ -598,6 +599,168 @@ public function fieldExists($table, $column) {
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getTableSchema($table) {
+    $definition = [];
+
+    $query = $this->connection->query("SHOW FULL COLUMNS FROM {" . $table . "}");
+    while (($row = $query->fetchAssoc()) !== FALSE) {
+      $name = $row['Field'];
+      // Parse out the field type and meta information.
+      preg_match('@([a-z]+)(?:\((\d+)(?:,(\d+))?\))?\s*(unsigned)?@', $row['Type'], $matches);
+      $type  = $this->fieldTypeMap($matches[1]);
+      if ($row['Extra'] === 'auto_increment') {
+        // If this is an auto increment, then the type is 'serial'.
+        $type = 'serial';
+      }
+      $definition['fields'][$name] = [
+        'type' => $type,
+        'not null' => $row['Null'] === 'NO',
+      ];
+      if ($size = $this->fieldSizeMap($matches[1])) {
+        $definition['fields'][$name]['size'] = $size;
+      }
+      if (isset($matches[2]) && $type === 'numeric') {
+        // Add precision and scale.
+        $definition['fields'][$name]['precision'] = $matches[2];
+        $definition['fields'][$name]['scale'] = $matches[3];
+      }
+      elseif ($type === 'time' || $type === 'datetime') {
+        // @todo Core doesn't support these, so convert to varchar.
+        $definition['fields'][$name]['type'] = 'varchar';
+        $definition['fields'][$name]['length'] = '100';
+      }
+      elseif (!isset($definition['fields'][$name]['size'])) {
+        // Try use the provided length, if it doesn't exist default to 100. It's
+        // not great but good enough for our dumps at this point.
+        $definition['fields'][$name]['length'] = isset($matches[2]) ? $matches[2] : 100;
+      }
+
+      if (isset($row['Default'])) {
+        $definition['fields'][$name]['default'] = $row['Default'];
+      }
+
+      if (isset($matches[4])) {
+        $definition['fields'][$name]['unsigned'] = TRUE;
+      }
+
+      // Check for the 'varchar_ascii' type that should be 'binary'.
+      if (isset($row['Collation']) && $row['Collation'] == 'ascii_bin') {
+        $definition['fields'][$name]['type'] = 'varchar_ascii';
+        $definition['fields'][$name]['binary'] = TRUE;
+      }
+
+      // Check for the non-binary 'varchar_ascii'.
+      if (isset($row['Collation']) && $row['Collation'] == 'ascii_general_ci') {
+        $definition['fields'][$name]['type'] = 'varchar_ascii';
+      }
+
+      // Check for the 'utf8_bin' collation.
+      if (isset($row['Collation']) && $row['Collation'] == 'utf8_bin') {
+        $definition['fields'][$name]['binary'] = TRUE;
+      }
+    }
+
+    // Set primary key, unique keys, and indexes.
+    $definition = array_merge($definition, $this->getTableIndexes($table));
+
+    // Set table collation, if known.
+    if ($collation = $this->getTableCollation($table)) {
+      $definition['mysql_character_set'] = $collation;
+    }
+
+    return $definition;
+  }
+
+  /**
+   * Given a database field type, return a Drupal type.
+   *
+   * @param string $type
+   *   The MySQL field type.
+   *
+   * @return string
+   *   The Drupal schema field type. If there is no mapping, the original field
+   *   type is returned.
+   */
+  protected function fieldTypeMap($type) {
+    // Convert everything to lowercase.
+    $map = array_map('strtolower', $this->getFieldTypeMap());
+    $map = array_flip($map);
+
+    // The MySql map contains type:size. Remove the size part.
+    return isset($map[$type]) ? explode(':', $map[$type])[0] : $type;
+  }
+
+  /**
+   * Given a database field type, return a Drupal size.
+   *
+   * @param string $type
+   *   The MySQL field type.
+   *
+   * @return string
+   *   The Drupal schema field size.
+   */
+  public function fieldSizeMap($type) {
+    // Convert everything to lowercase.
+    $map = array_map('strtolower', $this->getFieldTypeMap());
+    $map = array_flip($map);
+
+    $schema_type = explode(':', $map[$type])[0];
+    // Only specify size on these types.
+    if (in_array($schema_type, ['blob', 'float', 'int', 'text'])) {
+      // The MySQL map contains type:size. Remove the type part.
+      return explode(':', $map[$type])[1];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTableIndexes($table) {
+    $definition = [];
+
+    // Note, this query doesn't support ordering, so that is worked around
+    // below by keying the array on Seq_in_index.
+    $query = $this->connection->query("SHOW INDEX FROM {" . $table . "}");
+    while (($row = $query->fetchAssoc()) !== FALSE) {
+      $index_name = $row['Key_name'];
+      $column = $row['Column_name'];
+      // Key the arrays by the index sequence for proper ordering (start at 0).
+      $order = $row['Seq_in_index'] - 1;
+
+      // If specified, add length to the index.
+      if ($row['Sub_part']) {
+        $column = [$column, $row['Sub_part']];
+      }
+
+      if ($index_name === 'PRIMARY') {
+        $definition['primary key'][$order] = $column;
+      }
+      elseif ($row['Non_unique'] == 0) {
+        $definition['unique keys'][$index_name][$order] = $column;
+      }
+      else {
+        $definition['indexes'][$index_name][$order] = $column;
+      }
+    }
+
+    return $definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTableCollation($table) {
+    $data = $this->connection
+      ->query("SHOW TABLE STATUS LIKE '{" . $table . "}'")
+      ->fetchAssoc();
+
+    // Set `mysql_character_set`. This will be ignored by other backends.
+    return str_replace('_general_ci', '', $data['Collation']);
+  }
+
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Database/SchemaIntrospectionInterface.php b/core/lib/Drupal/Core/Database/SchemaIntrospectionInterface.php
new file mode 100644
index 0000000..e0bf239
--- /dev/null
+++ b/core/lib/Drupal/Core/Database/SchemaIntrospectionInterface.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\Core\Database;
+
+interface SchemaIntrospectionInterface {
+
+  /**
+   * Returns a schema array a given table.
+   *
+   * @param string $table
+   *   The table name.
+   *
+   * @return array
+   *   A full schema definition, suitable for hook_schema().
+   */
+  public function getTableSchema($table);
+
+  /**
+   * Returns primary key, unique keys, and index schema for a table.
+   *
+   * @param string $table
+   *   The table to find indexes for.
+   *
+   * @return array
+   *   The index definition(s), suitable for hook_schema().
+   */
+  public function getTableIndexes($table);
+
+  /**
+   * Returns the table collation.
+   *
+   * @param string $table
+   *   The table to find indexes for.
+   *
+   * @return string|NULL
+   *   The table collation, or NULL if not known.
+   */
+  public function getTableCollation($table);
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
index 8641fd3..f5f7d8d 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\SchemaException;
+use Drupal\Core\Database\SchemaIntrospectionInterface;
 use Drupal\Core\Database\SchemaObjectDoesNotExistException;
 use Drupal\Core\Database\SchemaObjectExistsException;
 use Drupal\KernelTests\KernelTestBase;
@@ -57,6 +58,13 @@ function testSchema() {
     // Assert that the table exists.
     $this->assertTrue(db_table_exists('test_table'), 'The table exists.');
 
+    // If the schema class supports introspection, assert that it generates a
+    // correct representation of the table.
+    $schema = Database::getConnection()->schema();
+    if ($schema instanceof SchemaIntrospectionInterface) {
+      $this->assertEquals($table_specification, $schema->getTableSchema('test_table'));
+    }
+
     // Assert that the table comment has been set.
     $this->checkSchemaComment($table_specification['description'], 'test_table');
 
