diff --git a/core/lib/Drupal/Core/Command/DbDumpCommand.php b/core/lib/Drupal/Core/Command/DbDumpCommand.php
index 6951270..fc65646 100644
--- a/core/lib/Drupal/Core/Command/DbDumpCommand.php
+++ b/core/lib/Drupal/Core/Command/DbDumpCommand.php
@@ -110,7 +110,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
   protected function generateScript() {
     $tables = '';
     foreach ($this->getTables() as $table) {
-      $schema = $this->getTableSchema($table);
+      $schema = $this->connection->schema()->getTableSchema($table);
       $data = $this->getTableData($table);
       $tables .= $this->getTableScript($table, $schema, $data);
     }
@@ -144,138 +144,6 @@ protected function getTables() {
   }
 
   /**
-   * Returns a schema array for a given table.
-   *
-   * @param string $table
-   *   The table name.
-   *
-   * @return array
-   *   A schema array (as defined by hook_schema()).
-   *
-   * @todo This implementation is hard-coded for MySQL.
-   */
-  protected function getTableSchema($table) {
-    $query = $this->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($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, 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';
-      }
-
-      // 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.
-    $this->getTableIndexes($table, $definition);
-
-    // Set table collation.
-    $this->getTableCollation($table, $definition);
-
-    return $definition;
-  }
-
-  /**
-   * Adds primary key, unique keys, and index information to the schema.
-   *
-   * @param string $table
-   *   The table to find indexes for.
-   * @param array &$definition
-   *   The schema definition to modify.
-   */
-  protected 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;
-      }
-    }
-  }
-
-  /**
-   * Set the table collation.
-   *
-   * @param string $table
-   *   The table to find indexes for.
-   * @param array &$definition
-   *   The schema definition to modify.
-   */
-  protected function getTableCollation($table, &$definition) {
-    $query = $this->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.
@@ -303,47 +171,6 @@ protected function getTableData($table) {
   }
 
   /**
-   * 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->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 string $type
-   *   The MySQL field type.
-   *
-   * @return string
-   *   The Drupal schema field size.
-   */
-  protected function fieldSizeMap($type) {
-    // Convert everything to lowercase.
-    $map = array_map('strtolower', $this->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 string $table
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
index 28552fa..cfb15f9 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
@@ -137,6 +137,130 @@ protected function createTableSql($name, $table) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getTableSchema($table) {
+    $query = $this->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($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, 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';
+      }
+
+      // 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.
+    $this->getTableIndexes($table, $definition);
+
+    // Set table collation.
+    $this->getTableCollation($table, $definition);
+
+    return $definition;
+  }
+
+  /**
+   * Adds primary key, unique keys, and index information to the schema.
+   *
+   * @param string $table
+   *   The table to find indexes for.
+   * @param array &$definition
+   *   The schema definition to modify.
+   */
+  protected 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;
+      }
+    }
+  }
+
+  /**
+   * Set the table collation.
+   *
+   * @param string $table
+   *   The table to find indexes for.
+   * @param array &$definition
+   *   The schema definition to modify.
+   */
+  protected function getTableCollation($table, &$definition) {
+    $query = $this->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']);
+  }
+
+  /**
    * Create an SQL string for a field to be used in table creation or alteration.
    *
    * Before passing a field out of a schema definition into this function it has
@@ -272,6 +396,47 @@ public function getFieldTypeMap() {
     return $map;
   }
 
+  /**
+   * 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.
+   */
+  protected function fieldSizeMap($type) {
+    // Convert everything to lowercase.
+    $map = array_map('strtolower', $this->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];
+    }
+  }
+
   protected function createKeysSql($spec) {
     $keys = array();
 
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
index 0828d59..4f2f82b 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
@@ -820,6 +820,35 @@ protected function _createKeys($table, $new_keys) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getTableSchema($table) {
+    $definition = [];
+
+    // Do not use {} around the information schema table.
+    $query = $this->connection->query("SELECT * FROM information_schema.columns WHERE table_name = '{" . $table . "}' ORDER BY ordinal_position");
+    while ($row = $query->fetchAssoc()) {
+      $column = $row['column_name'];
+      $definition['fields'][$column] = [
+        // @todo need to combine data_type + udt_name (and others) to get
+        // actual Drupal schema types. Also, collation_name will need to be
+        // taken into account.
+        'type' => $row['udt_name'],
+        'not null' => $row['is_nullable'] == 'NO',
+      ];
+      if ($description = $this->getComment($table, $column)) {
+        $definition['fields'][$column]['description'] = $description;
+      }
+    }
+
+    if ($description = $this->getComment($table)) {
+      $definition['description'] = $description;
+    }
+
+    return $definition;
+  }
+
+  /**
    * Retrieve a table or column comment.
    */
   public function getComment($table, $column = NULL) {
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
index 404cbb2..0bc2e38 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
@@ -723,4 +723,11 @@ public function findTables($table_expression) {
     return $tables;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getTableSchema($table) {
+    // @todo Implement getTableSchema() method.
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php
index bd65330..04806f9 100644
--- a/core/lib/Drupal/Core/Database/Schema.php
+++ b/core/lib/Drupal/Core/Database/Schema.php
@@ -516,6 +516,17 @@ public function fieldExists($table, $column) {
   abstract public function dropIndex($table, $name);
 
   /**
+   * Returns the schema definition for a given table
+   *
+   * @param string $table
+   *   The table name.
+   *
+   * @return array
+   *   The schema definition for a given table.
+   */
+  abstract public function getTableSchema($table);
+
+  /**
    * Change a field definition.
    *
    * IMPORTANT NOTE: To maintain database portability, you have to explicitly
diff --git a/core/modules/system/src/Tests/Database/SchemaTest.php b/core/modules/system/src/Tests/Database/SchemaTest.php
index e6aea74..7fa3192 100644
--- a/core/modules/system/src/Tests/Database/SchemaTest.php
+++ b/core/modules/system/src/Tests/Database/SchemaTest.php
@@ -30,6 +30,7 @@ class SchemaTest extends KernelTestBase {
    * Tests database interactions.
    */
   function testSchema() {
+    $schema = Database::getConnection()->schema();
     // Try creating a table.
     $table_specification = array(
       'description' => 'Schema table description may contain "quotes" and could be long—very long indeed.',
@@ -61,6 +62,7 @@ function testSchema() {
 
     // Assert that the table exists.
     $this->assertTrue(db_table_exists('test_table'), 'The table exists.');
+    $this->assertEqual($table_specification, $schema->getTableSchema('test_table'), 'Correct table schema is generated.');
 
     // Assert that the table comment has been set.
     $this->checkSchemaComment($table_specification['description'], 'test_table');
