5 use Drupal\Core\Database\Database;
6 use Drush\Log\LogLevel;
7 use Webmozart\PathUtil\Path;
11 // An Drupal style array containing specs for connecting to database.
14 // Default code appended to sql-query connections.
15 public $query_extra = '';
17 // The way you pass a sql file when issueing a query.
18 public $query_file = '<';
21 * Typically, SqlBase objects are contructed via drush_sql_get_class().
23 public function __construct($db_spec = NULL) {
24 $this->db_spec = $db_spec;
28 * Get the current $db_spec.
30 public function db_spec() {
31 return $this->db_spec;
35 * The unix command used to connect to the database.
38 public function command() {}
41 * A string for connecting to a database.
43 * @param bool $hide_password
44 * If TRUE, DBMS should try to hide password from process list.
45 * On mysql, that means using --defaults-extra-file to supply the user+password.
49 public function connect($hide_password = TRUE) {
50 return trim($this->command() . ' ' . $this->creds($hide_password) . ' ' . drush_get_option('extra', $this->query_extra));
55 * Execute a SQL dump and return the path to the resulting dump file.
57 * @param string|bool @file
58 * The path where the dump file should be stored. If TRUE, generate a path
59 * based on usual backup directory and current date.
61 public function dump($file = '') {
63 $table_selection = $this->get_expanded_table_selection();
64 $file = $this->dumpFile($file);
65 $cmd = $this->dumpCmd($table_selection);
66 // Gzip the output from dump command(s) if requested.
67 if (drush_get_option('gzip')) {
69 $file_suffix .= '.gz';
72 $file .= $file_suffix;
73 $cmd .= ' > ' . drush_escapeshellarg($file);
76 // Avoid the php memory of the $output array in drush_shell_exec().
77 if (!$return = drush_op_system($cmd)) {
79 drush_log(dt('Database dump saved to !path', array('!path' => $file)), LogLevel::SUCCESS);
80 drush_backend_set_result($file);
84 return drush_set_error('DRUSH_SQL_DUMP_FAIL', 'Database dump failed');
89 * Build bash for dumping a database.
91 * @param array $table_selection
92 * Supported keys: 'skip', 'structure', 'tables'.
94 * One or more mysqldump/pg_dump/sqlite3/etc statements that are ready for executing.
95 * If multiple statements are needed, enclose in parenthesis.
97 public function dumpCmd($table_selection) {}
100 * Generate a path to an output file for a SQL dump when needed.
102 * @param string|bool @file
103 * If TRUE, generate a path based on usual backup directory and current date.
104 * Otherwise, just return the path that was provided.
106 public function dumpFile($file) {
107 $database = $this->db_spec['database'];
109 // $file is passed in to us usually via --result-file. If the user
110 // has set $options['result-file'] = TRUE, then we
111 // will generate an SQL dump file in the same backup
112 // directory that pm-updatecode uses.
114 if ($file === TRUE) {
115 // User did not pass a specific value for --result-file. Make one.
116 $backup = drush_include_engine('version_control', 'backup');
117 $backup_dir = $backup->prepare_backup_dir($database);
118 if (empty($backup_dir)) {
119 $backup_dir = drush_find_tmp();
121 $file = Path::join($backup_dir, '@DATABASE_@DATE.sql');
123 $file = str_replace(array('@DATABASE', '@DATE'), array($database, gmdate('Ymd_His')), $file);
129 * Execute a SQL query.
131 * Note: This is an API function. Try to avoid using drush_get_option() and instead
132 * pass params in. If you don't want to query results to print during --debug then
133 * provide a $result_file whose value can be drush_bit_bucket().
135 * @param string $query
136 * The SQL to be executed. Should be NULL if $input_file is provided.
137 * @param string $input_file
138 * A path to a file containing the SQL to be executed.
139 * @param string $result_file
140 * A path to save query results to. Can be drush_bit_bucket() if desired.
143 * TRUE on success, FALSE on failure
145 public function query($query, $input_file = NULL, $result_file = '') {
146 $input_file_original = $input_file;
147 if ($input_file && drush_file_is_tarball($input_file)) {
148 if (drush_shell_exec('gzip -d %s', $input_file)) {
149 $input_file = trim($input_file, '.gz');
152 return drush_set_error(dt('Failed to decompress input file.'));
156 // Save $query to a tmp file if needed. We will redirect it in.
158 $query = $this->query_prefix($query);
159 $query = $this->query_format($query);
160 $input_file = drush_save_data_to_temp_file($query);
166 $this->silent(), // This removes column header and various helpful things in mysql.
167 drush_get_option('extra', $this->query_extra),
169 drush_escapeshellarg($input_file),
171 $exec = implode(' ', $parts);
174 $exec .= ' > '. drush_escapeshellarg($result_file);
177 // In --verbose mode, drush_shell_exec() will show the call to mysql/psql/sqlite,
178 // but the sql query itself is stored in a temp file and not displayed.
179 // We show the query when --debug is used and this function created the temp file.
180 if ((drush_get_context('DRUSH_DEBUG') || drush_get_context('DRUSH_SIMULATE')) && empty($input_file_original)) {
181 drush_log('sql-query: ' . $query, LogLevel::NOTICE);
184 $success = drush_shell_exec($exec);
186 if ($success && drush_get_option('file-delete')) {
187 drush_op('drush_delete_dir', $input_file);
194 * A string to add to the command when queries should not print their results.
196 public function silent() {}
199 public function query_prefix($query) {
200 // Inject table prefixes as needed.
201 if (drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_DATABASE)) {
202 // Enable prefix processing which can be dangerous so off by default. See http://drupal.org/node/1219850.
203 if (drush_get_option('db-prefix')) {
204 if (drush_drupal_major_version() >= 7) {
205 $query = Database::getConnection()->prefixTables($query);
208 $query = db_prefix_tables($query);
216 public function query_format($query) {
221 * Drop specified database.
223 * @param array $tables
224 * An array of table names
226 * True if successful, FALSE if failed.
228 public function drop($tables) {
231 $sql = 'DROP TABLE '. implode(', ', $tables);
232 $return = $this->query($sql);
238 * Build a SQL string for dropping and creating a database.
240 * @param string dbname
242 * @param boolean $quoted
243 * Quote the database name. Mysql uses backticks to quote which can cause problems
244 * in a Windows shell. Set TRUE if the CREATE is not running on the bash command line.
246 public function createdb_sql($dbname, $quoted = FALSE) {}
249 * Create a new database.
251 * @param boolean $quoted
252 * Quote the database name. Mysql uses backticks to quote which can cause problems
253 * in a Windows shell. Set TRUE if the CREATE is not running on the bash command line.
255 * True if successful, FALSE otherwise.
257 public function createdb($quoted = FALSE) {
258 $dbname = $this->db_spec['database'];
259 $sql = $this->createdb_sql($dbname, $quoted);
260 // Adjust connection to allow for superuser creds if provided.
262 return $this->query($sql);
266 * Drop all tables (if DB exists) or CREATE target database.
269 * TRUE or FALSE depending on success.
271 public function drop_or_create() {
272 if ($this->db_exists()) {
273 return $this->drop($this->listTables());
276 return $this->createdb();
281 * Determine if the specified DB already exists.
285 public function db_exists() {}
287 public function delete() {}
290 * Build a fragment connection parameters.
292 * @param bool $hide_password
293 * If TRUE, DBMS should try to hide password from process list.
294 * On mysql, that means using --defaults-extra-file to supply the user+password.
297 public function creds($hide_password = TRUE) {}
300 * The active database driver.
303 public function scheme() {
304 return $this->db_spec['driver'];
308 * Get a list of all table names and expand input that may contain
309 * wildcards (`*`) if necessary so that the array returned only contains valid
310 * table names i.e. actual tables that exist, without a wildcard.
313 * An array of tables with each table name in the appropriate
314 * element of the array.
316 public function get_expanded_table_selection() {
317 $table_selection = drush_sql_get_table_selection();
318 // Get the existing table names in the specified database.
319 $db_tables = $this->listTables();
320 if (isset($table_selection['skip'])) {
321 $table_selection['skip'] = _drush_sql_expand_and_filter_tables($table_selection['skip'], $db_tables);
323 if (isset($table_selection['structure'])) {
324 $table_selection['structure'] = _drush_sql_expand_and_filter_tables($table_selection['structure'], $db_tables);
326 if (isset($table_selection['tables'])) {
327 $table_selection['tables'] = _drush_sql_expand_and_filter_tables($table_selection['tables'], $db_tables);
329 return $table_selection;
333 * Extract the name of all existing tables in the given database.
336 * An array of table names which exist in the current database.
338 public function listTables() {}
341 * Helper method to turn associative array into options with values.
346 public function params_to_options($parameters) {
347 // Turn each parameter into a valid parameter string.
348 $parameter_strings = array();
349 foreach ($parameters as $key => $value) {
350 // Only escape the values, not the keys or the rest of the string.
351 $value = drush_escapeshellarg($value);
352 $parameter_strings[] = "--$key=$value";
355 // Join the parameters and return.
356 return implode(' ', $parameter_strings);
360 * Adjust DB connection with superuser credentials if provided.
362 * The options 'db-su' and 'db-su-pw' will be retreived from the
363 * specified site alias record, if it exists and contains those items.
364 * If it does not, they will be fetched via drush_get_option.
366 * Note that in the context of sql-sync, the site alias record will
367 * be taken from the target alias (e.g. `drush sql-sync @source @target`),
368 * which will be overlayed with any options that begin with 'target-';
369 * therefore, the commandline options 'target-db-su' and 'target-db-su-pw'
370 * may also affect the operation of this function.
374 public function su() {
375 $create_db_target = $this->db_spec;
377 $create_db_target['database'] = '';
378 $db_superuser = drush_get_option('db-su');
379 if (isset($db_superuser)) {
380 $create_db_target['username'] = $db_superuser;
382 $db_su_pw = drush_get_option('db-su-pw');
383 // If --db-su-pw is not provided and --db-su is, default to empty password.
384 // This way db cli command will take password from .my.cnf or .pgpass.
385 if (!empty($db_su_pw)) {
386 $create_db_target['password'] = $db_su_pw;
388 elseif (isset($db_superuser)) {
389 unset($create_db_target['password']);
391 $this->db_spec = $create_db_target;