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