sql.drush.inc

  1. 8.0.x commands/sql/sql.drush.inc
  2. 6.x commands/sql/sql.drush.inc
  3. 7.x commands/sql/sql.drush.inc
  4. 3.x commands/sql/sql.drush.inc
  5. 4.x commands/sql/sql.drush.inc
  6. 5.x commands/sql/sql.drush.inc
  7. master commands/sql/sql.drush.inc

Drush sql commands

Functions

Namesort descending Description
drush_sql_bootstrap_database_configuration Safely bootstrap Drupal to the point where we can access the database configuration.
drush_sql_bootstrap_further Check whether further bootstrap is needed. If so, do it.
drush_sql_cli
drush_sql_conf Command callback. Displays the Drupal site's database connection string.
drush_sql_connect Command callback. Emits a connect string.
drush_sql_create Command callback. Create a database.
drush_sql_drop Drops all tables in the database.
drush_sql_dump Command callback. Outputs the entire Drupal database in SQL format using mysqldump or equivalent.
drush_sql_expand_wildcard_tables Expand wildcard tables.
drush_sql_filter_tables Filters tables.
drush_sql_get_class Wrapper for drush_get_class; instantiates an driver-specific instance of SqlBase class.
drush_sql_get_table_selection Construct an array that places table names in appropriate buckets based on whether the table is to be skipped, included for structure only, or have structure and data dumped. The keys of the array are:
drush_sql_get_version Wrapper for drush_get_class; instantiates a Drupal version-specific instance of SqlVersion class.
drush_sql_query Command callback. Executes the given SQL query on the Drupal database.
drush_sql_register_post_sync_op Call from a pre-sql-sync hook to register an sql query to be executed in the post-sql-sync hook.
drush_sql_sanitize Command callback. Run's the sanitization operations on the current database.
sql_drush_command Implementation of hook_drush_command().
sql_drush_help Implementation of hook_drush_help().
sql_drush_help_alter Implements hook_drush_help_alter().
sql_drush_sql_sync_sanitize Implements hook_sql_drush_sql_sync_sanitize.
sql_sql_sync_complete Command argument complete callback.
_drush_sql_expand_and_filter_tables Given the table names in the input array that may contain wildcards (`*`), expand the table names so that the array returned only contains table names that exist in the database.
_drush_sql_get_post_sync_messages Builds a confirmation message for all post-sync operations.
_drush_sql_get_raw_table_list Consult the specified options and return the list of tables specified.

File

