sync.sql.inc

  1. 6.x commands/sql/sync.sql.inc
  2. 3.x commands/sql/sync.sql.inc
  3. 4.x commands/sql/sync.sql.inc
  4. 5.x commands/sql/sync.sql.inc

Functions

File

commands/sql/sync.sql.inc
View source
  1. <?php
  2. require_once DRUSH_BASE_PATH . '/commands/core/rsync.core.inc';
  3. function drush_sql_sync($source = null, $destination = null) {
  4. $source_database = drush_get_option('source-database', 'default');
  5. $source_target = drush_get_option('source-target');
  6. $target_database = drush_get_option('target-database', 'default');
  7. $target_target = drush_get_option('target-target');
  8. //
  9. // If the destination was not explicitly set, but a particular
  10. // target database was specified on the command line, then we
  11. // will implicitly assume that the destination alias is the
  12. // same as the source alias.
  13. //
  14. if (!isset($destination) && (isset($target_database) || (isset($target_target)))) {
  15. $destination = $source;
  16. }
  17. //
  18. // If there is no destination specification, then exit.
  19. //
  20. if (!isset($destination)) {
  21. drush_print(dt("You must specify a destination target."));
  22. exit(1);
  23. }
  24. //
  25. // Default branch: copy 'sync' with the specified source
  26. // and destination.
  27. //
  28. else {
  29. _drush_sql_sync($source, $destination, TRUE);
  30. }
  31. }
  32. function _drush_sql_sync($source, $destination, $show_warning = TRUE) {
  33. // Preflight destination in case it defines the alias used by the source
  34. _drush_sitealias_get_record($destination);
  35. // After preflight, get source and destination settings
  36. $source_settings = drush_sitealias_get_record($source);
  37. $destination_settings = drush_sitealias_get_record($destination);
  38. // Insure that we have database records for the source and destination
  39. // alias records. sitealias_get_databases_from_record will cache the
  40. // database info inside the alias records, and drush_sitealias_set_alias_context
  41. // will copy the database record into the 'alias' context. We do not
  42. // actually use the databases record at this time.
  43. sitealias_get_databases_from_record($source_settings);
  44. sitealias_get_databases_from_record($destination_settings);
  45. // Check to see if this is an sql-sync multiple command (multiple sources and multiple destinations)
  46. $is_multiple = drush_do_multiple_command('sql-sync', $source_settings, $destination_settings);
  47. if ($is_multiple === FALSE) {
  48. // Evaluate the source and destination specifications into options.
  49. // The options from the 'source-*' and 'target-*' aliases are set
  50. // in a drush context that has a lower priority than the command-line
  51. // options; this allows command-line options to override the default
  52. // values specified in a site-alias.
  53. drush_sitealias_set_alias_context($source_settings, 'source-');
  54. drush_sitealias_set_alias_context($destination_settings, 'target-');
  55. // Get the options for the source and target databases
  56. $source_db_url = _drush_sql_get_spec_from_options('source-', FALSE);
  57. // The host may have special ssh requirements
  58. $source_remote_ssh_options = drush_get_option('source-ssh-options');
  59. // rsync later will also have to know this option
  60. $source_rsync_options = array('ssh-options' => $source_remote_ssh_options);
  61. $target_db_url = _drush_sql_get_spec_from_options('target-', FALSE);
  62. // The host may have special ssh requirements
  63. $target_remote_ssh_options = drush_get_option('target-ssh-options');
  64. // rsync later will also have to know this option
  65. $target_rsync_options = array('ssh-options' => $target_remote_ssh_options);
  66. if (empty($source_db_url)) {
  67. return drush_set_error('DRUSH_DATABASE_NOT_FOUND', dt('Error: no database record could be found for !source', array('!source' => $source)));
  68. }
  69. if (empty($target_db_url)) {
  70. return drush_set_error('DRUSH_DATABASE_NOT_FOUND', dt('Error: no database record could be found for !destination', array('!destination' => $destination)));
  71. }
  72. // Set up the result file and the remote file.
  73. // If the result file is not set, then create a temporary file.
  74. // If the remote file is not set, use the same name for the remote
  75. // and local files and hope for the best.
  76. $source_dump = drush_get_option('source-dump');
  77. $target_dump = drush_get_option('target-dump');
  78. $use_temp_files = drush_get_option('temp');
  79. $source_is_tmp = FALSE;
  80. $target_is_tmp = FALSE;
  81. if (!isset($source_db_url['remote-host']) && !isset($target_db_url['remote-host'])) {
  82. if (isset($source_dump)) {
  83. $target_dump = $source_dump;
  84. }
  85. else {
  86. if (!isset($target_dump)) {
  87. $target_dump = drush_sql_dump_file($target_db_url);
  88. $target_is_tmp = TRUE;
  89. }
  90. $source_dump = $target_dump;
  91. }
  92. }
  93. if (!isset($target_dump)) {
  94. $target_dump = drush_sql_dump_file($target_db_url);
  95. $target_is_tmp = TRUE;
  96. }
  97. if (!isset($source_dump)) {
  98. $source_dump = drush_sql_dump_file($source_db_url);
  99. $source_is_tmp = TRUE;
  100. $source_rsync_options['remove-source-files'] = TRUE;
  101. }
  102. if (isset($source_db_url['remote-host']) && isset($target_db_url['remote-host'])) {
  103. $local_file = drush_tempnam($source_db_url['database'] . ($source_db_url['database'] == $target_db_url['database'] ? '' : '-to-' . $target_db_url['database']) . '.sql.');
  104. }
  105. elseif (!isset($source_db_url['remote-host'])) {
  106. $local_file = $source_dump;
  107. }
  108. elseif (!isset($target_db_url['remote-host'])) {
  109. $local_file = $target_dump;
  110. }
  111. // If source is remote, then use ssh to dump the database and then rsync to local machine
  112. // If source is local, call drush_sql_dump to dump the database to local machine
  113. // In either case, the '--no-dump' option will cause the sql-dump step to be skipped, and
  114. // we will import from the existing local file (first using rsync to fetch it if it does not exist)
  115. //
  116. // No dump affects both local and remote sql-dumps; it prevents drush sql-sync
  117. // from calling sql-dump when the local cache file is newer than the cache threshhold
  118. // No sync affects the remote sql-dump; it will prevent drush sql-sync from
  119. // rsyncing the local sql-dump file with the remote sql-dump file.
  120. $no_sync = drush_get_option(array('no-sync', 'source-no-sync'));
  121. $no_dump = drush_get_option(array('no-dump', 'source-no-dump'));
  122. $no_cache = drush_get_option(array('no-cache', 'source-no-cache'));
  123. if (!isset($no_cache)) {
  124. $cache = drush_get_option(array('cache', 'source-cache'));
  125. if (!isset($cache)) {
  126. $cache = 24; // Default cache is 24 hours if nothing else is specified.
  127. }
  128. }
  129. // If the 'cache' option is set, then we will set the no-dump option iff the
  130. // target file exists and its modification date is less than "cache" hours.
  131. if (isset($cache)) {
  132. if (file_exists($local_file) && (filesize($local_file) > 0)) {
  133. if ((filemtime($local_file) - time()) < ($cache * 60 * 60)) {
  134. drush_log(dt('Modification time of local dump file is less than !cache hours old. Use the --no-cache option to force a refresh.', array('!cache' => $cache)));
  135. $no_dump = TRUE;
  136. $no_sync = TRUE;
  137. }
  138. else {
  139. drush_log(dt('Local sql cache file exists but is greater than !cache hours old.', array('!cache' => $cache)));
  140. }
  141. }
  142. else {
  143. drush_log('Local sql cache file does not exist.');
  144. }
  145. }
  146. $table_selection = array();
  147. if (!isset($no_dump)) {
  148. $table_selection = drush_sql_get_table_selection();
  149. }
  150. // Prompt for confirmation. This is destructive.
  151. if (!drush_get_context('DRUSH_SIMULATE') && $show_warning) {
  152. // If there are multiple destinations, then
  153. // prompt once here and suppress the warning message
  154. // and the normal confirmation below.
  155. if (array_key_exists('site-list', $destination_settings)) {
  156. drush_print(dt('You are about to sync the database from !source, overwriting all of the following targets:', array('!source' => $source)));
  157. foreach ($destination_settings['site-list'] as $one_destination) {
  158. drush_print(dt(' !target', array('!target' => $one_destination)));
  159. }
  160. drush_print();
  161. }
  162. else {
  163. // Check to see if we are using a temporary file in a situation
  164. // where the user did not specify "--temp".
  165. if (($source_is_tmp || $target_is_tmp) && (!isset($use_temp_files)) && (isset($source_db_url['remote-host']) || isset($target_db_url['remote-host']))) {
  166. drush_print(dt('WARNING: Using temporary files to store and transfer sql-dump. It is recommended that you specify --source-dump and --target-dump options on the command line, or set \'%dump\' in the path-aliases section of your site alias records. This facilitates fast file transfer via rsync.'));
  167. }
  168. $txt_source = (isset($source_db_url['remote-host']) ? $source_db_url['remote-host'] . '/' : '') . $source_db_url['database'];
  169. $txt_destination = (isset($target_db_url['remote-host']) ? $target_db_url['remote-host'] . '/' : '') . $target_db_url['database'];
  170. drush_print(dt("You will destroy data from !target and replace with data from !source.", array('!source' => $txt_source, '!target' => $txt_destination)));
  171. drush_print();
  172. }
  173. if (array_key_exists('tables', $table_selection) && (count($table_selection['tables']) > 0)) {
  174. drush_print(dt(' Only the following tables will be transferred: !list', array('!list' => implode(',', $table_selection['tables']))));
  175. drush_print();
  176. }
  177. elseif (!empty($table_selection)) {
  178. $skip_tables_list = implode(',', $table_selection['skip'] + $table_selection['structure']);
  179. if(!empty($skip_tables_list)) {
  180. drush_print(dt(' The following tables will be skipped: !list', array('!list' => $skip_tables_list)));
  181. drush_print();
  182. }
  183. }
  184. // TODO: actually make the backup if desired.
  185. drush_print(dt("You might want to make a backup first, using sql_dump command.\n"));
  186. if (!drush_confirm(dt('Do you really want to continue?'))) {
  187. drush_die('Aborting.');
  188. }
  189. }
  190. if (isset($source_db_url['remote-host'])) {
  191. $source_remote_user = drush_get_option('source-remote-user');
  192. if (isset($source_remote_user)) {
  193. $source_at ='@';
  194. $source_remote_pass = drush_get_option('source-remote-pass') ? ':' . drush_get_option('source-remote-pass') : '';
  195. }
  196. if (!isset($no_dump)) {
  197. $source_intermediate = $source_dump;
  198. $mv_intermediate = '';
  199. // If we are doing a remote dump and the source is not a temporary file,
  200. // then first dump to a temporary file and move it to the specified file after
  201. // the dump is complete. This will reduce contention during simultaneous dumps
  202. // from different users sharing the same dump file.
  203. if (!isset($source_is_tmp)) {
  204. $source_intermediate = $source_dump . '-' . date("U");
  205. $mv_intermediate = '; mv -f ' . $source_intermediate . ' ' . $source_dump;
  206. }
  207. drush_set_option('result-file', $source_intermediate);
  208. $dump_exec = drush_sql_build_dump_command($table_selection, $source_db_url) . $mv_intermediate;
  209. if (isset($cache) && !isset($source_is_tmp)) {
  210. // Inject some bash commands to remotely test the modification date of the target file
  211. // if the cache option is set.
  212. $dump_exec = 'if [ ! -s ' . $source_dump . '] || [ $((`date "+%s"`-`stat --format="%Y" ' . $source_dump . '`)) -gt ' . ($cache * 60 * 60) . ' ] ; then ' . $dump_exec . '; fi';
  213. }
  214. $dump_exec = "ssh $source_remote_ssh_options $source_remote_user$source_at" . $source_db_url['remote-host'] . " " . escapeshellarg($dump_exec);
  215. }
  216. }
  217. else {
  218. if (!isset($no_dump)) {
  219. drush_set_option('result-file', $local_file);
  220. $dump_exec = drush_sql_build_dump_command($table_selection, $source_db_url);
  221. }
  222. $no_sync = TRUE;
  223. }
  224. // Call sql-dump, either on the local machine or remotely via ssh, as appropriate.
  225. if (!empty($dump_exec)) {
  226. drush_op('system', $dump_exec);
  227. // TODO: IF FAILURE THEN ABORT
  228. }
  229. // If the sql-dump was remote, then rsync the file over to the local machine.
  230. if (!isset($no_sync)) {
  231. // If the source file is a temporary file, then we will have rsync
  232. // delete it for us (remove-source-files option set above).
  233. drush_core_call_rsync($source_remote_user . $source_at . $source_db_url['remote-host'] . ':' . $source_dump, $local_file, $source_rsync_options);
  234. }
  235. // We will handle lists of destination sites differently from
  236. // single source-to-destination syncs.
  237. if (array_key_exists('site-list', $destination_settings)) {
  238. // Insure that we will not dump the source sql database
  239. // repeatedly, but will instead re-use it each time through
  240. // the redispatch loop.
  241. drush_set_option('no-dump', TRUE);
  242. drush_set_option('no-sync', TRUE);
  243. drush_set_option('source-dump', $source_dump);
  244. // Call sql-sync for each destination to push the $source_dump
  245. // to each target in turn.
  246. foreach ($destination_settings['site-list'] as $one_destination) {
  247. drush_do_command_redispatch('sql-sync', array($source, $one_destination));
  248. }
  249. }
  250. else {
  251. // Prior to database import, we will generate a "create database" command
  252. // if the '--create-db' option was specified. Note that typically the
  253. // web server user will not have permissions to create a database; to specify
  254. // a different user to use with the create db command, the '--db-su' option
  255. // may be used.
  256. // Under postgres, "alter role username with createdb;" will give create database
  257. // permissions to the specified user if said user was not created with this right.
  258. $pre_import_commands = '';
  259. $create_db = drush_get_option('create-db');
  260. if (isset($create_db)) {
  261. $create_db_target = $target_db_url;
  262. $create_db_target['database'] = '';
  263. $db_superuser = drush_get_option(array('db-su', 'target-db-su'));
  264. if (isset($db_superuser)) {
  265. $create_db_target['username'] = $db_superuser;
  266. }
  267. $db_su_pw = drush_get_option(array('db-su-pw', 'target-db-su-pw'));
  268. if (isset($db_su_pw)) {
  269. $create_db_target['password'] = $db_su_pw;
  270. }
  271. $db_su_connect = _drush_sql_connect($create_db_target);
  272. switch (_drush_sql_get_scheme($target_db_url)) {
  273. case 'mysql':
  274. $pre_import_commands = 'echo "DROP DATABASE IF EXISTS ' . $target_db_url['database'] . '; CREATE DATABASE ' . $target_db_url['database'] . '; GRANT ALL PRIVILEGES ON ' . $target_db_url['database'] . '.* TO \'' . $target_db_url['username'] . '\'@\'' . $target_db_url['host'] . '\' IDENTIFIED BY \'' . $target_db_url['password'] . '\';" | ' . $db_su_connect . '; ';
  275. break;
  276. case 'pgsql':
  277. $pre_import_commands = 'echo "drop database if exists ' . $target_db_url['database'] . '; create database ' . $target_db_url['database'] . ';" | ' . $db_su_connect . '; ';
  278. break;
  279. }
  280. }
  281. // Generate the import command
  282. $import_command = _drush_sql_connect($target_db_url);
  283. switch (_drush_sql_get_scheme($target_db_url)) {
  284. case 'mysql':
  285. $import_command .= ' ' . (drush_get_context('DRUSH_DEBUG') ? ' ' : '--silent');
  286. break;
  287. case 'pgsql':
  288. $import_command .= ' ' . (drush_get_context('DRUSH_DEBUG') ? ' ' : '-q');
  289. break;
  290. }
  291. // If destination is remote, then use rsync to push the database, then use ssh to import the database
  292. // If destination is local, then just import the database locally
  293. if (isset($target_db_url['remote-host'])) {
  294. $target_remote_user = drush_get_option('target-remote-user');
  295. if (isset($target_remote_user)) {
  296. $target_at ='@';
  297. $target_remote_pass = drush_get_option('target-remote-pass') ? ':' . drush_get_option('target-remote-pass') : '';
  298. }
  299. drush_core_call_rsync($local_file, $target_remote_user . $target_at . $target_db_url['remote-host'] . ':' . $target_dump, $target_rsync_options);
  300. $connect_exec = $pre_import_commands . $import_command . ' < ' . $target_dump;
  301. $import_exec = "ssh $target_remote_ssh_options $target_remote_user$target_at" . $target_db_url['remote-host'] . ' ' . escapeshellarg($connect_exec);
  302. // delete the remote target file if it is a temporary file
  303. if ($target_is_tmp) {
  304. $import_exec .= '; rm -f ' . escapeshellarg($target_dump);
  305. }
  306. }
  307. else {
  308. $import_exec = $pre_import_commands . $import_command . ' < ' . $local_file;
  309. }
  310. drush_op('system', $import_exec);
  311. }
  312. }
  313. }