diff --git a/commands/sql/sql.drush.inc b/commands/sql/sql.drush.inc
index 3439cb7..bd9a743 100644
--- a/commands/sql/sql.drush.inc
+++ b/commands/sql/sql.drush.inc
@@ -290,6 +290,82 @@ function drush_sql_get_table_selection() {
   return array('skip' => $skip_tables, 'structure' => $structure_tables, 'tables' => $tables);
 }
 
+function drush_sql_expand_table_selection($table_selection, $db_spec, $site_record = NULL) {
+  if (isset($table_selection['skip'])) {
+    $table_selection['skip'] = drush_sql_expand_wildcard_tables($table_selection['skip'], $db_spec, $site_record);
+  }
+  if (isset($table_selection['structure'])) {
+    $table_selection['structure'] = drush_sql_expand_wildcard_tables($table_selection['structure'], $db_spec, $site_record);
+  }
+  if (isset($table_selection['tables'])) {
+    $table_selection['tables'] = drush_sql_validate_tables($table_selection['tables'], $db_spec, $site_record);
+  }
+  return $table_selection;
+}
+
+function drush_sql_get_expanded_table_selection($db_spec, $site_record = NULL) {
+  return drush_sql_expand_table_selection(drush_sql_get_table_selection(), $db_spec, $site_record);
+}
+
+/**
+ * Expand wildcard tables.
+ *
+ * @param $tables
+ *   Array of table names, some of which may contain wildcards (`*`).
+ * @param $db_spec
+ * @param $site_record
+ * @return
+ *   Array with the corresponding expanded table names, all of which actually
+ *   exist in the database, none of which still contains a wildcard (`*`).
+ */
+function drush_sql_expand_wildcard_tables($tables, $db_spec, $site_record = NULL) {
+  // Get the existing table names contained in the given database.
+  $db_tables = _drush_sql_get_db_table_list($db_spec, $site_record);
+
+  // Table name expansion based on `*` wildcard.
+  $expanded_db_tables = array();
+  foreach ($tables as $k => $table) {
+    // Only deal with table names containing a wildcard.
+    if (strpos($table, '*') !== FALSE) {
+      $pattern = '/^' . str_replace('*', '.*', $table) . '$/i';
+      // Merge those existing tables which match the pattern with the rest of
+      // the expanded table names.
+      $expanded_db_tables += preg_grep($pattern, $db_tables);
+    }
+  }
+  $tables += $expanded_db_tables;
+  $tables = drush_sql_validate_tables($tables, $db_spec);
+  $tables = array_unique($tables);
+  sort($tables);
+
+  return $tables;
+}
+
+/**
+ * Validate tables.
+ *
+ * @param $tables
+ *   Array of table names to validate.
+ * @param $db_spec
+ * @param $site_record
+ * @return
+ *   Array with only valid table names (i.e. all of which actually exist in
+ *   the database).
+ */
+function drush_sql_validate_tables($tables, $db_spec, $site_record = NULL) {
+  // Get the existing table names contained in the current database.
+  $db_tables = _drush_sql_get_db_table_list($db_spec, $site_record);
+
+  // Ensure all the tables actually exist in the database.
+  foreach ($tables as $k => $table) {
+    if (!in_array($table, $db_tables)) {
+      unset($tables[$k]);
+    }
+  }
+
+  return $tables;
+}
+
 /**
  * Build a mysqldump/pg_dump/sqlite statement.
  *
@@ -301,7 +377,11 @@ function drush_sql_get_table_selection() {
  *     2. The filepath where the dump will be saved.
  */
 function drush_sql_dump($db_spec = NULL) {
-  return drush_sql_build_dump_command(drush_sql_get_table_selection(), $db_spec, drush_get_option('result-file', FALSE));
+  if (is_null($db_spec)) {
+    $db_spec = _drush_sql_get_db_spec();
+  }
+  $table_selection = drush_sql_get_expanded_table_selection($db_spec);
+  return drush_sql_build_dump_command($table_selection, $db_spec, drush_get_option('result-file', FALSE));
 }
 
 /**
@@ -329,9 +409,6 @@ function drush_sql_build_dump_command($table_selection, $db_spec = NULL, $file =
   // @see drush_get_option_help() in drush.inc
   $ordered_dump = drush_get_option('ordered-dump');
 
-  if (is_null($db_spec)) {
-    $db_spec = _drush_sql_get_db_spec();
-  }
   $database = $db_spec['database'];
 
   // $file is passed in to us usually via --result-file.  If the user
@@ -527,6 +604,96 @@ function _drush_sql_get_table_list($option_name) {
 }
 
 /**
+ * Extract the name of all existing tables in the given database.
+ *
+ * @param $db_spec
+ * @param $site_record
+ *   Necessary for remote databases.
+ * @return array
+ *   A list of table names which exist in the current database.
+ */
+function _drush_sql_get_db_table_list($db_spec, $site_record = NULL) {
+  $db_tables = drush_get_context('DRUSH_SQL_DB_TABLES', array());
+
+  $cache_key = serialize($db_spec);
+
+  if (isset($db_tables[$cache_key])) {
+    return $db_tables[$cache_key];
+  }
+
+  $suffix = '';
+  $scheme = _drush_sql_get_scheme($db_spec);
+  switch ($scheme) {
+    case 'pgsql':
+      $query = drush_sql_show_tables_pgsql();
+      break;
+    case 'sqlite':
+      $query = '.tables';
+      break;
+    case 'sqlsrv':
+      $query = 'SELECT TABLE_NAME FROM information_schema.tables';
+      break;
+    case 'oracle':
+      $query = "SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME NOT IN ('BLOBS','LONG_IDENTIFIERS')";
+      $suffix = '.sql';
+      break;
+    default:
+      $query = 'SHOW TABLES;';
+  }
+
+  $target_site = '@self';
+  // Remote.
+  if (isset($db_spec['remote-host'])) {
+    if (is_null($site_record)) {
+      drush_log(dt('Retrieving the table list for a remote DB, but the site record is missing.'), 'error');
+      exit;
+    }
+    $target_site = $site_record;
+  }
+  $result = drush_invoke_process($target_site, 'sqlq', array($query), array(), array('override-simulated' => TRUE, 'integrate' => FALSE));
+  $tables_raw = explode("\n", rtrim($result['output']));
+  // Shift off the "Calling system(…);" line at the top of the output.
+  array_shift($tables_raw);
+
+  $tables = array();
+  if (!empty($tables_raw)) {
+    if ($scheme === 'sqlite') {
+      // SQLite's '.tables' command always outputs the table names in a column
+      // format, like this:
+      // table_alpha    table_charlie    table_echo
+      // table_bravo    table_delta      table_foxtrot
+      // …and there doesn't seem to be a way to fix that. So we need to do some
+      // clean-up.
+      $sql = '';
+      foreach ($tables_raw as $line) {
+        preg_match_all('/[^\s]+/', $line, $matches);
+        if (!empty($matches[0])) {
+          foreach ($matches[0] as $match) {
+            $tables[] = $match;
+          }
+        }
+      }
+    }
+    elseif ($scheme === 'sqlsrv') {
+      // Shift off the header of the column of data returned.
+      array_pop($tables_raw);
+      array_pop($tables_raw);
+      $tables = $tables_raw;
+    }
+    else {
+      // Shift off the header of the column of data returned.
+      array_shift($tables_raw);
+      $tables = $tables_raw;
+    }
+  }
+
+  $db_tables[$cache_key] = $tables;
+  drush_set_context('DRUSH_SQL_DB_TABLES', $db_tables);
+
+  return $db_tables[$cache_key];
+}
+
+/**
  * Command callback. Executes the given SQL query on the Drupal database.
  */
 