commands/sql/sql.drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Drush sql commands
  5. */
  6. /**
  7. * Implementation of hook_drush_help().
  8. */
  9. function sql_drush_help($section) {
  10. switch ($section) {
  11. case 'meta:sql:title':
  12. return dt('SQL commands');
  13. case 'meta:sql:summary':
  14. return dt('Examine and modify your Drupal database.');
  15. case 'drush:sql-sanitize':
  16. return dt('Run sanitization operations on the current database. You can add more sanitization to this command by implementing hook_drush_sql_sync_sanitize().');
  17. }
  18. }
  19. /**
  20. * Implementation of hook_drush_command().
  21. */
  22. function sql_drush_command() {
  23. $options['database'] = array(
  24. 'description' => 'The DB connection key if using multiple connections in settings.php.',
  25. 'example-value' => 'key',
  26. );
  27. $db_url['db-url'] = array(
  28. 'description' => 'A Drupal 6 style database URL.',
  29. 'example-value' => 'mysql://root:pass@127.0.0.1/db',
  30. );
  31. $options['target'] = array(
  32. 'description' => 'The name of a target within the specified database connection. Defaults to \'default\'.',
  33. 'example-value' => 'key',
  34. // Gets unhidden in help_alter(). We only want to show this to D7 users but have to
  35. // declare it here since some commands do not bootstrap fully.
  36. 'hidden' => TRUE,
  37. );
  38. $items['sql-drop'] = array(
  39. 'description' => 'Drop all tables in a given database.',
  40. 'arguments' => array(
  41. ),
  42. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  43. 'options' => array(
  44. 'yes' => 'Skip confirmation and proceed.',
  45. 'result-file' => array(
  46. 'description' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
  47. 'example-value' => '/path/to/file',
  48. ),
  49. ) + $options + $db_url,
  50. 'topics' => array('docs-policy'),
  51. );
  52. $items['sql-conf'] = array(
  53. 'description' => 'Print database connection details using print_r().',
  54. 'hidden' => TRUE,
  55. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  56. 'options' => array(
  57. 'all' => 'Show all database connections, instead of just one.',
  58. 'show-passwords' => 'Show database password.',
  59. ) + $options,
  60. 'outputformat' => array(
  61. 'default' => 'print-r',
  62. 'pipe-format' => 'var_export',
  63. 'private-fields' => 'password',
  64. ),
  65. );
  66. $items['sql-connect'] = array(
  67. 'description' => 'A string for connecting to the DB.',
  68. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  69. 'options' => $options + $db_url + array(
  70. 'extra' => array(
  71. 'description' => 'Add custom options to the mysql command.',
  72. 'example-value' => '--skip-column-names',
  73. ),
  74. ),
  75. 'examples' => array(
  76. '`drush sql-connect` < example.sql' => 'Import sql statements from a file into the current database.',
  77. ),
  78. );
  79. $items['sql-create'] = array(
  80. 'description' => 'Create a database.',
  81. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  82. 'examples' => array(
  83. 'drush sql-create' => 'Create the database for the current site.',
  84. 'drush @site.test sql-create' => 'Create the database as specified for @site.test.',
  85. 'drush sql-create --db-su=root --db-su-pw=rootpassword --db-url="mysql://drupal_db_user:drupal_db_password@127.0.0.1/drupal_db"' =>
  86. 'Create the database as specified in the db-url option.'
  87. ),
  88. 'options' => array(
  89. 'db-su' => 'Account to use when creating a new database. Optional.',
  90. 'db-su-pw' => 'Password for the "db-su" account. Optional.',
  91. ) + $options + $db_url,
  92. );
  93. $items['sql-dump'] = array(
  94. 'description' => 'Exports the Drupal DB as SQL using mysqldump or equivalent.',
  95. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  96. 'examples' => array(
  97. 'drush sql-dump --result-file=../18.sql' => 'Save SQL dump to the directory above Drupal root.',
  98. 'drush sql-dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.php',
  99. ),
  100. 'options' => array(
  101. 'result-file' => array(
  102. 'description' => 'Save to a file. The file should be relative to Drupal root. If --result-file is provided with no value, then date based filename will be created under ~/drush-backups directory.',
  103. 'example-value' => '/path/to/file',
  104. 'value' => 'optional',
  105. ),
  106. 'skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
  107. 'structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
  108. 'tables-key' => 'A key in the $tables array. Optional.',
  109. 'skip-tables-list' => 'A comma-separated list of tables to exclude completely. Optional.',
  110. 'structure-tables-list' => 'A comma-separated list of tables to include for structure, but not data. Optional.',
  111. 'tables-list' => 'A comma-separated list of tables to transfer. Optional.',
  112. 'create-db' => array('hidden' => TRUE, 'description' => 'Omit DROP TABLE statements. Postgres and Oracle only. Used by sql-sync, since including the DROP TABLE statements interfere with the import when the database is created.'),
  113. 'data-only' => 'Dump data without statements to create any of the schema.',
  114. 'ordered-dump' => 'Order by primary key and add line breaks for efficient diff in revision control. Slows down the dump. Mysql only.',
  115. 'gzip' => 'Compress the dump using the gzip program which must be in your $PATH.',
  116. ) + $options + $db_url,
  117. );
  118. $items['sql-query'] = array(
  119. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  120. 'description' => 'Execute a query against a database.',
  121. 'examples' => array(
  122. 'drush sql-query "SELECT * FROM users WHERE uid=1"' => 'Browse user record. Table prefixes, if used, must be added to table names by hand.',
  123. 'drush sql-query --db-prefix "SELECT * FROM {users} WHERE uid=1"' => 'Browse user record. Table prefixes are honored. Caution: curly-braces will be stripped from all portions of the query.',
  124. '`drush sql-connect` < example.sql' => 'Import sql statements from a file into the current database.',
  125. 'drush sql-query --file=example.sql' => 'Alternate way to import sql statements from a file.',
  126. ),
  127. 'arguments' => array(
  128. 'query' => 'An SQL query. Ignored if \'file\' is provided.',
  129. ),
  130. 'options' => array(
  131. 'result-file' => array(
  132. 'description' => 'Save to a file. The file should be relative to Drupal root. Optional.',
  133. 'example-value' => '/path/to/file',
  134. ),
  135. 'file' => 'Path to a file containing the SQL to be run. Gzip files are accepted.',
  136. 'extra' => array(
  137. 'description' => 'Add custom options to the mysql command.',
  138. 'example-value' => '--skip-column-names',
  139. ),
  140. 'db-prefix' => 'Enable replacement of braces in your query.',
  141. 'db-spec' => array(
  142. 'description' => 'A database specification',
  143. 'hidden' => TRUE, // Hide since this is only used with --backend calls.
  144. )
  145. ) + $options + $db_url,
  146. 'aliases' => array('sqlq'),
  147. );
  148. $items['sql-cli'] = array(
  149. 'description' => "Open a SQL command-line interface using Drupal's credentials.",
  150. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  151. // 'options' => $options + $db_url,
  152. 'allow-additional-options' => array('sql-connect'),
  153. 'aliases' => array('sqlc'),
  154. 'examples' => array(
  155. 'drush sql-cli' => "Open a SQL command-line interface using Drupal's credentials.",
  156. 'drush sql-cli --extra=-A' => "Open a SQL CLI and skip reading table information.",
  157. ),
  158. 'remote-tty' => TRUE,
  159. );
  160. $items['sql-sanitize'] = array(
  161. 'description' => "Run sanitization operations on the current database.",
  162. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  163. 'drush dependencies' => array('sqlsync'),
  164. 'options' => drupal_sanitize_options() + array(
  165. 'db-prefix' => 'Enable replacement of braces in sanitize queries.',
  166. ) + $db_url,
  167. 'aliases' => array('sqlsan'),
  168. );
  169. return $items;
  170. }
  171. /**
  172. * Implements hook_drush_help_alter().
  173. */
  174. function sql_drush_help_alter(&$command) {
  175. // Drupal 7+ only options.
  176. if (drush_drupal_major_version() >= 7) {
  177. if ($command['commandfile'] == 'sql') {
  178. unset($command['options']['target']['hidden']);
  179. }
  180. }
  181. }
  182. /**
  183. * Command argument complete callback.
  184. *
  185. * @return
  186. * Array of available site aliases.
  187. */
  188. function sql_sql_sync_complete() {
  189. return array('values' => array_keys(_drush_sitealias_all_list()));
  190. }
  191. /**
  192. * Safely bootstrap Drupal to the point where we can
  193. * access the database configuration.
  194. */
  195. function drush_sql_bootstrap_database_configuration() {
  196. // Under Drupal 7, if the database is configured but empty, then
  197. // DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION will throw an exception.
  198. // If this happens, we'll just catch it and continue.
  199. // TODO: Fix this in the bootstrap, per http://drupal.org/node/1996004
  200. try {
  201. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION);
  202. }
  203. catch (Exception $e) {
  204. }
  205. }
  206. /**
  207. * Check whether further bootstrap is needed. If so, do it.
  208. */
  209. function drush_sql_bootstrap_further() {
  210. if (!drush_get_option(array('db-url', 'db-spec'))) {
  211. drush_sql_bootstrap_database_configuration();
  212. }
  213. }
  214. /**
  215. * Command callback. Displays the Drupal site's database connection string.
  216. */
  217. function drush_sql_conf() {
  218. drush_sql_bootstrap_database_configuration();
  219. if (drush_get_option('all')) {
  220. $sqlVersion = drush_sql_get_version();
  221. return $sqlVersion->getAll();
  222. }
  223. else {
  224. $sql = drush_sql_get_class();
  225. return $sql->db_spec();
  226. }
  227. }
  228. /**
  229. * Command callback. Emits a connect string.
  230. */
  231. function drush_sql_connect() {
  232. drush_sql_bootstrap_further();
  233. $sql = drush_sql_get_class();
  234. return $sql->connect(FALSE);
  235. }
  236. /**
  237. * Command callback. Create a database.
  238. */
  239. function drush_sql_create() {
  240. drush_sql_bootstrap_further();
  241. $sql = drush_sql_get_class();
  242. $db_spec = $sql->db_spec();
  243. // Prompt for confirmation.
  244. if (!drush_get_context('DRUSH_SIMULATE')) {
  245. // @todo odd - maybe for sql-sync.
  246. $txt_destination = (isset($db_spec['remote-host']) ? $db_spec['remote-host'] . '/' : '') . $db_spec['database'];
  247. drush_print(dt("Creating database !target. Any possible existing database will be dropped!", array('!target' => $txt_destination)));
  248. if (!drush_confirm(dt('Do you really want to continue?'))) {
  249. return drush_user_abort();
  250. }
  251. }
  252. return $sql->createdb();
  253. }
  254. /**
  255. * Command callback. Outputs the entire Drupal database in SQL format using mysqldump or equivalent.
  256. */
  257. function drush_sql_dump() {
  258. drush_sql_bootstrap_further();
  259. $sql = drush_sql_get_class();
  260. return $sql->dump(drush_get_option('result-file', FALSE));
  261. }
  262. /**
  263. * Construct an array that places table names in appropriate
  264. * buckets based on whether the table is to be skipped, included
  265. * for structure only, or have structure and data dumped.
  266. * The keys of the array are:
  267. * - skip: tables to be skipped completed in the dump
  268. * - structure: tables to only have their structure i.e. DDL dumped
  269. * - tables: tables to have structure and data dumped
  270. *
  271. * @return array
  272. * An array of table names with each table name in the appropriate
  273. * element of the array.
  274. */
  275. function drush_sql_get_table_selection() {
  276. // Skip large core tables if instructed. Used by 'sql-drop/sql-dump/sql-sync' commands.
  277. $skip_tables = _drush_sql_get_raw_table_list('skip-tables');
  278. // Skip any structure-tables as well.
  279. $structure_tables = _drush_sql_get_raw_table_list('structure-tables');
  280. // Dump only the specified tables. Takes precedence over skip-tables and structure-tables.
  281. $tables = _drush_sql_get_raw_table_list('tables');
  282. return array('skip' => $skip_tables, 'structure' => $structure_tables, 'tables' => $tables);
  283. }
  284. /**
  285. * Expand wildcard tables.
  286. *
  287. * @param array $tables
  288. * An array of table names, some of which may contain wildcards (`*`).
  289. * @param array $db_tables
  290. * An array with all the existing table names in the current database.
  291. * @return
  292. * $tables array with wildcards resolved to real table names.
  293. */
  294. function drush_sql_expand_wildcard_tables($tables, $db_tables) {
  295. // Table name expansion based on `*` wildcard.
  296. $expanded_db_tables = array();
  297. foreach ($tables as $k => $table) {
  298. // Only deal with table names containing a wildcard.
  299. if (strpos($table, '*') !== FALSE) {
  300. $pattern = '/^' . str_replace('*', '.*', $table) . '$/i';
  301. // Merge those existing tables which match the pattern with the rest of
  302. // the expanded table names.
  303. $expanded_db_tables += preg_grep($pattern, $db_tables);
  304. }
  305. }
  306. return $expanded_db_tables;
  307. }
  308. /**
  309. * Filters tables.
  310. *
  311. * @param array $tables
  312. * An array of table names to filter.
  313. * @param array $db_tables
  314. * An array with all the existing table names in the current database.
  315. * @return
  316. * An array with only valid table names (i.e. all of which actually exist in
  317. * the database).
  318. */
  319. function drush_sql_filter_tables($tables, $db_tables) {
  320. // Ensure all the tables actually exist in the database.
  321. foreach ($tables as $k => $table) {
  322. if (!in_array($table, $db_tables)) {
  323. unset($tables[$k]);
  324. }
  325. }
  326. return $tables;
  327. }
  328. /**
  329. * Given the table names in the input array that may contain wildcards (`*`),
  330. * expand the table names so that the array returned only contains table names
  331. * that exist in the database.
  332. *
  333. * @param array $tables
  334. * An array of table names where the table names may contain the
  335. * `*` wildcard character.
  336. * @param array $db_tables
  337. * The list of tables present in a database.
  338. * @return array
  339. * An array of tables with non-existant tables removed.
  340. */
  341. function _drush_sql_expand_and_filter_tables($tables, $db_tables) {
  342. $expanded_tables = drush_sql_expand_wildcard_tables($tables, $db_tables);
  343. $tables = drush_sql_filter_tables(array_merge($tables, $expanded_tables), $db_tables);
  344. $tables = array_unique($tables);
  345. sort($tables);
  346. return $tables;
  347. }
  348. /**
  349. * Consult the specified options and return the list of tables
  350. * specified.
  351. *
  352. * @param option_name
  353. * The option name to check: skip-tables, structure-tables
  354. * or tables. This function will check both *-key and *-list,
  355. * and, in the case of sql-sync, will also check target-*
  356. * and source-*, to see if an alias set one of these options.
  357. * @returns array
  358. * Returns an array of tables based on the first option
  359. * found, or an empty array if there were no matches.
  360. */
  361. function _drush_sql_get_raw_table_list($option_name) {
  362. foreach(array('' => 'cli', 'target-,,source-' => NULL) as $prefix_list => $context) {
  363. foreach(explode(',',$prefix_list) as $prefix) {
  364. $key_list = drush_get_option($prefix . $option_name . '-key', NULL, $context);
  365. foreach(explode(',', $key_list) as $key) {
  366. $all_tables = drush_get_option($option_name, array());
  367. if (array_key_exists($key, $all_tables)) {
  368. return $all_tables[$key];
  369. }
  370. if ($option_name != 'tables') {
  371. $all_tables = drush_get_option('tables', array());
  372. if (array_key_exists($key, $all_tables)) {
  373. return $all_tables[$key];
  374. }
  375. }
  376. }
  377. $table_list = drush_get_option($prefix . $option_name . '-list', NULL, $context);
  378. if (isset($table_list)) {
  379. return empty($table_list) ? array() : explode(',', $table_list);
  380. }
  381. }
  382. }
  383. return array();
  384. }
  385. /**
  386. * Command callback. Executes the given SQL query on the Drupal database.
  387. */
  388. function drush_sql_query($query = NULL) {
  389. drush_sql_bootstrap_further();
  390. $filename = drush_get_option('file', NULL);
  391. // Enable prefix processing when db-prefix option is used.
  392. if (drush_get_option('db-prefix')) {
  393. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_DATABASE);
  394. }
  395. if (drush_get_context('DRUSH_SIMULATE')) {
  396. if ($query) {
  397. drush_print(dt('Simulating sql-query: !q', array('!q' => $query)));
  398. }
  399. else {
  400. drush_print(dt('Simulating sql-import from !f', array('!f' => drush_get_option('file'))));
  401. }
  402. }
  403. else {
  404. $sql = drush_sql_get_class(drush_get_option('db-spec'));
  405. $result = $sql->query($query, $filename, drush_get_option('result-file'));
  406. if (!$result) {
  407. return drush_set_error('DRUSH_SQL_NO_QUERY', dt('Query failed.'));
  408. }
  409. drush_print(implode("\n", drush_shell_exec_output()));
  410. }
  411. return TRUE;
  412. }
  413. /**
  414. * Drops all tables in the database.
  415. */
  416. function drush_sql_drop() {
  417. drush_sql_bootstrap_further();
  418. $sql = drush_sql_get_class();
  419. $db_spec = $sql->db_spec();
  420. if (!drush_confirm(dt('Do you really want to drop all tables in the database !db?', array('!db' => $db_spec['database'])))) {
  421. return drush_user_abort();
  422. }
  423. $tables = $sql->listTables();
  424. return $sql->drop($tables);
  425. }
  426. function drush_sql_cli() {
  427. drush_sql_bootstrap_further();
  428. $sql = drush_sql_get_class();
  429. return !(bool)drush_shell_proc_open($sql->connect());
  430. }
  431. /**
  432. * Command callback. Run's the sanitization operations on the current database.
  433. *
  434. * @see hook_drush_sql_sync_sanitize() for adding custom sanitize routines.
  435. */
  436. function drush_sql_sanitize() {
  437. drush_sql_bootstrap_further();
  438. if (drush_get_option('db-prefix')) {
  439. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_DATABASE);
  440. }
  441. drush_command_invoke_all('drush_sql_sync_sanitize', 'default');
  442. $options = drush_get_context('post-sync-ops');
  443. if (!empty($options)) {
  444. if (!drush_get_context('DRUSH_SIMULATE')) {
  445. $messages = _drush_sql_get_post_sync_messages();
  446. if ($messages) {
  447. drush_print();
  448. drush_print($messages);
  449. }
  450. }
  451. }
  452. if (!drush_confirm(dt('Do you really want to sanitize the current database?'))) {
  453. return drush_user_abort();
  454. }
  455. $sanitize_query = '';
  456. foreach($options as $id => $data) {
  457. // Enable prefix processing when db-prefix option is used.
  458. if (drush_get_option('db-prefix')) {
  459. if (drush_drupal_major_version() >= 7) {
  460. $data['query'] = Database::getConnection()->prefixTables($data['query']);
  461. }
  462. else {
  463. $data['query'] = db_prefix_tables($data['query']);
  464. }
  465. }
  466. $sanitize_query .= $data['query'] . " ";
  467. }
  468. if ($sanitize_query) {
  469. $sql = drush_sql_get_class();
  470. return $sql->query($sanitize_query);
  471. }
  472. }
  473. /**
  474. * Call from a pre-sql-sync hook to register an sql
  475. * query to be executed in the post-sql-sync hook.
  476. * @see drush_sql_pre_sql_sync() and @see drush_sql_post_sql_sync().
  477. *
  478. * @param $id
  479. * String containing an identifier representing this
  480. * operation. This id is not actually used at the
  481. * moment, it is just used to fufill the contract
  482. * of drush contexts.
  483. * @param $message
  484. * String with the confirmation message that describes
  485. * to the user what the post-sync operation is going
  486. * to do. This confirmation message is printed out
  487. * just before the user is asked whether or not the
  488. * sql-sync operation should be continued.
  489. * @param $query
  490. * String containing the sql query to execute. If no
  491. * query is provided, then the confirmation message will
  492. * be displayed to the user, but no action will be taken
  493. * in the post-sync hook. This is useful for drush modules
  494. * that wish to provide their own post-sync hooks to fix
  495. * up the target database in other ways (e.g. through
  496. * Drupal APIs).
  497. */
  498. function drush_sql_register_post_sync_op($id, $message, $query = NULL) {
  499. $options = drush_get_context('post-sync-ops');
  500. $options[$id] = array('message' => $message, 'query' => $query);
  501. drush_set_context('post-sync-ops', $options);
  502. }
  503. /**
  504. * Builds a confirmation message for all post-sync operations.
  505. *
  506. * @return string
  507. * All post-sync operation messages concatenated together.
  508. */
  509. function _drush_sql_get_post_sync_messages() {
  510. $messages = FALSE;
  511. $options = drush_get_context('post-sync-ops');
  512. if (!empty($options)) {
  513. $messages = dt('The following post-sync operations will be done on the destination:') . "\n";
  514. foreach($options as $id => $data) {
  515. $messages .= " * " . $data['message'] . "\n";
  516. }
  517. }
  518. return $messages;
  519. }
  520. /**
  521. * Wrapper for drush_get_class; instantiates an driver-specific instance
  522. * of SqlBase class.
  523. *
  524. * @param array $db_spec
  525. * If known, specify a $db_spec that the class can operate with.
  526. *
  527. * @throws \Drush\Sql\SqlException
  528. *
  529. * @return Drush\Sql\SqlBase
  530. */
  531. function drush_sql_get_class($db_spec = NULL) {
  532. $database = drush_get_option('database', 'default');
  533. $target = drush_get_option('target', 'default');
  534. // Try a few times to quickly get $db_spec.
  535. if (!empty($db_spec)) {
  536. if (!empty($db_spec['driver'])) {
  537. return drush_get_class(array('Drush\Sql\Sql', 'Drupal\Driver\Database\\' . $db_spec['driver'] . '\Drush'), array($db_spec), array($db_spec['driver']));
  538. }
  539. }
  540. elseif ($url = drush_get_option('db-url')) {
  541. $url = is_array($url) ? $url[$database] : $url;
  542. $db_spec = drush_convert_db_from_db_url($url);
  543. $db_spec['db_prefix'] = drush_get_option('db-prefix');
  544. return drush_sql_get_class($db_spec);
  545. }
  546. elseif (($databases = drush_get_option('databases')) && (array_key_exists($database, $databases)) && (array_key_exists($target, $databases[$database]))) {
  547. $db_spec = $databases[$database][$target];
  548. return drush_sql_get_class($db_spec);
  549. }
  550. else {
  551. // No parameter or options provided. Determine $db_spec ourselves.
  552. if ($sqlVersion = drush_sql_get_version()) {
  553. if ($db_spec = $sqlVersion->get_db_spec()) {
  554. return drush_sql_get_class($db_spec);
  555. }
  556. }
  557. }
  558. throw new \Drush\Sql\SqlException('Unable to find a matching SQL Class. Drush cannot find your database connection details.');
  559. }
  560. /**
  561. * Wrapper for drush_get_class; instantiates a Drupal version-specific instance
  562. * of SqlVersion class.
  563. *
  564. * @return Drush\Sql\SqlVersion
  565. */
  566. function drush_sql_get_version() {
  567. return drush_get_class('Drush\Sql\Sql', array(), array(drush_drupal_major_version())) ?: NULL;
  568. }
  569. /**
  570. * Implements hook_sql_drush_sql_sync_sanitize.
  571. *
  572. * Sanitize usernames, passwords, and sessions when the --sanitize option is used.
  573. * It is also an example of how to write a database sanitizer for sql sync.
  574. *
  575. * To write your own sync hook function, define mymodule_drush_sql_sync_sanitize()
  576. * and follow the form of this function to add your own database
  577. * sanitization operations via the register post-sync op function;
  578. * @see drush_sql_register_post_sync_op(). This is the only thing that the
  579. * sync hook function needs to do; sql-sync takes care of the rest.
  580. *
  581. * The function below has a lot of logic to process user preferences and
  582. * generate the correct SQL regardless of whether Postgres, Mysql,
  583. * Drupal 6/7/8 is in use. A simpler sanitize function that
  584. * always used default values and only worked with Drupal 6 + mysql
  585. * appears in the drush.api.php. @see hook_drush_sql_sync_sanitize().
  586. */
  587. function sql_drush_sql_sync_sanitize($site) {
  588. $site_settings = drush_sitealias_get_record($site);
  589. $databases = sitealias_get_databases_from_record($site_settings);
  590. if (drush_get_option('db-prefix') || !empty($databases['default']['default']['prefix'])) {
  591. $wrap_table_name = TRUE;
  592. }
  593. else {
  594. $wrap_table_name = FALSE;
  595. }
  596. $user_table_updates = array();
  597. $message_list = array();
  598. // Sanitize passwords.
  599. $newpassword = drush_get_option(array('sanitize-password', 'destination-sanitize-password'), 'password');
  600. if ($newpassword != 'no' && $newpassword !== 0) {
  601. $major_version = drush_drupal_major_version();
  602. $pw_op = "";
  603. // In Drupal 6, passwords are hashed via the MD5 algorithm.
  604. if ($major_version == 6) {
  605. $pw_op = "MD5('$newpassword')";
  606. }
  607. // In Drupal 7, passwords are hashed via a more complex algorithm,
  608. // available via the user_hash_password function.
  609. elseif ($major_version == 7) {
  610. $core = DRUSH_DRUPAL_CORE;
  611. include_once $core . '/includes/password.inc';
  612. include_once $core . '/includes/bootstrap.inc';
  613. $hash = user_hash_password($newpassword);
  614. $pw_op = "'$hash'";
  615. }
  616. else {
  617. // D8+. Mimic Drupal's /scripts/password-hash.sh
  618. drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL);
  619. $password_hasher = \Drupal::service('password');
  620. $hash = $password_hasher->hash($newpassword);
  621. $pw_op = "'$hash'";
  622. }
  623. if (!empty($pw_op)) {
  624. $user_table_updates[] = "pass = $pw_op";
  625. $message_list[] = "passwords";
  626. }
  627. }
  628. // Sanitize email addresses.
  629. $newemail = drush_get_option(array('sanitize-email', 'destination-sanitize-email'), 'user+%uid@localhost.localdomain');
  630. if ($newemail != 'no' && $newemail !== 0) {
  631. if (strpos($newemail, '%') !== FALSE) {
  632. // We need a different sanitization query for Postgres and Mysql.
  633. $db_driver = $databases['default']['default']['driver'];
  634. if ($db_driver == 'pgsql') {
  635. $email_map = array('%uid' => "' || uid || '", '%mail' => "' || replace(mail, '@', '_') || '", '%name' => "' || replace(name, ' ', '_') || '");
  636. $newmail = "'" . str_replace(array_keys($email_map), array_values($email_map), $newemail) . "'";
  637. }
  638. elseif ($db_driver == 'mssql') {
  639. $email_map = array('%uid' => "' + uid + '", '%mail' => "' + replace(mail, '@', '_') + '", '%name' => "' + replace(name, ' ', '_') + '");
  640. $newmail = "'" . str_replace(array_keys($email_map), array_values($email_map), $newemail) . "'";
  641. }
  642. else {
  643. $email_map = array('%uid' => "', uid, '", '%mail' => "', replace(mail, '@', '_'), '", '%name' => "', replace(name, ' ', '_'), '");
  644. $newmail = "concat('" . str_replace(array_keys($email_map), array_values($email_map), $newemail) . "')";
  645. }
  646. $user_table_updates[] = "mail = $newmail, init = $newmail";
  647. }
  648. else {
  649. $user_table_updates[] = "mail = '$newemail', init = '$newemail'";
  650. }
  651. $message_list[] = 'email addresses';
  652. }
  653. if (!empty($user_table_updates)) {
  654. $table = $major_version >= 8 ? 'users_field_data' : 'users';
  655. if ($wrap_table_name) {
  656. $table = "{{$table}}";
  657. }
  658. $sanitize_query = "UPDATE {$table} SET " . implode(', ', $user_table_updates) . " WHERE uid > 0;";
  659. drush_sql_register_post_sync_op('user-email', dt('Reset !message in !table table', array('!message' => implode(' and ', $message_list), '!table' => $table)), $sanitize_query);
  660. }
  661. // Seems quite portable (SQLite?) - http://en.wikipedia.org/wiki/Truncate_(SQL)
  662. $table = 'sessions';
  663. if ($wrap_table_name) {
  664. $table = "{{$table}}";
  665. }
  666. $sql_sessions = "TRUNCATE TABLE {$table};";
  667. drush_sql_register_post_sync_op('sessions', dt('Truncate Drupal\'s sessions table'), $sql_sessions);
  668. }