? drush_pm
? drush_pm_cvs
? drush_pm_svn
? drush_pm_wget
? drush_simpletest
? drush_sql
? drush_tools
? drushrc.phpe
? example.drushrc.phpe
? services
? commands/alias
? commands/site
? commands/sitealias
? commands/pm/handler
? includes/table.inc
Index: drush.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/drush.php,v
retrieving revision 1.72
diff -u -p -r1.72 drush.php
--- drush.php	30 Sep 2009 15:27:46 -0000	1.72
+++ drush.php	1 Oct 2009 22:16:25 -0000
@@ -70,6 +70,31 @@ function drush_main() {
   foreach ($phases as $phase) {
     if (drush_bootstrap($phase)) {
       $command = drush_parse_command();
+
+      // Process a remote command if 'remote-host' option is set.
+      $remote_host = drush_get_option('remote-host');
+      if(isset($remote_host)) {         
+        
+        $args = drush_get_arguments();
+        $command = array_shift($args);
+        $data = drush_get_context('options');
+        foreach (array('r', 'root', 'l', 'uri', 'v', 'd', 'i') as $key) {
+          unset($data[$key]);
+        }
+        foreach (array('root', 'uri') as $key) {
+          $data[$key] = drush_get_option($key);
+        }
+       
+        $drush_path = drush_get_option('drush');
+        $remote_user = drush_get_option('remote-user');
+        drush_log(dt('Begin drush_backend_invoke'));
+        $values = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
+        drush_log(dt('drush_backend_invoke is complete'));
+
+        $command_found = TRUE;
+        break;
+      }
+
       if (is_array($command)) {
         if ($command['bootstrap'] == $phase && empty($command['bootstrap_errors'])) {
           drush_log(dt("Found command: !command", array('!command' => $command['command'])), 'bootstrap');
Index: example.drushrc.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/example.drushrc.php,v
retrieving revision 1.12
diff -u -p -r1.12 example.drushrc.php
--- example.drushrc.php	3 Sep 2009 11:54:58 -0000	1.12
+++ example.drushrc.php	1 Oct 2009 22:16:25 -0000
@@ -81,3 +81,62 @@ $options['structure-tables'] = array(
 #   'theme_default' => 'minnelli',
 #   'anonymous' => 'Visitor',
 # );
+
+/**
+ * Site aliases:
+ *
+ * To create aliases to remote Drupal installations, add entries
+ * to the site-aliases option array here.  These settings can be
+ * used in place of a site specification on the command line, and
+ * may also be used in arguments to certain commands such as
+ * "drush sync", "drush sql sync" and "drush sql load".
+ *
+ * Each entry in the 'site-aliases' array is accessed by its
+ * site alias (e.g. 'stage' or 'dev').  Most alias records use
+ * only a few of these keys; a simple alias record can be generated
+ * using the "drush alias print" command.
+ *
+ * The following settings are stored in the site alias record:
+ *
+ * - 'db-url': The database connection string from settings.php.
+ *   For remote databases accessed via an ssh tunnel, set the port
+ *   number to the tunneled port as it is accessed on the local machine.
+ *   If 'db-url' is not provided, then drush will automatically look it
+ *   up, either from settings.php on the local machine, or via backend invoke
+ *   if the target alias specifies a remote server.
+ * - 'remote-port': If the database is remote and 'db-url' contains
+ *   a tunneled port number, put the actual database port number
+ *   used on the remote machine in the 'remote-port' setting.
+ * - 'uri': This should always be the same as the site's folder name
+ *   in the 'sites' folder.  Behaves like the "-l" command-line flag.
+ * - 'remote-host': The fully-qualified domain name of the remote system
+ *   hosting the Drupal instance.  The remote-host option must be
+ *   omitted for local sites, as this option controls whether or not
+ *   rsync parameters are for local or remote machines.
+ * - 'remote-user': The username to log in as when using ssh or rsync.
+ * - 'path-aliases': An array of aliases for common rsync targets.
+ *   Relative aliases are always taken from the Drupal root.
+ *     '!root': The Drupal root; must not be specified as a relative path.
+ *     '!dump': Path to the file that "drush sql sync" should use to store sql dump file.
+ *     '!files': Path to 'files' directory.
+ *
+ * Remove the leading hash signs to enable.
+ */
+#$options['site-aliases']['stage'] = array(
+#    'db-url' => 'pgsql://username:password@dbhost.com:port/databasename',
+#    'uri' => 'stage.mydrupalsite.com',
+#    'remote-user' => 'publisher',
+#    'path-aliases' => array(
+#      '!root' => '/path/to/remote/drupal/root',
+#      '!dump' => '/path/to/live/sql_dump.sql',
+#      '!files' => 'sites/mydrupalsite.com/files',
+#      '!data' => '/my/arbitrary/path',    )
+#  );
+#$options['site-aliases']['dev'] = array(
+#    'db-url' => 'pgsql://username:password@localhost/databasename',
+#    'uri' => 'dev.mydrupalsite.com',
+#    'path-aliases' => array(
+#      '!dump' => '/path/to/dev/sql_dump.sql',
+#      '!data' => '/my/other/path',    )
+#  );
+
Index: commands/core/core.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v
retrieving revision 1.35
diff -u -p -r1.35 core.drush.inc
--- commands/core/core.drush.inc	1 Oct 2009 14:13:28 -0000	1.35
+++ commands/core/core.drush.inc	1 Oct 2009 22:16:25 -0000
@@ -89,13 +89,24 @@ function core_drush_command() {
     
   );
   $items['sync'] = array(
-    'description' => 'Rsync the Drupal tree to/from another server using ssh.',
+    'callback' => 'drush_core_sync',
+    'description' => 'Rsync the Drupal tree to/from another server using ssh.  Relative paths start from the Drupal root folder if a site alias is used; otherwise they start from the current working directory.',
     'arguments' => array(
-      'source' => 'See rsync documentation.',
-      'destination' => 'See rsync documentation.',
+      'source' => 'May be rsync path or site alias.  See rsync documentation and example.drushrc.php.',
+      'destination' => 'May be rsync path or site alias.  See rsync documentation and example.drushrc.php.',
+    ),
+    'options' => array(
+      '--mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz.  Default is -az.',
+      '--RSYNC-FLAG' => 'Most rsync flags passed to drush sync will be passed on to rsync.  See rsync documentation.',
+      '--all-paths' => 'If <source> and <destination> are site aliases, and neither <source> nor <destinatiln> specifies a path component, then rsync all path aliases that are defined in both the source and destination alias.',
+      '--exclude-conf' => 'Implies rsync --exclude="settings.php" --exclude="robots.txt" --exclude=".htaccess".  Default.',
+      '--include-conf' => 'Allow settings.php, robots.txt and .htaccess to be rsynced',
+      '--exclude-sites' => 'Exclude all directories in "sites/" except for "sites/all".',
     ),
     'examples' => array(
-      'sync dev:/var/www/files/ stage:/var/www/files/' => 'Rsync \'files\' dir from dev to stage',
+      'sync dev stage' => 'Rsync Drupal root from dev to stage (one of which must be local).',
+      'sync --all-paths --exclude-conf dev stage' => 'Rsync Drupal root and all other paths from dev to stage for every path alias defined in both dev and stage.',
+      'sync ./ stage:!files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on stage.',
     ),
   );
   $items['eval'] = array(
@@ -348,16 +359,26 @@ function drush_core_cache_clear() {
  *
  * @return void
  **/
-function drush_core_sync($source, $destination) {
-  // Local paths are relative to Drupal root
-  $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
+function _drush_core_sync($source, $destination, $parametric_options = array()) {
+  $remote_user = '';
+  $password = '';
   
-  if (!strstr($source, ':')) {
-    $source = $drupal_root. "/$source";
-  }
-  if (!strstr($destination, ':')) {
-    $destination = $drupal_root . "/$destination";
+  // One of $souruce or $destination (at most) will be remote;
+  // get the site alias settings for the remote component.
+  $remote_alias_settings = _drush_get_alias_record_for_remote($source, $destination);
+  if (!empty($remote_alias_settings)) {
+    if (array_key_exists('remote-user', $remote_alias_settings)) {
+      $remote_user = $remote_alias_settings['remote-user'] . '@';
+    }
+    // Note: password option does not work with rsync; use key files
+    if (array_key_exists('remote-pass', $remote_alias_settings)) {
+      $password = $remote_alias_settings['remote-pass'];
+    }
   }
+  
+  // expand file paths
+  $source = _drush_core_expand_path($source, $remote_user);
+  $destination = _drush_core_expand_path($destination, $remote_user);
 
   // Prompt for confirmation. This is destructive.
   if (!drush_get_context('DRUSH_SIMULATE')) {
@@ -366,15 +387,307 @@ function drush_core_sync($source, $desti
       return drush_set_error('CORE_SYNC_ABORT', 'Aborting.');
     }
   }
+  
+  return drush_core_call_rsync($source, $destination, $remote_user, $password, $parametric_options);
+}
 
-  $options = '-az';
-  $exec = "rsync -e ssh $options --exclude \"*.svn*\" $source $destination";
+function drush_core_call_rsync($source, $destination, $remote_user, $password, $parametric_options = array()) {
+  // merge the options specified by the caller with the options specified on the command line
+  $all_specified_options = array_merge(drush_get_merged_options(), $parametric_options);
+  $pwfile = '';
+  $options = ' --exclude="*.svn*"';
+  $mode = '-az';
+  // 'exclude-conf' is the default
+  if (!array_key_exists('include-conf', $all_specified_options)) {
+    $options .= ' --exclude="settings.php" --exclude="robots.txt" --exclude=".htaccess"';
+  }
+  if (array_key_exists('exclude-sites', $all_specified_options)) {
+    $options .= ' --include="sites/all" --exclude="sites/*"';
+  }
+  if (array_key_exists('mode', $all_specified_options)) {
+    $mode = "-" . $all_specified_options['mode'];
+  }
   if (drush_get_context('drush_log')) {
     // the drush_op() will be verbose about the command that gets executed.
-    $options .= 'v';
+    $mode .= 'v';
+    $options .= ' --stats --progress';
+  }
+  if (!empty($password)) {
+    $pwfile = drush_save_data_to_temp_file($password);
+    $options .= " --password-file $pwfile";
+  }
+  $rsync_available_options = array(
+    // unary options
+    'archive', // -a
+    'recursive', // -r
+    'relative', // -R
+    'backup', // -b
+    'update', // -u
+    'checksum', // -c
+    'dirs', // -d
+    'links', // -l
+    'copy-links', // -L
+    'copy-dirlinks', // -k
+    'keep-dirlinks', // -K
+    'hard-links', // -H
+    'perms', // -p
+    'executability', // -E
+    'acls', // -A
+    'xattrs', // -X
+    'owner', // -o
+    'group', // -g
+    'times', // -t
+    'omit-dir-times', // -O
+    'sparse', // -S
+    'dry-run', // -n
+    'whole-file', // -W
+    'one-file-system', // -x
+    'prune-empty-dirs', // -m
+    'ignore-times', // -I
+    'fuzzy', // -y
+    'cvs-exclude', // -C
+    'compress', // -Z
+    'protect-args', // -s
+    '8-bit-output', // -8
+    'human-readable', // -h
+    'itemize-changes', // -i
+    'copy-unsafe-links',
+    'safe-links',
+    'no-implied-dirs',
+    'inplace',
+    'append',
+    'append-verify',
+    'existing',
+    'remove-source-files',
+    'delete',
+    'delete-before',
+    'delete-during',
+    'delete-delay',
+    'delete-after',
+    'delete-excluded',
+    'ignore-errors',
+    'force',
+    'ignore-existing',
+    'partial',
+    'delay-updates',
+    'numeric-ids',
+    'size-only',
+    'blocking-io',
+    'stats',
+    'progress',
+    'list-only',
+    // options with values
+    'block-size',
+    'backup-dir',
+    'suffix',
+    'chmod', 
+    'rsync-path', 
+    'modify-window', 
+    'compare-dest', 
+    'copy-dest', 
+    'link-dest', 
+    'skip-compress', 
+    'filter', 
+    'exclude', 
+    'include', 
+    'address', 
+    'port', 
+    'sockopts', 
+    'out-format', 
+    'bwlimit', 
+    'iconv', 
+    'checksum-seed',
+    'max-delete',
+    'max-size',
+    'min-size',
+    'partial-dir',
+    'timeout',
+    'temp-dir',
+    'compress-level',
+    'out-format',
+    'protocol', 
+    );
+  foreach ($rsync_available_options as $test_option) {
+    if (array_key_exists($test_option, $all_specified_options)) {
+      $value = $all_specified_options[$test_option];
+      if ($value === TRUE) {
+        $options .= " --$test_option";
+      } 
+      elseif (isset($value)) {
+        $options .= " --$test_option=" . escapeshellarg($value);
+      }
+    }
   }
+  $exec = "rsync -e ssh $mode$options $source $destination";
+ 
+  $exec_result = drush_op('system', $exec) !== FALSE;
+  if (!empty($pwfile)) {
+    drush_op('unlink', $pwfile);
+  }
+  
+  return $exec_result;
+}
+
+// TODO:  This function, when called with --all-paths, will call _drush_core_sync
+// repeatedly, which will prompt for a y/n confirmation for every path (unless
+// 'drush -y' is used).  It would be better if it could prompt just once, but if
+// we did that, then the prompt should contain all of the paths that are going to
+// be copied.
+function drush_core_sync($source, $destination) {
+  $sync_all_paths = drush_get_option('all-paths');
+  if ((isset($sync_all_paths)) && ($result !== FALSE)) {
+    // If the source and destination paths are both aliases that contain
+    // no path component (that is, 'alias', not 'alias:/path'), then we will
+    // iterate over all of the path aliases of both, and sync all that are
+    // in common to both alias records.
+    $source_settings = drush_get_alias_record($source);
+    $destination_settings = drush_get_alias_record($destination);
+    if ((!empty($source_settings)) && (!empty($destination_settings)) && array_key_exists('path-aliases',$source_settings) && array_key_exists('path-aliases', $destination_settings)) {
+      $source_path_aliases = $source_settings['path-aliases'];
+      $destination_path_aliases = $destination_settings['path-aliases'];
+      $source_root = $source_path_aliases['!root'];
+      $destination_root = $destination_path_aliases['!root'];
+      // First call drush sync to rsync the source and destination paths as provided
+      $result = _drush_core_sync($source, $destination, array('exclude-sites' => TRUE));
+      // After sync-ing with 'exclude-sites', go back and sync the
+      // site directory for this installation.
+      if ($result !== FALSE) {
+        // TODO:  If the destination already exists, then it will probably have
+        // its own 'settings.php', and we almost certainly will want to call with
+        // --exclude-conf.  However, if the destination does not yet exist, then
+        // it would be confusing to include --exclude-conf.  Which option is better?
+        $result = _drush_core_sync($source . ':!root/sites/' . $source_settings['uri'] . '/', $destination . ':!root/sites/' . $destination_settings['uri'] . '/');  
+      }
+      // Iterate over all of the other path aliases
+      foreach ($source_path_aliases as $key => $value) {
+        // Skip this entry if its value is a path that is somewhere inside of the root,
+        // which we already rsync'ed at the top of this function.  (This also skips '!root').
+        if (($result !== FALSE) && (strncmp($source_root, $value, strlen($value)) != 0) && array_key_exists($key, $destination_path_aliases)) {
+          $result = _drush_core_sync($source . ':' . $key, $destination . ':' . $key);  
+        }
+      }
+    }
+  }
+  // If we didn't do a special-case rsync (alias-to-alias),
+  // then call sync with the original parameters here
+  if (!isset($result)) {
+    $result = _drush_core_sync($source, $destination);
+  }
+}
 
-  return drush_op('system', $exec) !== FALSE;
+// a path is "machine:/path" or "machine:path" or "/path" or "path"
+function _drush_core_expand_path($path, $remote_user) {
+  $path_aliases = array();
+  // Parse site aliases if there is a colon in the path
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+    $path = substr($path, $colon_pos + 1);
+    $site_alias_settings = drush_get_alias_record($alias);
+    $machine = $alias;
+  }
+  else {
+    // if the path is a site alias or a local site...
+    $site_alias_settings = drush_get_alias_record($path);
+    if (!empty($site_alias_settings) || _drush_core_is_local_machine($path)) {
+      $alias = $path;
+      $path = '';
+    }
+  }
+  
+  if (!empty($site_alias_settings)) {
+    if (array_key_exists('path-aliases', $site_alias_settings)) {
+      $path_aliases = $site_alias_settings['path-aliases'];
+    }
+    // Use 'remote-host' from settings if available; otherwise site is local 
+    if (array_key_exists('remote-host', $site_alias_settings)) {
+      $machine = $site_alias_settings['remote-host'];
+    }
+    else {
+      $machine = '';
+    }
+  }
+  
+  // Strip the machine portion of the path if the
+  // alias points to the local machine.
+  if (_drush_core_is_local_machine($machine)) {
+    $machine = '';
+  }
+  else {
+    $machine = "$remote_user$machine:";
+  }
+  
+  // If path aliases does not contain an alias for '!root',
+  // then assume that the remote Drupal root is the same
+  // as the local Drupal root.
+  if (!array_key_exists('!root', $path_aliases)) {
+    $path_aliases['!root'] = drush_get_context('DRUSH_DRUPAL_ROOT');
+  }
+  foreach ($path_aliases as $key => $value) {
+    // Expand all relative path aliases to be based off of the Drupal root
+    if (($value[0] != '/') && ($key != 'root')) {
+      $path_aliases[$key] = $path_aliases['!root'] . '/' . $value;
+    }
+    // Rsync is very particular about the meaning of paths that
+    // end with a '/' compared to those that do not.  In order to
+    // copy from !path to !path without creating an extra spurrious
+    // directory, the path alias must end with a '/'.  Therefore
+    // we will add a slash to the end of any alias that does not
+    // already have one.
+    if ($value[strlen($value)-1] != '/') {
+      $path_aliases[$key] = $path_aliases[$key] . '/';
+    }
+  }
+  
+  // Fill in path aliases in the path.
+  $path = dt($path, $path_aliases);
+  
+  // If the path is empty or if it does not exist, then 
+  // make it relative to the Drupal root.
+  if (empty($path) || ($path[0] != '/')) {
+      $path = $path_aliases['!root'] . $path;
+  }
+  return "$machine$path";
+}
+
+function _drush_core_is_local_machine($alias) {
+  // To do:  the directory in 'sites' might contain
+  // a component for the port or local path, so it
+  // would be better to do a 'contains' test on all
+  // files in 'sites' to determine if 'alias' is local.
+  // This might give a false positive, though: a remote
+  // 'mysite.com' would look local if there was a local
+  // 'dev.mysite.com'.
+  return ($alias == 'localhost') || ($alias == '127.0.0.1') || (file_exists(drush_get_context('DRUSH_DRUPAL_ROOT') . "/sites/$alias"));
+}
+
+function _drush_get_alias_record_if_remote($path) {
+  $site_alias_settings = array();
+  $colon_pos = strpos($path, ':');
+  if ($colon_pos != FALSE) {
+    $alias = substr($path, 0, $colon_pos);
+  }
+  else {
+    $alias = $path;
+  }
+  if (!_drush_core_is_local_machine($alias)) {
+    $site_alias_settings = drush_get_alias_record($alias);
+  }
+  return $site_alias_settings;
+}
+
+function _drush_get_alias_record_for_remote($source, $destination) {
+  $source_settings = _drush_get_alias_record_if_remote($source);
+  $destination_settings = _drush_get_alias_record_if_remote($destination);
+  /*if ((!empty($source_settings)) && (!empty($destination_settings))) {
+    return FALSE;
+  }
+  else*/if (!empty($source_settings)) {
+    return $source_settings;
+  }
+  else {
+    return $destination_settings;
+  }
 }
 
 /**
Index: commands/sql/sql.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/sql/sql.drush.inc,v
retrieving revision 1.21
diff -u -p -r1.21 sql.drush.inc
--- commands/sql/sql.drush.inc	1 Oct 2009 14:13:29 -0000	1.21
+++ commands/sql/sql.drush.inc	1 Oct 2009 22:16:26 -0000
@@ -22,6 +22,8 @@ function sql_drush_help($section) {
       return dt("Usage: drush [options] sql query <query>...\n<query> is a SQL statement, which can alternatively be passed via STDIN. Any additional arguments are passed to the mysql command directly.");
     case 'drush:sql load':
       return dt("Usage: drush [options] sql load <source_dir> <target_dir>.\n <source_dir> and <target_dir> are names of directories under \'sites\'. These determine from where and to where you want your database copied. Optional: specify \'common\' for --skip if you wish to omit disposable tables like cache*, search*, etc. Your skip lists are specified in your drushrc.php file. Any additional arguments are passed to the mysqldump command directly.");
+    case 'drush:sql sync':
+      return dt("Usage: drush [options] sql sync <source_alias> <target_alias>.\n <source_alias> and <target_alias> are site aliases, or names of directories under \'sites\'. These determine from where and to where you want your database copied.");
   }
 }
 
@@ -91,6 +93,37 @@ function sql_drush_command() {
       '--destination_database' => 'The DESTINATION DB connection key if using multiple DB connections',
     ),
   );
+  $items['sql sync'] = array(
+    'callback' => 'drush_sql_sync',
+    'description' => 'Copy source database to target database using rsync.',
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
+    'examples' => array(
+      'drush sql sync prod dev' => 'Copy the DB defined in sites/prod to the DB in sites/dev.',
+    ),
+    'arguments' => array(
+      'from' => 'Name of subdirectory within /sites or a site alias.',
+      'to' => 'Name of subdirectory within /sites or a site alias.',
+    ),
+    'options' => array(
+      '--skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
+      '--structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
+      '--cache' => 'Skip dump if result file exists and is less than "cache" hours old. Optional; default is 24 hours.',
+      '--no-cache' => 'Do not cache the sql dump file.',
+      '--no-dump' => 'Do not dump the sql database; always use an existing dump file.',
+      '--source-db-url' => 'Database specification for source system to dump from.',
+      '--source-remote-port' => 'Override sql database port number in source-db-url. Optional.',
+      '--source-remote-host' => 'Remote machine to run sql dump file on. Optional; default is local machine.',
+      '--source-dump' => 'Path to dump file. Optional; default is to create a temporary file.',
+      '--target-db-url' => '',
+      '--target-remote-port' => '',
+      '--target-remote-host' => '',
+      '--target-dump' => '',
+      '--create-db' => 'Create a new database before importing the database dump on the target machine.',
+      '--db-su' => 'Remote account to use when creating a new database. Optional.', 
+      '--db-repair-script' => 'Path to script to execute to repair the source database dump prior to import at the destinarion.  Repair is necessary if the source and destination have different URIs or different Drupal root paths.  The following replacements are done to the repair script arguments: !input, !output, !source-db, !target-db, !source-uri, !target-uri, !source-root, !target-root.  The script receives input on stdin, unless !input is used, and stdout is written to the target file unless !output is used.',
+      '--simple-repair' => 'Provides a simple repair script to fix URI references in the database.  Simple repair is sufficient if the source and destination have the same Drupal root path, and if modules that use serialized output with embedded paths to the sites directory are avoided (e.g. the color module).  Simple repair is more likely to be effective if source and destination systems have the same path to the "files" directory (e.g. color module works).',
+    ),
+  );
   if (drush_drupal_major_version() >=7) {
     $items['sql load']['options'] += array(
       '--source_target' => 'The name of a target within the SOURCE database.',
@@ -111,24 +144,39 @@ function sql_drush_command() {
  * Command callback. Displays the Drupal site's database connection string.
  */
 function drush_sql_conf() {
-  drush_print_r(_drush_sql_get_db_spec());
+  $all = drush_get_option('all');
+  if (isset($all)) {
+    $db_spec = $GLOBALS['databases'];
+    if (!isset($db_spec)) {
+      $db_spec = array('default' => array('default' => _drush_sql_get_db_spec()));
+    }
+  }
+  if (!isset($db_spec)) {
+    $db_spec = _drush_sql_get_db_spec();
+  }
+  drush_backend_set_result($db_spec);
+  drush_print_r($db_spec);
 }
 
 /**
  * Command callback. Emits a connect string for mysql or pgsql.
  */
-function drush_sql_connect() {
-  switch (_drush_sql_get_scheme()) {
+function _drush_sql_connect($db_spec = NULL) {
+  switch (_drush_sql_get_scheme($db_spec)) {
     case 'mysql':
       $command = 'mysql ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
-      $command .= _drush_sql_get_credentials();
+      $command .= _drush_sql_get_credentials($db_spec);
       break;
     case 'pgsql':
-      $command = 'psql -d ';
-      $command .= _drush_sql_get_credentials();
+      $command = 'psql';
+      $command .= _drush_sql_get_credentials($db_spec);
       break;
   }
-  drush_print($command);
+  return $command;
+}
+
+function drush_sql_connect() {
+  drush_print(_drush_sql_connect());
 }
 
 /**
@@ -150,75 +198,93 @@ function drush_sql_dump_execute() {
  *   A mysqldump statement that is ready for executing.
  */
 function drush_sql_dump($db_spec = NULL) {
-  $skip_tables = $structure_tables = $ignores = array();
-
+  $ignores = array();
+  
   // Skip large core tables if instructed.  Also used by 'sql load' command.
-  $key = drush_get_option('skip-tables-key');
-  $all_skip_tables = drush_get_option('skip-tables', array());
-  if (array_key_exists($key, $all_skip_tables)) {
-    $skip_tables = $all_skip_tables[$key];
-  }
-
+  $skip_tables = _drush_sql_get_table_list('skip-tables');
   // Skip any structure-tables as well.
-  $key = drush_get_option('structure-tables-key');
-  $all_structure_tables = drush_get_option('structure-tables', array());
-  if (array_key_exists($key, $all_structure_tables)) {
-    $structure_tables = $all_structure_tables[$key];
-    $skip_tables += $structure_tables;
-  }
+  $structure_tables = _drush_sql_get_table_list('structure-tables');
+  $skip_tables += $structure_tables;  
+  // Dump only the specified tables.  Takes precidence over skip-tables and structure-tables.
+  $tables = _drush_sql_get_table_list('tables');
+  $clean = drush_get_option('clean');
+  $data_only = drush_get_option('data-only');
 
-  switch (_drush_sql_get_scheme()) {
+  if (is_null($db_spec)) {
+    $db_spec = _drush_sql_get_db_spec();
+  }
+  $database = $db_spec['database'];
+  
+  switch (_drush_sql_get_scheme($db_spec)) {
     case 'mysql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-      $database = $db_spec['database'];
-
       $exec = 'mysqldump' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
       if ($file = drush_get_option('result-file')) {
         $exec .= ' --result-file '. $file;
       }
       $extra = ' --single-transaction --opt -Q' . _drush_sql_get_credentials($db_spec);
+      if (isset($data_only)) {
+        $extra .= ' --no-create-info';
+      }
       $exec .= $extra;
+      
+      if (!empty($tables)) {
+        $exec .= ' ' . implode(' ', $tables);
+      }
+      else {
+        // Append the ignore-table options.
+        foreach ($skip_tables as $table) {
+          $ignores[] = "--ignore-table=$database.$table";
+        }
+        $exec .= ' '. implode(' ', $ignores);
 
-      // Append the ignore-table options.
-      foreach ($skip_tables as $table) {
-        $ignores[] = "--ignore-table=$database.$table";
-      }
-      $exec .= ' '. implode(' ', $ignores);
-
-      // Run mysqldump again and append output if we need some structure only tables.
-      if (!empty($structure_tables)) {
-        $exec .= "; mysqldump --no-data $extra " . implode(' ', $structure_tables);
-        if ($file) {
-          $exec .= " >> $file";
+        // Run mysqldump again and append output if we need some structure only tables.
+        if (!empty($structure_tables)) {
+          $exec .= "; mysqldump --no-data $extra " . implode(' ', $structure_tables);
+          if ($file) {
+            $exec .= " >> $file";
+          }
         }
       }
       break;
     case 'pgsql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
-      $exec = 'pg_dump --clean ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
+      $create_db = drush_get_option('create-db');
+      $exec = 'pg_dump ' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
       if ($file = drush_get_option('result-file')) {
         $exec .= ' --file '. $file;
       }
-      $extra = ' ' . _drush_sql_get_credentials($db_spec);
+      // Unlike psql, pg_dump does not take a '-d' flag before the database name.
+      // We'll put the database name in 'by hand' and then clear the name from
+      // the record to prevent _drush_sql_get_credentials from inserting a -d term
+      // that pg_dump does not understand.
+      $extra = ' ' . $db_spec['database'];
+      $db_spec['database'] = null;
+      $extra .= _drush_sql_get_credentials($db_spec);
+      if (isset($data_only)) {
+        $extra .= ' --data-only';
+      }
       $exec .= $extra;
-      foreach ($skip_tables as $table) {
-        $ignores[] = "--exclude-table=$table";
+      $exec .= (!isset($create_db) && !isset($data_only) ? ' --clean' : '');
+      
+      if (!empty($tables)) {
+        foreach ($tables as $table) {
+          $exec .= " --table=$table";
+        }
       }
-      $exec .= ' '. implode(' ', $ignores);
-      // Run pg_dump again and append output if we need some structure only tables.
-      if (!empty($structure_tables)) {
-        $schemaonlies = array();
-	foreach ($structure_tables as $table) {
-          $schemaonlies[] = "--table=$table";
+      else {
+        foreach ($skip_tables as $table) {
+          $ignores[] = "--exclude-table=$table";
         }
-        $exec .= "; pg_dump --schema-only " . implode(' ', $schemaonlies) . $extra;
-        if ($file) {
-          $exec .= " >> $file";
+        $exec .= ' '. implode(' ', $ignores);
+        // Run pg_dump again and append output if we need some structure only tables.
+        if (!empty($structure_tables)) {
+          $schemaonlies = array();
+          foreach ($structure_tables as $table) {
+            $schemaonlies[] = "--table=$table";
+          }
+          $exec .= "; pg_dump --schema-only " . implode(' ', $schemaonlies) . $extra;
+          if ($file) {
+            $exec .= " >> $file";
+          }
         }
       }
       break;
@@ -227,26 +293,64 @@ function drush_sql_dump($db_spec = NULL)
   return $exec;
 }
 
+// Given an option name (skip-tables, structure-tables or tables),
+// look up the appropriate list of tables in the given catagory.
+function _drush_sql_get_table_list($option_name) {
+  $tables = array();
+  foreach(array('', 'source-', 'target-') as $prefix) {
+    $key_list = drush_get_option($prefix . $option_name . '-key');
+    foreach(explode(',', $key_list) as $key) {
+      $all_tables = drush_get_option($option_name, array());
+      if (array_key_exists($key, $all_tables)) {
+        $tables += $all_tables[$key];
+      }
+      if ($option_name != 'tables') {
+        $all_tables = drush_get_option('tables', array());
+        if (array_key_exists($key, $all_tables)) {
+          $tables += $all_tables[$key];
+        }
+      }
+    }
+    $table_list = drush_get_option($prefix . $option_name . '-list');
+    if (isset($table_list)) {
+      $tables += explode(',', $table_list);
+    }
+  }
+  
+  return $tables;
+}
+
 /**
  * Command callback. Executes the given SQL query on the Drupal database.
  */
 function drush_sql_query($query) {
+  return _drush_sql_query($query);
+}
+
+function _drush_sql_query($query, $db_spec = NULL) {
+  $scheme = _drush_sql_get_scheme($db_spec);
+  
+  // Convert mysql 'show tables;' query into something pgsql understands
+  if (($scheme == 'pgsql') && ($query == 'show tables;')) {
+    $query = "select tablename from pg_tables where schemaname='public';";
+  }
+  
   // Save $query to a tmp file. We will redirect it in.
   if ($file = drush_save_data_to_temp_file($query)) {
-    switch (_drush_sql_get_scheme()) {
+    switch ($scheme) {
       case 'mysql':
         $exec = 'mysql' . (drush_get_context('DRUSH_VERBOSE') ? ' -v' : '');
-        $exec .= _drush_sql_get_credentials();
-        $exec .= drush_get_option('extra');
+        $exec .= _drush_sql_get_credentials($db_spec);
+        $exec .= ' ' . drush_get_option('extra');
         $exec .= " < $file";
 
         break;
       case 'pgsql':
-        $exec = 'psql -d ';
-        $exec .= _drush_sql_get_credentials();
+        $exec = 'psql';
+        $exec .= _drush_sql_get_credentials($db_spec);
         $exec .= (drush_get_context('DRUSH_VERBOSE') ? '' : ' -q');
-        $exec .= ' --file -';
-        $exec .= " < $file";
+        $exec .= ' ' . (drush_get_option('extra') ? drush_get_option('extra') : "--no-align --field-separator=$'\t' --pset footer=off");
+        $exec .= " --file $file";
         break;
     }
     
@@ -256,6 +360,377 @@ function drush_sql_query($query) {
   }
 }
 
+function drush_sql_sync() {
+  $command = drush_get_command();
+  $source = $command['arguments'][0];
+  $destination = $command['arguments'][1];
+
+  $source_database = drush_get_option('source-database') ? drush_get_option('source-database') : 'default';
+  $source_target = drush_get_option('source-target');
+  $target_database = drush_get_option('target-database');
+  $target_target = drush_get_option('target-target');
+  
+  //
+  // If the destination was not explicitly set, but a particular
+  // target database was specified on the command line, then we
+  // will implicitly assume that the destination alias is the
+  // same as the source alias.
+  //
+  if (!isset($destination) && (isset($target_database) || (isset($target_target)))) {
+    $destination = $source;
+  }
+  
+  //
+  // If there is no destination specification at all, then we
+  // will copy the default database from the source onto 
+  // all of the slave databases from the source.
+  //
+  if (!isset($destination)) {
+    $source_settings = drush_get_alias_record($source, true);
+    
+    $slave_databases = $source_settings['databases'][$source_database];
+    unset($slave_databases['default']);
+    
+    //
+    // If there is only one slave database, then we'll call
+    // sql sync once as ussual to sync from the default to
+    // the single slave database (with warning prior to sync).
+    //
+    if (count($slave_databases) == 1) {
+      $slave_keys = array_keys($slave_databases);
+      _drush_sql_sync($source, $source, TRUE);
+    }
+    //
+    // If there are multiple slave databases, then we will print
+    // one confirmation warning here, then call sql sync once for
+    // every database, passing FALSE for $show_warning to prevent
+    // the warning message from showing up in the inner loop.
+    //
+    else {
+      if (!drush_get_context('DRUSH_SIMULATE')) {
+        $txt_source = $source_settings['databases'][$source_database]['default']['database'];
+        $sep = ' ';
+        foreach(array_reverse($slave_databases) as $slave_db_name => $slave_db_value) {
+          if (empty($txt_destination)) {
+            $txt_destination = 'and ' . $slave_db_value['database'];
+          }
+          else {
+            $txt_destination = $slave_db_value['database'] . $sep . $txt_destination;
+            $sep = ', ';
+          }
+        }
+        drush_print(dt("You will copy the data from the default database !source and destroy data in all of the slave databases, !target.", array('!source' => $txt_source, '!target' => $txt_destination)));
+        drush_print();
+        if (!drush_confirm(dt('Do you really want to continue?'))) {
+          drush_die('Aborting.');
+        }
+      }
+      //
+      // Set up a source dump file if one was not already
+      // specified so that we will only dump once rather than once
+      // per iteration.
+      //
+      $source_dump = drush_get_option('source-dump');
+      $source_is_tmp = FALSE;
+      if (!isset($source_dump)) {
+        $source_dump = tempnam(sys_get_temp_dir(), $source_database . '.sql.');
+        drush_op('unlink', $source_dump); // delete the temp file so that we will sql dump on the first iteration
+        drush_set_option('source-dump', $source_dump);
+        $source_is_tmp = TRUE;
+      }
+
+      foreach($slave_databases as $slave_db_name => $slave_db_value) {
+        drush_set_option('target-target', $slave_db_name);
+        _drush_sql_sync($source, $source, FALSE);
+      }
+
+      if ($source_is_tmp) {
+        drush_op('unlink', $source_dump);
+      }
+
+    }
+  }
+  //
+  // Default branch:  copy 'sync' with the specified source
+  // and destination.
+  //
+  else {
+    _drush_sql_sync($source, $destination, TRUE); 
+  }
+}
+
+function _drush_sql_sync($source, $destination, $show_warning = TRUE) { 
+  $source_settings = drush_get_alias_record($source, true);
+  $destination_settings = drush_get_alias_record($destination, true);
+
+  // Expand the source and destination specifications into options.
+  // The options from the 'source-*' and 'target-*' aliases are set
+  // in a drush context that has a lower priority than the command-line
+  // options; this allows command-line options to override the default
+  // values specified in a site alias.
+  drush_expand_site_alias_record($source_settings, 'source-');
+  drush_expand_site_alias_record($destination_settings, 'target-');
+
+  // Get the options for the source and target databases
+  $source_db_url = _drush_sql_get_spec_from_options('source-');
+  $source_remote_host = drush_get_option('source-remote-host');
+  $source_remote_port = drush_get_option('source-remote-port');
+  $source_rsync_options = array();
+  
+  $target_db_url = _drush_sql_get_spec_from_options('target-');
+  $target_remote_host = drush_get_option('target-remote-host');
+  $target_remote_port = drush_get_option('target-remote-port');
+  $target_rsync_options = array();
+  
+  // Prompt for confirmation. This is destructive.
+  if (!drush_get_context('DRUSH_SIMULATE') && $show_warning) {
+    $txt_source = (isset($source_remote_host) ? $source_remote_host . '/' : '') . $source_db_url['database'];
+    $txt_destination = (isset($target_remote_host) ? $target_remote_host . '/' : '') . $target_db_url['database'];
+    drush_print(dt("You will destroy data from !target and replace with data from !source.", array('!source' => $txt_source, '!target' => $txt_destination)));
+    drush_print();
+    // TODO: actually make the backup if desired.
+    drush_print(dt("You might want to make a backup first, using sql_dump command.\n"));
+    if (!drush_confirm(dt('Do you really want to continue?'))) {
+      drush_die('Aborting.');
+    }
+  }
+
+  // Set up the result file and the remote file.
+  // If the result file is not set, then create a temporary file.
+  // If the remote file is not set, use the same name for the remote
+  // and local files and hope for the best.
+  $source_dump = drush_get_option('source-dump');
+  $target_dump = drush_get_option('target-dump');
+  $source_is_tmp = FALSE;
+  $target_is_tmp = FALSE;
+  $local_is_tmp = FALSE;
+  if (!isset($source_remote_host) && !isset($target_remote_host)) {
+    if (isset($source_dump)) {
+      $target_dump = $source_dump;
+    }
+    else {
+      if (!isset($target_dump)) {
+        $target_dump = tempnam(sys_get_temp_dir(), $target_db_url['database'] . '.sql.');
+        $target_is_tmp = TRUE;
+      }
+      $source_dump = $target_dump;
+    }
+  }
+  if (!isset($target_dump)) {
+    $target_dump = tempnam(sys_get_temp_dir(), $target_db_url['database'] . '.sql.');
+    $target_is_tmp = TRUE;
+  }
+  if (!isset($source_dump)) {
+    $source_dump = tempnam(sys_get_temp_dir(), $source_db_url['database'] . '.sql.');
+    $source_is_tmp = TRUE;
+    $source_rsync_options['remove-source-files'] = TRUE;
+  }
+  
+  if (isset($source_remote_host) && isset($target_remote_host)) {
+    $local_file = tempnam(sys_get_temp_dir(), $source_db_url['database'] . ($source_db_url['database'] == $target_db_url['database'] ? '' : '-to-' . $target_db_url['database']) . '.sql.');
+    $local_is_tmp = TRUE;
+  }
+  elseif (!isset($source_remote_host)) {
+    $local_file = $source_dump;
+  }
+  elseif (!isset($target_remote_host)) {
+    $local_file = $target_dump;
+  }
+
+  // If source is remote, then use ssh to dump the database and then rsync to local machine
+  // If source is local, call drush_sql_dump to dump the database to local machine
+  // In either case, the '--no-dump' option will cause the sql dump step to be skipped, and
+  // we will import from the existing local file (first using rsync to fetch it if it does not exist)
+  //  
+  // No dump affects both local and remote sql dumps; it prevents drush sql sync
+  // from calling sql dump when the local cache file is newer than the cache threshhold
+  // No sync affects the remote sql dump; it will prevent drush sql sync from
+  // rsyncing the local sql dump file with the remote sql dump file.
+  $no_sync = drush_get_option(array('no-sync', 'source-no-sync'));
+  $no_dump = drush_get_option(array('no-dump', 'source-no-dump'));
+  $no_cache = drush_get_option(array('no-cache', 'source-no-cache'));
+  if (!isset($no_cache)) {
+    $cache = drush_get_option(array('cache', 'source-cache'));
+    if (!isset($cache)) {
+      $cache = 24; // default cache is 24 hours if nothing else is specified.
+    }
+  }
+  // If the 'cache' option is set, then we will set the no-dump option iff the
+  // target file exists and its modification date is less than "cache" hours.
+  if (isset($cache)) {
+    if (file_exists($local_file) && (filesize($local_file) > 0)) {
+      if ((filemtime($local_file) - time()) < ($cache * 60 * 60)) {
+        drush_log(dt('modification time is less than !cache hours old.', array('!cache' => $cache)));
+        $no_dump = TRUE;
+        $no_sync = TRUE;
+      }
+      else {
+        drush_log(dt('cache file exists but is greater than !cache hours old.', array('!cache' => $cache)));
+      }
+    }
+    else {
+      drush_log('cache file does not exist');
+    }
+  }
+  if (isset($source_remote_host)) {
+    if (isset($source_remote_port)) {
+      $source_db_url['port'] = $source_remote_port;
+    }
+    $source_remote_user = drush_get_option('source-remote-user'); 
+    if (isset($source_remote_user)) {
+      $source_at ='@';
+      $source_remote_pass = drush_get_option('source-remote-pass') ? ':' . drush_get_option('source-remote-pass') : '';
+    }
+    
+    if (!isset($no_dump)) {
+      $source_intermediate = $source_dump;
+      $mv_intermediate = '';
+      // If we are doing a remote dump and the source is not a temporary file,
+      // then first dump to a temporary file and move it to the specified file after 
+      // the dump is complete.  This will reduce contention during simultaneous dumps 
+      // from different users sharing the same dump file.
+      if (!isset($source_is_tmp)) {
+        $source_intermediate = $source_dump . '-' . date("U");
+        $mv_intermediate = '; mv -f ' . $source_intermediate . ' ' . $source_dump;
+      }
+      drush_set_option('result-file', $source_intermediate);
+      $dump_exec = drush_sql_dump($source_db_url) . $mv_intermediate;
+      if (isset($cache) && !isset($source_is_tmp)) {
+        // Inject some bash commands to remotely test the modification date of the target file
+        // if the cache option is set.
+        $dump_exec = 'if [ ! -s ' . $source_dump . '] || [ $((`date "+%s"`-`stat --format="%Y" ' . $source_dump . '`)) -gt ' . ($cache * 60 *  60) . ' ] ; then ' . $dump_exec . '; fi';
+      }
+      $dump_exec = 'ssh ' . $source_remote_user . $source_at . $source_remote_host . ' ' . escapeshellarg($dump_exec);
+    }
+  }
+  else {
+    if (!isset($no_dump)) {
+      drush_set_option('result-file', $local_file);
+      $dump_exec = drush_sql_dump($source_db_url);
+    }
+    $no_sync = TRUE;
+  }
+  
+  // Call sql dump, either on the local machine or remotely via ssh, as appropriate.
+  if (!empty($dump_exec)) {
+    drush_op('system', $dump_exec);
+    // TODO: IF FAILURE THEN ABORT
+  }
+
+  // If the sql dump was remote, then rsync the file over to the local machine. 
+  if (!isset($no_sync)) {
+    // If the source file is a temporary file, then we will have rsync
+    // delete it for us (remove-source-files option set above).
+    drush_core_call_rsync($source_remote_host . ':' . $source_dump, $local_file, $source_remote_user, $source_remote_pass, $source_rsync_options);
+  }
+  
+  // After the database export, we will run the database repair command
+  // if one was specified.
+  $database_repair_script = drush_get_option('db-repair-script');
+  if ((!isset($database_repair_script)) && (drush_get_option('simple-repair')) && ($source_settings['uri'] != $destination_settings['uri'])) {
+    $database_repair_script = 'sed -e \'s|sites/!source-uri|sites/!target-uri|g\'';
+  }
+  if (isset($database_repair_script)) {
+    $repair_input_file = $local_file;
+    $local_file = tempnam(sys_get_temp_dir(), $source_db_url['database'] . '-repaired.sql.');
+    $repair_is_tmp = $local_is_tmp;
+    $local_is_tmp = TRUE;
+    if ($repair_input_file == $target_dump) {
+      $target_dump = $local_file;
+      $local_is_tmp = !$target_is_tmp;
+    }
+    
+    $script_replacement_array = array(
+      '!input' => $repair_input_file,
+      '!output' => $local_file,
+      '!source-db' => $source_db_url['database'],
+      '!target-db' => $target_db_url['database'],
+      '!source-uri' => $source_settings['uri'], 
+      '!target-uri' => $destination_settings['uri'],
+      '!source-root' => $source_root, 
+      '!target-root' => $destination_root,
+    );
+    
+    if (strstr($database_repair_script, '!input') === FALSE) {
+      $input_from_stdin = 'cat ' . $repair_input_file . ' | ';
+    }
+    if (strstr($database_repair_script, '!output') === FALSE) {
+      $redirect_to_output_file = ' > ' . $local_file;
+    }
+    
+    $repair_exec = $input_from_stdin . dt($database_repair_script, $script_replacement_array) . $redirect_to_output_file;
+    drush_op('system', $repair_exec);
+    
+    if ($repair_is_tmp) {
+      drush_op('unlink', $repair_input_file);
+    }
+  }
+  
+  // Prior to database import, we will generate a "create database" command
+  // if the '--create-db' option was specified.  Note that typically the
+  // web server user will not have permissions to create a database; to specify
+  // a different user to use with the create db command, the '--db-su' option
+  // may be used.
+  // Under postgres, "alter role username with createdb;" will give create database 
+  // permissions to the specified user if said user was not created with this right.
+  $pre_import_commands = '';
+  $create_db = drush_get_option('create-db');
+  if (isset($create_db)) {
+    $create_db_target = $target_db_url;
+    $create_db_target['database'] = '';
+    $db_superuser = drush_get_option(array('db-su', 'target-db-su'));
+    if (isset($db_superuser)) {
+      $create_db_target['username'] = $db_superuser;
+    }
+    $db_su_connect = _drush_sql_connect($create_db_target);
+    switch (_drush_sql_get_scheme($target_db_url)) {
+      case 'mysql':
+        $pre_import_commands = 'mysqladmin --password=\'' . $target_db_url['password'] . '\' create ' . $target_db_url['database'] . '; echo "GRANT ALL PRIVILEGES ON ' . $target_db_url['database'] . '.* TO \'' . $target_db_url['username'] . '\'@\'localhost\' IDENTIFIED BY \'' . $target_db_url['password'] . '\';" | mysql --password=\'' . $target_db_url['password'] . '\'; ';
+        break;
+      case 'pgsql':
+        $pre_import_commands = 'echo "create database ' . $target_db_url['database'] . ';" | ' . $db_su_connect . '; ';
+        break;
+    }
+  }
+    
+  // If destination is remote, then use rsync to push the database, then use ssh to import the database
+  // If destination is local, then just import the database locally
+  if (isset($target_remote_host)) {
+    if (isset($target_remote_port)) {
+      $target_db_url['port'] = $target_remote_port;
+    }
+    $target_remote_user = drush_get_option('target-remote-user'); 
+    if (isset($target_remote_user)) {
+      $target_at ='@';
+      $target_remote_pass = drush_get_option('target-remote-pass') ? ':' . drush_get_option('target-remote-pass') : '';
+    }
+    
+    drush_core_call_rsync($local_file, $target_remote_host . ':' . $target_dump, $target_remote_user, $target_remote_pass);
+    
+    $connect_exec = $pre_import_commands . _drush_sql_connect($target_db_url) . ' < ' . $target_dump;
+    $import_exec = 'ssh ' . $target_remote_user . $target_at . $target_remote_host . ' ' . escapeshellarg($connect_exec);
+    // delete the remote target file if it is a temporary file
+    if ($target_is_tmp) {
+      $import_exec .= '; rm -f ' . escapeshellarg($target_dump);
+    }
+  }
+  else {
+    $import_exec = $pre_import_commands . _drush_sql_connect($target_db_url) . ' < ' . $local_file;
+  }
+  
+  drush_op('system', $import_exec);
+  // delete local temporary files
+  if (($source_is_tmp) && (!isset($source_remote_host))) {
+    drush_op('unlink', $source_dump);
+  }
+  if (($target_is_tmp) && (!isset($target_remote_host))) {
+    drush_op('unlink', $target_dump);
+  }
+  if ($local_is_tmp) {
+    drush_op('unlink', $local_file);
+  }
+}
+
 /**
  * Copy an entire database to another database. For example, migrate from production to dev
  * or dev to staging.
@@ -269,8 +744,8 @@ function drush_sql_query($query) {
  *    be replaced with the contents of `source`. *
  **/
 function drush_sql_load($source, $destination) {
-  $source = _drush_sql_get_spec_from_settings("./sites/$source/settings.php", 'source');
-  $destination = _drush_sql_get_spec_from_settings("./sites/$destination/settings.php", 'target');
+  $source = _drush_sql_get_spec_from_site_alias($source, 'source-');
+  $destination = _drush_sql_get_spec_from_site_alias($destination, 'target-');
   
   // Prompt for confirmation. This is destructive.
   if (!drush_get_context('DRUSH_SIMULATE')) {
@@ -292,13 +767,13 @@ function drush_sql_load($source, $destin
   // Get command to export from source.
   if ($retrieve = drush_sql_dump($source)) {
     
-    switch (_drush_sql_get_scheme()) {
+    switch (_drush_sql_get_scheme($source)) {
     case 'mysql':
       // Build import command for target. No verbose here as that emits too much SQL.
       $send = 'mysql' . _drush_sql_get_credentials($destination);
       break;
     case 'pgsql':
-      $send .= 'psql -d ' . _drush_sql_get_credentials($destination) . ' --file -';
+      $send .= 'psql' . _drush_sql_get_credentials($destination) . ' --file -';
       break;
     }
     
@@ -316,7 +791,7 @@ function drush_sql_cli() {
       $command .= _drush_sql_get_credentials();
       break;
     case 'pgsql':
-      $command = ' psql -d ';
+      $command = ' psql';
       $command .= _drush_sql_get_credentials();
       break;
   }
@@ -344,24 +819,7 @@ function _drush_sql_get_db_spec() {
       $url = $GLOBALS['db_url'];
       // TODO: array version not working?
       $url =  is_array($url) ? $url[$database] : $url;
-      $url = parse_url($url);
-      // Fill in defaults to prevent notices.
-      $url += array(
-        'driver' => NULL,
-        'user' => NULL,
-        'pass' => NULL,
-        'port' => NULL,
-        'database' => NULL,
-      );
-      $url = (object)$url;
-      return array(
-        'driver' => $url->scheme == 'mysqli' ? 'mysql' : $url->scheme,
-        'username' => urldecode($url->user),
-        'password' => urldecode($url->pass),
-        'port' => urldecode($url->port),
-        'host' => urldecode($url->host),
-        'database' => substr(urldecode($url->path), 1), // skip leading '/' character≈
-      );
+      return drush_convert_db_from_db_url($url);
       
     default:
       // We don't use DB API here `sql load` would have to messily addConnection.
@@ -369,6 +827,20 @@ function _drush_sql_get_db_spec() {
   }
 }
 
+
+// Build a $db_spec from a given site alias
+function _drush_sql_get_spec_from_site_alias($alias, $prefix) {
+  global $db_url;
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $site_alias_settings = $all_site_aliases[$alias];
+    $database = drush_get_option($prefix . 'database') ? drush_get_option($prefix . 'database') : 'default';
+    $target = drush_get_option($prefix . 'target') ? drush_get_option('target') : 'default';
+    return $site_alias_settings['databases'][$database][$target];
+  }
+  return _drush_sql_get_spec_from_settings("./sites/$alias/settings.php", $prefix);
+}
+
 // Build a $db_spec from a given settings.php.
 function _drush_sql_get_spec_from_settings($file, $prefix) {
   // Reset global databases to whatever is defined in $file
@@ -376,16 +848,37 @@ function _drush_sql_get_spec_from_settin
   require $file;
   
   // Translate the custom options for sql load into ones usually recognized (i.e. no prefix).
-  if ($database = drush_get_option($prefix . '_database')) {
+  if ($database = drush_get_option($prefix . 'database')) {
     drush_set_option('database', $database);
   }
-  if ($target = drush_get_option($prefix . '_target')) {
+  if ($target = drush_get_option($prefix . 'target')) {
+    drush_set_option('target', $target);
+  }
+  // Backwards-compatible for sql load:  accept 'source_database' in addition to the more standard 'source-database'
+  if ($database = drush_get_option(str_replace('-', '_', $prefix . 'database'))) {
+    drush_set_option('database', $value);
+  }
+  if ($target = drush_get_option(str_replace('-', '_', $prefix . 'target'))) {
     drush_set_option('target', $target);
   }
   
   return _drush_sql_get_db_spec();
 }
 
+function _drush_sql_get_spec_from_options($prefix) {
+  $databases = drush_get_option($prefix . 'databases');
+  if (isset($databases)) {
+    $database = drush_get_option($prefix . 'database') ? drush_get_option($prefix . 'database') : 'default';
+    $target = drush_get_option($prefix . 'target') ? drush_get_option($prefix . 'target') : 'default';
+    return $databases[$database][$target];    
+  }
+  $db_url = drush_get_option($prefix . 'db-url');
+  if (isset($db_url)) {
+    return drush_convert_db_from_db_url($db_url);
+  }
+  return _drush_sql_get_db_spec();
+}
+
 function _drush_sql_get_scheme($db_spec = NULL) {
   if (is_null($db_spec)) {
     $db_spec = _drush_sql_get_db_spec();
@@ -400,31 +893,27 @@ function _drush_sql_get_scheme($db_spec 
  * @return string
  */
 function _drush_sql_get_credentials($db_spec = NULL) {
-  switch (_drush_sql_get_scheme()) {
+  if (is_null($db_spec)) {
+    $db_spec = _drush_sql_get_db_spec();
+  }
+  
+  switch (_drush_sql_get_scheme($db_spec)) {
     case 'mysql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
       $cred = ' -h' . $db_spec['host'] .
          (empty($db_spec['port']) ? '' : ' -P' . $db_spec['port']) .
          ' -u' . $db_spec['username'] .
          (empty($db_spec['password']) ? '' : ' -p' . $db_spec['password']) . ' ' . $db_spec['database'];
       break;
   case 'pgsql':
-      if (is_null($db_spec)) {
-        $db_spec = _drush_sql_get_db_spec();
-      }
-
-      $cred = $db_spec['database'] . ' ' .
-         (empty($db_spec['host']) || ($db_spec['host'] == "localhost") ? '' : '-h ' . $db_spec['host']) . ' ' .
-         (empty($db_spec['port']) ? '' : '-p' . $db_spec['port']) . ' ';
+      $cred = (isset($db_spec['database']) ? ' -d ' . (empty($db_spec['database']) ? 'template1' :  $db_spec['database']) : '') .
+         (empty($db_spec['host'])  ? ' -h localhost ' : ' -h ' . $db_spec['host']) .
+         (empty($db_spec['port']) ? ' -p 5432 ' : ' -p ' . $db_spec['port']);
       // Adding '-U' will cause Postgres to prompt for a password, so disable this for now.
       // Use "sudo -u postgres drush sql ..." to access the database without a password,
       // presuming that "postgres" is the database superuser and you have
       // "local all all ident sameuser" in your Postgres pg_hba.conf file.
       // See: http://drupal.org/node/438828
-      // $cred .= '-U ' . $db_spec['username'] . ' ';
+      $cred .= ' -U ' . $db_spec['username'] . ' ';
       break;
   }
   return escapeshellcmd($cred);
Index: includes/backend.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/backend.inc,v
retrieving revision 1.16
diff -u -p -r1.16 backend.inc
--- includes/backend.inc	22 Sep 2009 00:22:42 -0000	1.16
+++ includes/backend.inc	1 Oct 2009 22:16:26 -0000
@@ -56,12 +56,27 @@
  */
 define('DRUSH_BACKEND_OUTPUT_DELIMITER', 'DRUSH_BACKEND_OUTPUT_START>>>%s<<<DRUSH_BACKEND_OUTPUT_END');
 
+function drush_backend_set_result($value) {
+  if (drush_get_context('DRUSH_BACKEND')) {
+    drush_set_context('BACKEND_RESULT', $value);
+  }
+}
+
+function drush_backend_get_result() {
+  return drush_get_context('BACKEND_RESULT');
+}
+
 function drush_backend_output() {
   $data = array();
 
   $data['output'] = ob_get_contents();
   ob_end_clean();
-
+  
+  $result_object = drush_backend_get_result();
+  if (isset($result_object)) {
+    $data['object'] = $result_object;
+  }
+  
   $error = drush_get_error();
   $data['error_status'] = ($error) ? $error : DRUSH_SUCCESS;
 
@@ -157,7 +172,7 @@ function _drush_backend_integrate($data)
  *   If it executed succesfully, it returns an associative array containing the command
  *   called, the output of the command, and the error code of the command.
  */
-function _drush_proc_open($cmd, $data = NULL) {
+function _drush_proc_open($cmd, $data = NULL, $context = NULL) {
   $descriptorspec = array(
      0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
      1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
@@ -173,7 +188,7 @@ function _drush_proc_open($cmd, $data = 
     $info = stream_get_meta_data($pipes[1]);
     stream_set_blocking($pipes[1], TRUE);
     stream_set_timeout($pipes[1], 1);
-    $stream = '';
+    $string = '';
     while (!feof($pipes[1]) && !$info['timed_out']) { 
       $string .= fgets($pipes[1], 4096);
       $info = stream_get_meta_data($pipes[1]);
@@ -184,7 +199,6 @@ function _drush_proc_open($cmd, $data = 
     $info = stream_get_meta_data($pipes[2]);
     stream_set_blocking($pipes[2], TRUE);
     stream_set_timeout($pipes[2], 1);
-    $stream = '';
     while (!feof($pipes[2]) && !$info['timed_out']) { 
       $string .= fgets($pipes[2], 4096);
       $info = stream_get_meta_data($pipes[2]);
@@ -233,8 +247,13 @@ function _drush_proc_open($cmd, $data = 
  *   If the command was completed, this will return an associative array containing the data from drush_backend_output().
  */
 function drush_backend_invoke($command, $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL) {
-  $cmd = _drush_backend_generate_command($command, $data, $method, $drush_path, $hostname, $username);
-  drush_log(dt('Running: !cmd', array('!cmd' => $cmd)), 'command');
+  $args = explode(" ", $command);
+  $command = array_shift($args);
+  return drush_backend_invoke_args($command, $args, $data, $method, $integrate, $drush_path, $hostname, $username);
+}
+
+function drush_backend_invoke_args($command, $args, $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL) {
+  $cmd = _drush_backend_generate_command($command, $args, $data, $method, $drush_path, $hostname, $username);
   return _drush_backend_invoke($cmd, $data, $integrate);
 }
 
@@ -260,6 +279,7 @@ function drush_backend_invoke($command, 
  *   If the command was completed, this will return an associative array containing the data from drush_backend_output().
  */
 function _drush_backend_invoke($cmd, $data = null, $integrate = TRUE) {
+  drush_log(dt('Running: !cmd', array('!cmd' => $cmd)), 'command');
   $proc = _drush_proc_open($cmd, $data);
 
   if (($proc['code'] == DRUSH_APPLICATION_ERROR) && $integrate) {
@@ -280,9 +300,11 @@ function _drush_backend_invoke($cmd, $da
 
 /**
  * Generate a command to execute.
-
+ *
  * @param command
  *    A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'.
+ * @param args
+ *    An array or arguments for the command.
  * @param data
  *    Optional. An array containing options to pass to the remote script.
  *    Array items with a numeric key are treated as optional arguments to the command.
@@ -308,25 +330,26 @@ function _drush_backend_invoke($cmd, $da
  * @return
  *   A text string representing a fully escaped command.
  */
-function _drush_backend_generate_command($command, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null) {
-  $drush_path = !is_null($drush_path) ? $drush_path : DRUSH_COMMAND; // Call own drush.php file.
+function _drush_backend_generate_command($command, $args, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null) {
+  $drush_path = !is_null($drush_path) ? $drush_path : (is_null($hostname) ? DRUSH_COMMAND : 'drush'); // Call own drush.php file on local machines, or 'drush' on remote machines.
   $data['root'] = ($data['root']) ? $data['root'] : drush_get_context('DRUSH_DRUPAL_ROOT');
   $data['uri'] = array_key_exists('uri', $data) ? $data['uri'] : drush_get_context('DRUSH_URI');
 
   $option_str = _drush_backend_argument_string($data, $method);
-  $args = explode(" ", $command);
   foreach ($data as $key => $arg) {
     if (is_numeric($key)) {
       $args[] = $arg;
       unset($data[$key]);
     }
   }
-  $command = '';
   foreach ($args as $arg) {
     $command .= ' ' . escapeshellarg($arg);
   }
+  $php='';
+  if (".php" == substr($drush_path, strlen($drush_path) - strlen(".php"))) {
+    $php  = drush_find_php() . ' ';
+  }
   // @TODO: Implement proper multi platform / multi server support.
-  $php  = drush_find_php();
   $cmd = $php . sprintf(escapeshellcmd(" %s %s %s --backend"), escapeshellcmd($drush_path), $option_str, $command);
 
   if (!is_null($hostname)) {
@@ -348,7 +371,9 @@ function _drush_backend_generate_command
  */
 function drush_backend_fork($command, $data, $drush_path = null, $hostname = null, $username = null) {
   $data['quiet'] = TRUE;
-  $cmd = "(" . _drush_backend_generate_command($command, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null';
+  $args = explode(" ", $command);
+  $command = array_shift($args);
+  $cmd = "(" . _drush_backend_generate_command($command, $args, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null';
   drush_log(dt("Forking : !cmd", array('!cmd' => $cmd)));
   system($cmd);
 }
Index: includes/context.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/context.inc,v
retrieving revision 1.10
diff -u -p -r1.10 context.inc
--- includes/context.inc	30 Sep 2009 02:32:09 -0000	1.10
+++ includes/context.inc	1 Oct 2009 22:16:26 -0000
@@ -269,7 +269,7 @@ function drush_get_option($option, $defa
   else {
     // We are not checking a specific context, so check them in a predefined order of precedence.
     static $contexts = array(
-      'process', 'options', 'stdin', 
+      'process', 'options', 'stdin', 'alias',
       'custom', 'site', 'drupal', 'user', 'system', 
       'drush', 'default');
 
@@ -294,7 +294,7 @@ function drush_get_option($option, $defa
  */
 function drush_get_merged_options() {
   static $contexts = array(
-      'process', 'options', 'stdin', 
+      'process', 'options', 'stdin', 'alias', 
       'custom', 'site', 'drupal', 'user', 'system',
       'drush', 'default');
   $cache = drush_get_context();
Index: includes/environment.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/environment.inc,v
retrieving revision 1.44
diff -u -p -r1.44 environment.inc
--- includes/environment.inc	1 Oct 2009 17:24:00 -0000	1.44
+++ includes/environment.inc	1 Oct 2009 22:16:26 -0000
@@ -399,6 +399,9 @@ function _drush_bootstrap_drush() {
   // Load a drushrc.php file in the ~/.drush directory.
   drush_load_config('user');
 
+  // If the first argument or the '-l' / '--uri' option is a site alias, then process it
+  _drush_process_site_alias();
+  
   $backend = drush_set_context('DRUSH_BACKEND', drush_get_option(array('b', 'backend')));
 
   if ($backend) {
@@ -436,6 +439,242 @@ function _drush_bootstrap_drush() {
   _drush_find_commandfiles(DRUSH_BOOTSTRAP_DRUSH);
 }
 
+function _drush_process_site_alias() {
+  $args = drush_get_arguments();
+  
+  // Test to see if the first arg is a site specification
+  if (drush_expand_site_specification($args[0])) {
+    array_shift($args);
+    // We only need to expand the site specification
+    // once, then we are done.  (Should we set a flag indicating
+    // that we already did this?  Otherwise 'drush alias1 alias2 command'
+    // would behave strangely.)
+    drush_set_arguments($args);
+    return TRUE;
+  }
+
+  return drush_expand_site_specification(drush_get_option(array('l', 'uri')));
+}
+
+/**
+ * Given an alias:
+ *
+ * If it is the name of a site alias, return the alias record from
+ * the site aliases array.
+ *
+ * If it is the name of a folder in the 'sites' folder, construct
+ * an alias record from values stored in settings.php.
+ *
+ * If it is a site specification, construct an alias record from the
+ * values in the specification.
+ * 
+ * Site specifications come in several forms:
+ *
+ * 1.) /path/to/drupal#sitename
+ * 2.) user@server/path/to/drupal#sitename
+ * 3.) user@server/path/to/drupal            (sitename == server)
+ * 4.) user@server#sitename                  (only if $option['r'] set in some drushrc file on server)
+ * 5.) #sitename                             (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)      
+ * 6.) sitename                              (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)      
+ *
+ * Note that in the case of the first four forms, it is also possible
+ * to add additional site variable to the specification using uri query
+ * syntax.  For example:
+ *
+ *      user@server/path/to/drupal?db-url=...#sitename
+ */
+function drush_get_alias_record($alias, $db_settings_needed = false) {
+  $alias_record = array();
+  
+  $all_site_aliases = drush_get_option('site-aliases', array());
+  if (array_key_exists($alias, $all_site_aliases)) {
+    $alias_record = $all_site_aliases[$alias];
+    // If the alias record does not have a defined 'databases' entry,
+    // then we'll need to look one up
+    if ($db_settings_needed && !isset($alias_record['db-url']) && !isset($alias_record['databases'])) {
+      // Check to see if we've cached the databases record from a previous invocation of this function
+      $alias_record['databases'] = drush_get_option('databases-' . $alias, NULL, 'backend-cache');
+      if (!isset($alias_record['databases'])) {
+        // If the alias record is remote, then we'll use backend_invoke to fetch the
+        // database settings from the remote machine
+        if (array_key_exists('remote-host', $alias_record)) {
+          $data = array('root' => $alias_record['path-aliases']['!root'], 'uri' => $alias_record['uri'], 'all' => TRUE);
+          $args = array();
+          $values = drush_backend_invoke_args("sql conf", $args, $data, 'GET', FALSE, $alias_record['drush'], $alias_record['remote-host'], $alias_record['remote-user']);
+          $alias_record['databases'] = $values['object'];
+        }
+        // If the alias record is for a local machine, then look up and
+        // use the database settings from the appropriate settings.php file.
+        else {
+          $alias_record = array_merge(_drush_build_alias_from_settings($alias), $alias_record);
+        }
+        // Cache our result so we don't need to do IPC
+        drush_set_option('databases-' . $alias, $alias_record['databases'], 'backend-cache');
+      }
+    }
+  }
+  else {
+    if (isset($alias)) {
+      // Cases 1.) - 4.):
+      // We will check for a site specification if the alias has at least
+      // two characters from the set '@', '/', '#'.
+      if ((strpos($alias, '@') === FALSE ? 0 : 1) + (strpos($alias, '/') === FALSE ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) {
+        if ((substr($alias,0,7) != 'http://') && (substr($alias,0,1) != '/')) {
+          // Add on a scheme so that "user:pass@server" will always parse correctly
+          $parsed = parse_url('http://' . $alias);
+        }
+        else {
+          $parsed = parse_url($alias);
+        }
+        foreach (array('user' => 'remote-user', 'pass' => 'remote-pass', 'host' => 'remote-host', 'fragment' => 'uri', 'path' => '!root') as $url_key => $option_key) {
+          if (array_key_exists($url_key, $parsed)) {
+            drush_set_alias_record_element($alias_record, $option_key, $parsed[$url_key]);
+          }
+        }
+        foreach (explode('&', $parsed['query']) as $query_arg) {
+          $query_components = explode('=', $query_arg);
+          drush_set_alias_record_element($alias_record, urldecode($query_components[0]), urldecode($query_components[1]));
+        }
+        
+        // Case 3.): If the URL contains a 'host' portion but no fragment, then set the uri to the host
+        // Note: We presume that 'server' is the best default for case 3; without this code, the default would
+        // be whatever is set in $options['l'] on the target machine's drushrc.php settings file.
+        if (array_key_exists('host', $parsed) && !array_key_exists('fragment', $parsed)) {
+          $alias_record['uri'] = $parsed['host'];
+        }
+      }
+      else {
+        // Case 5.) and 6.):
+        // If the alias is the name of a folder in the 'sites' directory, 
+        // then use it as a local site specification.
+        $alias_record = _drush_build_alias_from_settings($alias);
+      }
+    }
+
+  }
+  if (isset($alias_record['db-url']) && !isset($alias_record['databases'])) {
+    $alias_record['databases'] = array('default' => array('default' => drush_convert_db_from_db_url($alias_record['db-url'])));
+  }
+  if ($db_settings_needed && !isset($alias_record['databases'])) {
+    drush_print("oh oh, could not get database spec when it was required for " . $alias);
+    exit(1);
+  }
+  return $alias_record;
+}
+
+function _drush_build_alias_from_settings($alias) {
+  $alias_record = array();
+  // BUG WORKAROUND:  if we use 'drush_locate_root' we will set 'uri' after 
+  // drush_bootstrap_drush, which works.  If we use drush_get_context('DRUSH_DRUPAL_ROOT'),
+  // then we set 'uri' after drush_bootstrap_drupal_root, which seems like it should work
+  // but doesn't.
+  $drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
+  // drush_get_context('DRUSH_DRUPAL_ROOT');
+  if (substr($alias,0,1) == '#') {
+    $alias = substr($alias,1);
+  }
+  $site_settings_file = $drupal_root . '/sites/' . $alias . '/settings.php';
+  if (isset($drupal_root) && file_exists($site_settings_file)) {
+    $alias_record['uri'] = $alias;
+    global $databases, $db_url;
+    require $site_settings_file;
+    if (isset($db_url)) {
+      $alias_record['db-url'] = $db_url;
+    }
+    else {
+      $alias_record['databases'] = $databases;
+    }
+    $alias_record['path-aliases'] = array(
+      '!root' => $drupal_root,
+      /*'!files' => '/path/to/file/storage', */
+      /*'!dump' => '/path/to/sql/dumpfile', */ );
+  }
+  return $alias_record;
+}
+
+/**
+ * Convert from an old-style database URL to an array of database settings
+ * (Called from environment.inc; move this function to that file?)
+ */
+function drush_convert_db_from_db_url($db_url) {
+  $url = parse_url($db_url);
+  // Fill in defaults to prevent notices.
+  $url += array(
+    'driver' => NULL,
+    'user' => NULL,
+    'pass' => NULL,
+    'port' => NULL,
+    'database' => NULL,
+  );
+  $url = (object)$url;
+  return array(
+    'driver' => $url->scheme == 'mysqli' ? 'mysql' : $url->scheme,
+    'username' => urldecode($url->user),
+    'password' => urldecode($url->pass),
+    'port' => urldecode($url->port),
+    'host' => urldecode($url->host),
+    'database' => substr(urldecode($url->path), 1), // skip leading '/' character
+  );
+}
+
+function drush_set_alias_record_element(&$alias_record, $key, $value) {
+  if (substr($key,0,1) == '!') {
+    $alias_record['path-aliases'][$key] = $value;
+  }
+  elseif (!empty($key)) {
+    $alias_record[$key] = $value;
+  }
+}
+
+function drush_expand_site_specification($alias, $prefix = '') {
+  $site_alias_settings =  drush_get_alias_record($alias);
+  if (!empty($site_alias_settings)) {
+    drush_expand_site_alias_record($site_alias_settings, $prefix);  
+    return TRUE;
+  }
+  return FALSE;
+}
+
+function drush_expand_site_alias_record($site_alias_settings, $prefix) {
+  // Transfer all non-array options from the site alias to the drush options
+  // in the 'alias' context.
+  foreach ($site_alias_settings as $key => $value) {
+    if (!is_array($value) || ($key == "databases")) {
+      drush_set_option($prefix . $key, $value, 'alias');
+    }
+  }
+  // Transfer selected path aliases to the drush options.
+  if (array_key_exists('path-aliases', $site_alias_settings)) {
+    foreach (array('!root', '!drush', '!dump', '!include') as $key) {
+      if (array_key_exists($key, $site_alias_settings['path-aliases'])) {
+        drush_set_option($prefix . substr($key, 1), $site_alias_settings['path-aliases'][$key], 'alias');
+      }
+    }
+  }
+  // If there are prefix-specific options (e.g. 'source-options' or 'target-options'),
+  // then transfer those values as well.
+  drush_apply_special_alias_record_options($site_alias_settings, $prefix);
+}
+
+function drush_apply_special_alias_options($alias, $prefix) {
+  if ($prefix != '') {
+    $site_alias_settings =  drush_get_alias_record($alias);
+    if (!empty($site_alias_settings)) {
+      drush_apply_special_alias_record_options($site_alias_settings, $prefix);
+    }
+  }
+}
+
+function drush_apply_special_alias_record_options($site_alias_settings, $prefix) {
+  if ($prefix != '') {
+    if ((array_key_exists($prefix . 'options', $site_alias_settings))) {
+      foreach ($site_alias_settings[$prefix . 'options'] as $key => $value) {
+        drush_set_option($key, $value, 'alias');
+      }
+    }
+  }
+}
+
 /**
  * Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
  *
@@ -881,4 +1120,5 @@ function drush_get_themes() {
   else {
     return drush_set_error('DRUSH_CORE', dt('Drush was unable to load theme information.'));
   }
-}
\ No newline at end of file
+}
+