@@ -612,57 +779,18 @@ function drush_sql_drop() {
 // drop database; create database fails.  If _drush_sql_drop
 // is rewritten to also use that technique, it should maintain
 // the drop tables code here as a fallback.
-function _drush_sql_drop($db_spec = NULL) {
-  // TODO: integrate with _drush_sql_get_table_list?
+function _drush_sql_drop($db_spec) {
+  $tables = _drush_sql_get_db_table_list($db_spec);
 
-  $suffix = '';
   $scheme = _drush_sql_get_scheme($db_spec);
-  switch ($scheme) {
-    case 'pgsql':
-      $query = drush_sql_show_tables_pgsql();
-      break;
-    case 'sqlite':
-      $query = '.tables';
-      break;
-    case 'sqlsrv':
-      $query = 'SELECT TABLE_NAME FROM information_schema.tables';
-      break;
-    case 'oracle':
-      $query = "SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME NOT IN ('BLOBS','LONG_IDENTIFIERS')";
-      $suffix = '.sql';
-      break;
-    default:
-      $query = 'SHOW TABLES;';
-  }
-
-  $filename = drush_save_data_to_temp_file($query, $suffix);
-  $exec = drush_sql_build_exec($db_spec, $filename);
 
-  // Actually run this prep query no matter if in SIMULATE.
-  $old = drush_get_context('DRUSH_SIMULATE');
-  drush_set_context('DRUSH_SIMULATE', FALSE);
-  drush_shell_exec($exec);
-  drush_set_context('DRUSH_SIMULATE', $old);
-  if ($tables = drush_shell_exec_output()) {
+  if (count($tables)) {
     if ($scheme === 'sqlite') {
-      // SQLite's '.tables' command always outputs the table names in a column
-      // format, like this:
-      // table_alpha    table_charlie    table_echo
-      // table_bravo    table_delta      table_foxtrot
-      // …and there doesn't seem to be a way to fix that. So we need to do some
-      // clean-up.
-      // Since we're already doing iteration here, might as well build the SQL
-      // too, since SQLite only wants one table per DROP TABLE command (so we have
-      // to do "DROP TABLE foo; DROP TABLE bar;" instead of
-      // "DROP TABLE foo, bar;").
       $sql = '';
-      foreach ($tables as $line) {
-        preg_match_all('/[^\s]+/', $line, $matches);
-        if (!empty($matches[0])) {
-          foreach ($matches[0] as $match) {
-            $sql .= "DROP TABLE {$match};";
-          }
-        }
+      // SQLite only wants one table per DROP TABLE command (so we have to do
+      // "DROP TABLE foo; DROP TABLE bar;" instead of "DROP TABLE foo, bar;").
+      foreach ($tables as $table) {
+        $sql .= "DROP TABLE {$match};";
       }
       // We can't use drush_op('db_query', $sql) because it will only perform one
       // SQL command and we're technically performing several.
@@ -670,16 +798,7 @@ function _drush_sql_drop($db_spec = NULL) {
       $exec .= " '{$sql}'";
       return drush_op_system($exec) == 0;
     }
-    elseif ($scheme === 'sqlsrv') {
-      // Shift off the header of the column of data returned.
-      array_pop($tables);
-      array_pop($tables);
-      $sql = 'DROP TABLE '. implode(', ', $tables);
-      return _drush_sql_query($sql, $db_spec);
-    }
     else {
-      // Shift off the header of the column of data returned.
-      array_shift($tables);
       $sql = 'DROP TABLE '. implode(', ', $tables);
       return _drush_sql_query($sql, $db_spec);
     }
diff --git a/commands/sql/sync.sql.inc b/commands/sql/sync.sql.inc
index 287c4e6..835aa29 100644
--- a/commands/sql/sync.sql.inc
+++ b/commands/sql/sync.sql.inc
@@ -243,7 +243,7 @@ function drush_sql_sync($source = NULL, $destination = NULL) {
 
     $table_selection = array();
     if (!isset($no_dump)) {
-      $table_selection = drush_sql_get_table_selection();
+      $table_selection = drush_sql_get_expanded_table_selection($source_db_url, $source_settings);
     }
 
     // Prompt for confirmation. This is destructive.
diff --git a/examples/example.drushrc.php b/examples/example.drushrc.php
index 3005d7a..0ad679c 100644
--- a/examples/example.drushrc.php
+++ b/examples/example.drushrc.php
@@ -268,7 +268,7 @@
  * commands when the "--structure-tables-key=common" option is provided.
  * You may add specific tables to the existing array or add a new element.
  */
-# $options['structure-tables']['common'] = array('cache', 'cache_filter', 'cache_menu', 'cache_page', 'history', 'sessions', 'watchdog');
+# $options['structure-tables']['common'] = array('cache', 'cache_*', 'history', 'search_*', 'sessions', 'watchdog');
 
 /**
  * List of tables to be omitted entirely from SQL dumps made by the 'sql-dump'
diff --git a/includes/drush.inc b/includes/drush.inc
index 5be9125..10a9b85 100644
--- a/includes/drush.inc
+++ b/includes/drush.inc
@@ -383,7 +383,7 @@ function drush_get_global_options($brief = FALSE) {
   $options['debug']              = array('short-form' => 'd', 'context' => 'DRUSH_DEBUG', 'description' => 'Display even more information, including internal messages.');
   $options['yes']                = array('short-form' => 'y', 'context' => 'DRUSH_AFFIRMATIVE', 'description' => "Assume 'yes' as answer to all prompts.");
   $options['no']                 = array('short-form' => 'n', 'context' => 'DRUSH_NEGATIVE', 'description' => "Assume 'no' as answer to all prompts.");
-  $options['simulate']           = array('short-form' => 's', 'context' => 'DRUSH_SIMULATE', 'description' => "Simulate all relevant actions (don't actually change the system).");
+  $options['simulate']           = array('short-form' => 's', 'context' => 'DRUSH_SIMULATE', 'never-propagate' => TRUE, 'description' => "Simulate all relevant actions (don't actually change the system).");
   $options['pipe']               = array('short-form' => 'p', 'description' => "Emit a compact representation of the command for scripting.");
   $options['help']               = array('short-form' => 'h', 'description' => "This help system.");
   $options['version']            = array('description' => "Show drush version.");
