? 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/core/rsync.inc
? commands/pm/handler
? commands/sql/sync.sql.inc
? includes/sitealias.inc
? includes/table.inc
Index: drush.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/drush.php,v
retrieving revision 1.73
diff -u -p -r1.73 drush.php
--- drush.php	26 Oct 2009 02:26:36 -0000	1.73
+++ drush.php	26 Oct 2009 22:01:21 -0000
@@ -28,6 +28,7 @@ require_once DRUSH_BASE_PATH . '/include
 require_once DRUSH_BASE_PATH . '/includes/drush.inc';
 require_once DRUSH_BASE_PATH . '/includes/backend.inc';
 require_once DRUSH_BASE_PATH . '/includes/context.inc';
+require_once DRUSH_BASE_PATH . '/includes/sitealias.inc';
 
 drush_set_context('argc', $GLOBALS['argc']);
 drush_set_context('argv', $GLOBALS['argv']);
@@ -70,6 +71,13 @@ 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.
+      if (drush_remote_command()) {
+        $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');
@@ -115,6 +123,65 @@ function drush_main() {
 }
 
 /**
+ * Process commands that are executed on a remote drush instance.
+ *
+ * @return
+ *   TRUE if the command was handled remotely.
+ */
+function drush_remote_command() {
+  // The command will be executed remotely if the --remote-host flag
+  // is set; note that if a site alias is provided on the command line,
+  // and the site alias references a remote server, then the --remote-host
+  // option will be set when the site alias is processed.
+  // @see _drush_process_site_alias
+  $remote_host = drush_get_option('remote-host');
+  if (isset($remote_host)) {         
+
+    $args = drush_get_arguments();
+    $command = array_shift($args);
+    $data = drush_get_context('options');
+    
+    // Most command options are forwarded on to the remote 
+    // server; however, we will clear certain flags such as
+    // -v, -d and -i from the 'options' context (defaults, and
+    // options passed in on the command line).
+    foreach (array('v', 'd', 'i') as $key) {
+      unset($data[$key]);
+    }
+    
+    // After we clear out the flags we do not want from the
+    // 'options' context, we will add in the 'root' and 'uri'
+    // options from any context.  Note that these will usually
+    // come from the 'alias' context, which has a higher precidence
+    // than the 'site' context (where 'r' and 'l' from settings.php
+    // are stored).
+    foreach (array('root', 'r', 'uri', 'l') as $key) {
+      $value = drush_get_option($key);
+      if (isset($value)) {
+        $data[$key] = drush_get_option($key);
+      }
+    }
+    
+    // Finally, we call backend invoke with the
+    // specified remote host, remote user and drush path.
+    $drush_path = drush_get_option('drush-script');
+    if (!isset($drush_path)) {
+      $drush_folder = drush_get_option('drush');
+      if (isset($drush)) {
+        $drush_path = $drush_folder . '/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'));
+
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
  * Shutdown function for use while Drupal is bootstrapping and to return any
  * registered errors.
  *
Index: example.drushrc.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/example.drushrc.php,v
retrieving revision 1.13
diff -u -p -r1.13 example.drushrc.php
--- example.drushrc.php	22 Oct 2009 01:57:51 -0000	1.13
+++ example.drushrc.php	26 Oct 2009 22:01:21 -0000
@@ -85,3 +85,70 @@ $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 --full site alias" command.
+ *
+ * The following settings are stored in the site alias record:
+ *
+ * - 'db-url': The Drupal 6 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.
+ * - 'databases': Like 'db-url', but contains the full Drupal 7 databases
+ *   record.  Drush will look up the 'databases' record if it is not specified.
+ * - '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.
+ * - '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.
+ *     '!drush': The path to the folder where drush is stored.  Optional;
+ *     defaults to the folder containing the running script.  Always be sure
+ *     to set '!drush' if the path to drush is different on the remote server.
+ *     '!drush-script': The path to the 'drush' script (used by backend invoke);
+ *     default is 'drush' on remote machines, or the full path to drush.php on
+ *     the local machine.
+ *     '!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-host' => 'mystagingserver.myisp.com',
+#    'remote-user' => 'publisher',
+#    'path-aliases' => array(
+#      '!root' => '/path/to/remote/drupal/root',
+#      '!drush' => '/drush/path/drush',
+#      '!dump' => '/path/to/live/sql_dump.sql',
+#      '!files' => 'sites/mydrupalsite.com/files',
+#      '!custom' => '/my/custom/path',    )
+#  );
+#$options['site-aliases']['dev'] = array(
+#    'uri' => 'dev.mydrupalsite.com',
+#    'path-aliases' => array(
+#      '!root' => '/path/to/drupal/root', )
+#  );
+
Index: commands/core/core.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v
retrieving revision 1.47
diff -u -p -r1.47 core.drush.inc
--- commands/core/core.drush.inc	26 Oct 2009 14:39:22 -0000	1.47
+++ commands/core/core.drush.inc	26 Oct 2009 22:01:21 -0000
@@ -111,15 +111,27 @@ function core_drush_command() {
       '--immediate' => 'Rebuild the index immediately, instead of waiting for cron.',
     ),
   );
-  $items['sync'] = array(
-    'description' => 'Rsync the Drupal tree to/from another server using ssh.',
+  $items['rsync'] = array(
+    '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.',
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
     '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 <destination> 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',
+      'rsync dev stage' => 'Rsync Drupal root from dev to stage (one of which must be local).',
+      'rsync --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.',
+      'rsync ./ stage:!files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on stage.',
     ),
+    'aliases' => array('sync'),
   );
   $items['eval'] = array(
     'description' => 'Evaluate arbitrary php code after bootstrapping Drupal.',
@@ -271,7 +283,7 @@ function core_drush_help($section) {
       return dt("Force the search index to be rebuilt.");
     case 'drush:updatedb':
       return dt("Run update.php just as a web browser would.");
-    case 'drush:sync':
+    case 'drush:rsync':
       return dt("Sync the entire drupal directory or a subdirectory to a <destination> using ssh. Excludes .svn directories. Useful for pushing copies of your tree to a staging server, or retrieving a files directory from a remote site. Local paths should be specified relative to Drupal root.");
     case 'drush:eval':
       return dt("Run arbitrary PHP code in the context of Drupal");
@@ -433,40 +445,6 @@ function drush_core_cache_clear() {
 }
 
 /**
- * Push files from or to the local Drupal install using SSH and RSync
- *
- * @return void
- **/
-function drush_core_sync($source, $destination) {
-  // Local paths are relative to Drupal root
-  $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
-  
-  if (!strstr($source, ':')) {
-    $source = $drupal_root. "/$source";
-  }
-  if (!strstr($destination, ':')) {
-    $destination = $drupal_root . "/$destination";
-  }
-
-  // Prompt for confirmation. This is destructive.
-  if (!drush_get_context('DRUSH_SIMULATE')) {
-    drush_print(dt("You will destroy data from !target and replace with data from !source", array('!source' => $source, '!target' => $destination)));
-    if (!drush_confirm(dt('Do you really want to continue?'))) {
-      return drush_set_error('CORE_SYNC_ABORT', 'Aborting.');
-    }
-  }
-
-  $options = '-az';
-  $exec = "rsync -e ssh $options --exclude \"*.svn*\" $source $destination";
-  if (drush_get_context('drush_log')) {
-    // the drush_op() will be verbose about the command that gets executed.
-    $options .= 'v';
-  }
-
-  return drush_op('system', $exec) !== FALSE;
-}
-
-/**
  * Displays the most recent watchdog log messages (default: 10 messages).
  */
 function drush_core_watchdog_show($type = NULL) {
Index: commands/sql/sql.drush.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/sql/sql.drush.inc,v
retrieving revision 1.23
diff -u -p -r1.23 sql.drush.inc
--- commands/sql/sql.drush.inc	15 Oct 2009 15:27:36 -0000	1.23
+++ commands/sql/sql.drush.inc	26 Oct 2009 22:01:21 -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,36 @@ function sql_drush_command() {
       '--destination_database' => 'The DESTINATION DB connection key if using multiple DB connections',
     ),
   );
+  $items['sql sync'] = array(
+    '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-prepare-script' => 'Path to script to execute to repair the source database dump prior to import at the destination.  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 +143,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 +197,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 precedence 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;
-
-      // Append the ignore-table options.
-      foreach ($skip_tables as $table) {
-        $ignores[] = "--ignore-table=$database.$table";
+      
+      if (!empty($tables)) {
+        $exec .= ' ' . implode(' ', $tables);
       }
-      $exec .= ' '. implode(' ', $ignores);
+      else {
+        // 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 +292,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 category.
+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;
     }
     
@@ -269,8 +372,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 +395,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 +419,7 @@ function drush_sql_cli() {
       $command .= _drush_sql_get_credentials();
       break;
     case 'pgsql':
-      $command = ' psql -d ';
+      $command = ' psql';
       $command .= _drush_sql_get_credentials();
       break;
   }
@@ -335,8 +438,8 @@ function drush_sql_cli() {
  *   An info array describing a database target.
  */
 function _drush_sql_get_db_spec() {
-  $database = drush_get_option('database') ? drush_get_option('database') : 'default';
-  $target = drush_get_option('target') ? drush_get_option('target') : 'default';
+  $database = drush_get_option('database', 'default');
+  $target = drush_get_option('target', 'default');
   
   switch (drush_drupal_major_version()) {
     case 5:
@@ -344,24 +447,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 +455,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 = drush_sitealias_get_record($alias, TRUE);
+    $database = drush_get_option($prefix . 'database', 'default');
+    $target = drush_get_option($prefix . '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 +476,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', 'default');
+    $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();
@@ -401,7 +522,7 @@ function _drush_sql_get_scheme($db_spec 
  */
 function _drush_sql_get_credentials($db_spec = NULL) {
   if (is_null($db_spec)) {
-    $db_spec = _drush_sql_get_db_spec($db_spec);
+    $db_spec = _drush_sql_get_db_spec();
   }
   
   switch (_drush_sql_get_scheme($db_spec)) {
@@ -412,15 +533,15 @@ function _drush_sql_get_credentials($db_
          (empty($db_spec['password']) ? '' : ' -p' . $db_spec['password']) . ' ' . $db_spec['database'];
       break;
   case 'pgsql':
-      $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.17
diff -u -p -r1.17 backend.inc
--- includes/backend.inc	26 Oct 2009 02:26:37 -0000	1.17
+++ includes/backend.inc	26 Oct 2009 22:01:21 -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 successfully, 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,15 +199,18 @@ 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]);
+      // TODO:  This call to ob_flush says: "ob_flush(): failed to flush buffer. No buffer to flush."
+      // Commenting out this flush removes this warning with no apparant ill-effect; however,
+      // the warning is innocuous, and I do not know if there is some instance where the flush is 
+      // important, so I am leaving it in for Adrian's review.  The ob_flush above does not give warnings.
+      //  - Greg Anderson, 13 October 2009.
       ob_flush();
       flush();
     };
 
-
     fclose($pipes[0]);
     fclose($pipes[1]);
     fclose($pipes[2]);
@@ -221,8 +239,11 @@ function _drush_proc_open($cmd, $data = 
  *    If TRUE, any error statuses or log messages will be integrated into the current process. This might not be what you want,
  *    if you are writing a command that operates on multiple sites.
  * @param drush_path
- *    Optional. Defaults to the current drush.php file. You may also specify a different drush.php script.
- *    You will most likely need to set this when calling drush on a remote server.
+ *    Optional. Defaults to the current drush.php file on the local machine, and
+ *    to simply 'drush' (the drush script in the current PATH) on remote servers. 
+ *    You may also specify a different drush.php script explicitly.  You will need 
+ *    to set this when calling drush on a remote server if 'drush' is not in the
+ *    PATH on that machine.
  * @param hostname
  *    Optional. A remote host to execute the drush command on.
  * @param username
@@ -233,8 +254,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 +286,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 +307,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.
@@ -298,8 +327,11 @@ function _drush_backend_invoke($cmd, $da
  *    If TRUE, any error statuses or log messages will be integrated into the current process. This might not be what you want,
  *    if you are writing a command that operates on multiple sites.
  * @param drush_path
- *    Optional. Defaults to the current drush.php file. You may also specify a different drush.php script.
- *    You will most likely need to set this when calling drush on a remote server.
+ *    Optional. Defaults to the current drush.php file on the local machine, and
+ *    to simply 'drush' (the drush script in the current PATH) on remote servers. 
+ *    You may also specify a different drush.php script explicitly.  You will need 
+ *    to set this when calling drush on a remote server if 'drush' is not in the
+ *    PATH on that machine.
  * @param hostname
  *    Optional. A remote host to execute the drush command on.
  * @param username
@@ -308,25 +340,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 +381,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	26 Oct 2009 22:01:21 -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.47
diff -u -p -r1.47 environment.inc
--- includes/environment.inc	7 Oct 2009 16:23:20 -0000	1.47
+++ includes/environment.inc	26 Oct 2009 22:01:21 -0000
@@ -156,7 +156,6 @@ function _drush_bootstrap_phases($functi
  *     DRUSH_BOOTSTRAP_DRUPAL_FULL          = Initialize Drupal fully.
  *     DRUSH_BOOTSTRAP_DRUPAL_LOGIN         = Log into Drupal with a valid user.
  */
-
 function drush_bootstrap($phase) {
   static $phases;
   if (!$phases) {
@@ -399,6 +398,20 @@ function _drush_bootstrap_drush() {
   // Load a drushrc.php file in the ~/.drush directory.
   drush_load_config('user');
 
+  // Process the site alias that specifies which instance
+  // of drush (local or remote) this command will operate on.
+  // We must do this after we load our config files (so that
+  // site aliases are available), but before the rest
+  // of the drush and drupal root bootstrap phases are
+  // done, since site aliases may set option values that
+  // affect these phases.
+  // TODO: Note that this function will call drush_locate_root
+  // (from within _drush_sitealias_find_record_for_local_site),
+  // and drush_locate_root will be called again when bootstrapping
+  // the drupal root below.  Is there a good way to refactor this
+  // so that we do not need to search for the root twice?
+  drush_sitealias_check_arg();
+  
   $backend = drush_set_context('DRUSH_BACKEND', drush_get_option(array('b', 'backend')));
 
   if ($backend) {
@@ -461,6 +474,7 @@ function _drush_bootstrap_drupal_root_va
   }
 
   drush_bootstrap_value('drupal_root', $drupal_root);
+  
   return TRUE;
 }
 
@@ -894,4 +908,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
+}
+
