/** * wipePrefix() * * Clear out tables matching supplied prefix. * * @return boolean Currently always true. */ function wipePrefix($prefix, $confirm = false) { if ($confirm !== true) { die('Error #5466566b: Parameter 2 to wipePrefix() must be boolean true to proceed.'); } if ($prefix == '') { pb_backupbuddy::status('warning', 'No database prefix specified to wipe.'); return false; } pb_backupbuddy::status('message', 'Beginning wipe of database tables matching prefix `' . $prefix . '`...'); // Connect to database. //$this->connect_database(); global $wpdb; $rows = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE table_name LIKE '" . backupbuddy_core::dbEscape(str_replace('_', '\\_', $prefix)) . "%' AND table_schema = DATABASE()", ARRAY_A); $table_wipe_count = count($rows); foreach ($rows as $row) { pb_backupbuddy::status('details', 'Dropping table `' . $row['table_name'] . '`.'); $wpdb->query('DROP TABLE `' . $row['table_name'] . '`'); } unset($rows); pb_backupbuddy::status('message', 'Wiped database of ' . $table_wipe_count . ' tables.'); return true; }
private function _dump_php($output_directory, $tables, $resume_starting_row = 0) { //, $base_dump_mode, $additional_excludes ) { $this->time_start = microtime(true); $last_file_size_output = microtime(true); $output_file_size_every_max = 5; // Output current SQL file size no more often than this. Only checks if it has been enough time once each burst of max rows per select is processed. $max_rows_per_select = $this->_max_rows_per_select; if ($resume_starting_row > 0) { pb_backupbuddy::status('details', 'Resuming chunked dump at row `' . $resume_starting_row . '`.'); } global $wpdb; if (!is_object($wpdb)) { pb_backupbuddy::status('error', 'WordPress database object $wpdb did not exist. This should not happen.'); error_log('WordPress database object $wpdb did not exist. This should not happen. BackupBuddy Error #8945587973.'); return false; } // Connect if not connected for importbuddy. if (defined('PB_IMPORTBUDDY')) { if (!mysql_ping($wpdb->dbh)) { $maybePort = ''; if ('' != $this->_database_port) { $maybePort = ':' . $this->_database_port; pb_backupbuddy::status('details', 'Using custom specified port `' . $this->_database_port . '` in DB_HOST.'); } $wpdb->dbh = mysql_connect($this->_database_host . $maybePort, $this->_database_user, $this->_database_pass); mysql_select_db($this->_database_name, $wpdb->dbh); } } $insert_sql = ''; pb_backupbuddy::status('details', 'Loading DB kicker for use leter in case database goes away.'); @(include_once pb_backupbuddy::plugin_path() . '/lib/wpdbutils/wpdbutils.php'); if (class_exists('pluginbuddy_wpdbutils')) { global $wpdb; $dbhelper = new pluginbuddy_wpdbutils($wpdb); } else { pb_backupbuddy::status('details', __('Database Server connection status will not be verified as kicker is not available.', 'it-l10n-backupbuddy')); } global $wpdb; // Used later for checking that we are still connected to DB. // Iterate through all the tables to backup. // TODO: Future ability to break up DB exporting to multiple page loads if needed. $remainingTables = $tables; foreach ($tables as $table_key => $table) { $_count = 0; $insert_sql = ''; if ($resume_starting_row > 0) { pb_backupbuddy::status('details', 'Chunked resume on dumping `' . $table . '`. Resuming where left off.'); $rows_start = $resume_starting_row; $resume_starting_row = 0; // Don't want to skip anything on next table. } else { $rows_start = 0; } pb_backupbuddy::status('details', 'Dumping database table `' . $table . '`. Max rows per select: ' . $max_rows_per_select . '. Starting at row `' . $resume_starting_row . '`.'); pb_backupbuddy::status('startTableDump', $table); if (false === $this->_force_single_db_file) { $output_file = $output_directory . $table . '.sql'; } else { pb_backupbuddy::status('details', 'Advanced option to force to single .sql file enabled.'); $output_file = $output_directory . 'db_1.sql'; } pb_backupbuddy::status('details', 'SQL dump file `' . $output_file . '`.'); if ('-1' == $this->_maxExecutionTime) { pb_backupbuddy::status('details', 'Database max execution time chunking disabled based on -1 passed.'); } else { pb_backupbuddy::status('details', 'mysqlbuddy: PHP-based database dump with max execution time for this run: ' . $this->_maxExecutionTime . ' seconds.'); } if (false === ($file_handle = fopen($output_file, 'a'))) { pb_backupbuddy::status('error', 'Error #9018: Database file is not creatable/writable. Check your permissions for file `' . $output_file . '` in directory `' . $output_directory . '`.'); return false; } pb_backupbuddy::status('sqlFile', basename($output_file)); // Tells status checker which file to request size data for when polling server. if (0 == $rows_start) { $create_table = $wpdb->get_results("SHOW CREATE TABLE `{$table}`", ARRAY_N); if ($create_table === false) { pb_backupbuddy::status('error', 'Unable to access and dump database table `' . $table . '`. Table may not exist. Skipping backup of this table.'); //backupbuddy_core::mail_error( 'Error #4537384: Unable to access and dump database table `' . $table . '`. Table may not exist. Skipping backup of this table.' ); continue; // Skip this iteration as accessing this table failed. } // Table creation text if (!isset($create_table[0])) { pb_backupbuddy::status('error', 'Error #857835: Unable to get table creation SQL for table `' . $table . '`. Result: `' . print_r($create_table) . '`.'); return false; } $create_table_array = $create_table[0]; unset($create_table); $insert_sql .= str_replace("\n", '', $create_table_array[1]) . ";\n"; // Remove internal linebreaks; only put one at end. unset($create_table_array); // Disable keys for this table. $insert_sql .= "/*!40000 ALTER TABLE `{$table}` DISABLE KEYS */;\n"; } $queryCount = 0; $rows_remain = true; while (true === $rows_remain) { // Row creation text for all rows within this table. $query = "SELECT * FROM `{$table}` LIMIT " . $rows_start . ',' . $max_rows_per_select; $table_query = $wpdb->get_results($query, ARRAY_N); $rows_start += $max_rows_per_select; // Next loop we will begin at this offset. if ($table_query === false) { pb_backupbuddy::status('error', 'ERROR #85449745. Unable to retrieve data from table `' . $table . '`. This table may be corrupt (try repairing the database) or too large to hold in memory (increase mysql and/or PHP memory). Check your PHP error log for further errors which may provide further information. Not continuing database dump to insure backup integrity.'); return false; } $tableCount = count($table_query); pb_backupbuddy::status('details', 'Got `' . $tableCount . '` rows from `' . $table . '` of `' . $max_rows_per_select . '` max.'); if (0 == $tableCount || $tableCount < $max_rows_per_select) { $rows_remain = false; } $columns = $wpdb->get_col_info(); $num_fields = count($columns); foreach ($table_query as $fetch_row) { $insert_sql .= "INSERT INTO `{$table}` VALUES("; for ($n = 1; $n <= $num_fields; $n++) { $m = $n - 1; if ($fetch_row[$m] === NULL) { $insert_sql .= "NULL, "; } else { $insert_sql .= "'" . backupbuddy_core::dbEscape($fetch_row[$m]) . "', "; } } $insert_sql = substr($insert_sql, 0, -2); $insert_sql .= ");\n"; $writeReturn = fwrite($file_handle, $insert_sql); if (false === $writeReturn || 0 == $writeReturn) { pb_backupbuddy::status('error', 'Error #843948: Unable to write to SQL file. Return error/bytes written: `' . $writeReturn . '`.'); @fclose($file_handle); return false; } $insert_sql = ''; $_count++; // Show a heartbeat to keep user up to date [and entertained ;)]. if (0 === $_count % self::HEARTBEAT_COUNT_LIMIT || 0 === $_count % ceil($max_rows_per_select / 2)) { // Display every X queries based on heartbeat OR at least display every half max rows per select. pb_backupbuddy::status('details', 'Working... Dumped `' . $_count . '` rows from `' . $table . '` so far.'); } } // end foreach table row. if (false === $rows_remain) { pb_backupbuddy::status('details', 'Dumped `' . $_count . '` rows total from `' . $table . '`. No rows remain.'); } else { if (microtime(true) - $last_file_size_output > $output_file_size_every_max) { // It's been long enough to get the current file size of SQL file. // Display final SQL file size. $sql_filesize = pb_backupbuddy::$format->file_size(filesize($output_file)); pb_backupbuddy::status('details', 'Current database dump file `' . basename($output_file) . '` size: ' . $sql_filesize . '.'); $last_file_size_output = microtime(true); } } // If we are within X seconds (self::TIME_WIGGLE_ROOM) of reaching maximum PHP runtime then stop here so that it can be picked up in another PHP process... if ('-1' != $this->_maxExecutionTime) { if (microtime(true) - pb_backupbuddy::$start_time + self::TIME_WIGGLE_ROOM >= $this->_maxExecutionTime) { // used to use $this->time_start but this did not take into account time used prior to db step. pb_backupbuddy::status('details', 'Approaching limit of available PHP chunking time of `' . $this->_maxExecutionTime . '` sec. PHP ran for ' . round(microtime(true) - pb_backupbuddy::$start_time, 3) . ' sec, database dumping ran for ' . round(microtime(true) - $this->time_start, 3) . ' sec having dumped `' . $_count . '` rows. Proceeding to use chunking on remaining tables: ' . implode(',', $remainingTables)); @fclose($file_handle); return array($remainingTables, $rows_start); } // End if. } // Verify database is still connected and working properly. Sometimes mysql runs out of memory and dies in the above foreach. // No point in reconnecting as we can NOT trust that our dump was succesful anymore (it most likely was not). if (isset($dbhelper)) { if (!$dbhelper->kick()) { pb_backupbuddy::status('error', __('ERROR #9026: The mySQL server went away unexpectedly during database dump of table `' . $table . '`. This is almost always caused by mySQL running out of memory. The backup integrity can no longer be guaranteed so the backup has been halted.') . ' ' . __('Last table dumped before database server went away: ') . '`' . $table . '`.'); @fclose($file_handle); return false; } } else { pb_backupbuddy::status('details', 'Database kicker unavailable so connection status unverified.'); } } // End while rows remain. // Remove the current table from the list of tables to dump as it is done. unset($remainingTables[$table_key]); // Re-enable keys for this table. $insert_sql .= "/*!40000 ALTER TABLE `{$table}` ENABLE KEYS */;\n"; $writeReturn = fwrite($file_handle, $insert_sql); if (false === $writeReturn || 0 == $writeReturn) { pb_backupbuddy::status('error', 'Error #843948: Unable to write to SQL file. Return error/bytes written: `' . $writeReturn . '`.'); @fclose($file_handle); return false; } $insert_sql = ''; @fclose($file_handle); pb_backupbuddy::status('details', 'Finished dumping database table `' . $table . '`.'); pb_backupbuddy::status('finishTableDump', $table); if (isset($output_file)) { $stats = stat($output_file); pb_backupbuddy::status('details', 'Database SQL dump file (' . basename($output_file) . ') size: ' . pb_backupbuddy::$format->file_size($stats['size'])); pb_backupbuddy::status('sqlSize', $stats['size']); } pb_backupbuddy::status('details', 'About to flush...'); pb_backupbuddy::flush(); //unset( $tables[$table_key] ); } // end foreach table. @fclose($file_handle); unset($file_handle); pb_backupbuddy::status('details', __('Finished PHP based SQL dump method. Ran for ' . round(microtime(true) - $this->time_start, 3) . ' sec.', 'it-l10n-backupbuddy')); return true; }
private function _priorRollbackCleanup() { $this->_before(__FUNCTION__); pb_backupbuddy::status('details', 'Checking for any prior failed rollback data to clean up.'); global $wpdb; $shortSerial = substr($this->_state['serial'], 0, 4); // NEW prefix $sql = "SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'BBnew-" . $shortSerial . "\\_%' AND table_schema = DATABASE()"; $results = $wpdb->get_results($sql, ARRAY_A); pb_backupbuddy::status('details', 'Found ' . count($results) . ' tables to drop with the prefix `BBnew-' . $shortSerial . '_`.'); $dropCount = 0; foreach ($results as $result) { if (false === $wpdb->query("DROP TABLE `" . backupbuddy_core::dbEscape($result['table_name']) . "`")) { $this->_error('Unable to delete table `' . $result['table_name'] . '`.'); } else { $dropCount++; } } pb_backupbuddy::status('details', 'Dropped `' . $dropCount . '` new tables.'); // OLD prefix $sql = "SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'BBold-" . $shortSerial . "\\_%' AND table_schema = DATABASE()"; $results = $wpdb->get_results($sql, ARRAY_A); pb_backupbuddy::status('details', 'Found ' . count($results) . ' tables to drop with the prefix `BBold-' . $shortSerial . '_`.'); $dropCount = 0; foreach ($results as $result) { if (false === $wpdb->query("DROP TABLE `" . backupbuddy_core::dbEscape($result['table_name']) . "`")) { $this->_error('Unable to delete table `' . $result['table_name'] . '`.'); } else { $dropCount++; } } pb_backupbuddy::status('details', 'Dropped `' . $dropCount . '` old tables.'); pb_backupbuddy::status('details', 'Finished prior rollback cleanup.'); }
function finalize() { // LASTLY UPDATE SITE/HOME URLS to prevent double replacement; just in case! if (!isset($this->restoreData['dat']['tables_sizes'][$this->restoreData['dat']['db_prefix'] . 'options'])) { pb_backupbuddy::status('details', 'Options table was not backed up. Skipping finalizing database URLs for _options table.'); } else { // Update SITEURL in options table. Usually mass replacement will cover this but set these here just in case. mysql_query("UPDATE `" . $this->overridePrefix . "options` SET option_value='" . backupbuddy_core::dbEscape($this->restoreData['siteurl']) . "' WHERE option_name='siteurl' LIMIT 1"); pb_backupbuddy::status('details', 'Modified ' . mysql_affected_rows() . ' row(s) while updating Site URL in options table `' . $this->overridePrefix . 'options` to `' . $this->restoreData['siteurl'] . '`.'); if (mysql_error() != '') { pb_backupbuddy::status('error', 'mysql error: ' . mysql_error()); } // Update HOME URL in options table. Usually mass replacement will cover this but set these here just in case. if ($this->restoreData['homeurl'] != '') { mysql_query("UPDATE `" . $this->overridePrefix . "options` SET option_value='" . backupbuddy_core::dbEscape($this->restoreData['homeurl']) . "' WHERE option_name='home' LIMIT 1"); pb_backupbuddy::status('details', 'Modified ' . mysql_affected_rows() . ' row(s) while updating Home URL in options table to `' . $this->restoreData['homeurl'] . '`.'); if (mysql_error() != '') { pb_backupbuddy::status('error', 'mysql error: ' . mysql_error()); } } } return true; }
public function import($sql_file, $old_prefix, $query_start = 0, $ignore_existing = false) { $return = false; // Require a new table prefix. if ($this->_database_prefix == '') { pb_backupbuddy::status('error', 'ERROR 9008: A database prefix is required for importing.'); } if ($query_start > 0) { pb_backupbuddy::status('message', 'Continuing to restore database dump starting at query ' . $query_start . '.'); } else { pb_backupbuddy::status('message', 'Restoring database dump. This may take a moment...'); } global $wpdb; // Check whether or not tables already exist that might collide. if ($ignore_existing === false) { if ($query_start == 0) { // Check number of tables already existing with this prefix. Skips this check on substeps of DB import. $rows = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE table_name LIKE '" . backupbuddy_core::dbEscape(str_replace('_', '\\_', $this->_database_prefix)) . "%' AND table_schema = DATABASE()", ARRAY_A); if (count($rows) > 0) { pb_backupbuddy::status('error', 'Error #9014: Database import halted to prevent overwriting existing WordPress data. The database already contains a WordPress installation with this prefix `' . $this->_database_prefix . '` (' . count($rows) . ' tables). Restore has been stopped to prevent overwriting existing data.'); return false; } unset($rows); } } pb_backupbuddy::status('message', 'Starting database import procedure.'); pb_backupbuddy::status('details', 'mysqlbuddy: Maximum execution time for this run: ' . $this->_maxExecutionTime . ' seconds.'); pb_backupbuddy::status('details', 'mysqlbuddy: Old prefix: `' . $old_prefix . '`; New prefix: `' . $this->_database_prefix . '`.'); pb_backupbuddy::status('details', "mysqlbuddy: Importing SQL file: `{$sql_file}`. Old prefix: `{$old_prefix}`. Query start: `{$query_start}`."); pb_backupbuddy::status('details', 'About to flush...'); pb_backupbuddy::flush(); // Attempt each method in order. pb_backupbuddy::status('details', 'Preparing to import using available method(s) by priority. Basing import methods off dump methods: `' . implode(',', $this->_methods) . '`'); foreach ($this->_methods as $method) { if (method_exists($this, "_import_{$method}")) { pb_backupbuddy::status('details', 'mysqlbuddy: Attempting import method `' . $method . '`.'); $result = call_user_func(array($this, "_import_{$method}"), $sql_file, $old_prefix, $query_start, $ignore_existing); if ($result === true) { // Dump completed succesfully with this method. pb_backupbuddy::status('details', 'mysqlbuddy: Import method `' . $method . '` completed successfully.'); $return = true; break; } elseif ($result === false) { // Dump failed this method. Will try compatibility fallback to next mode if able. // Do nothing. Will try next mode next loop. pb_backupbuddy::status('details', 'mysqlbuddy: Import method `' . $method . '` failed. Trying another compatibility mode next if able.'); } else { // Something else returned; used for resuming (integer) or a message (string). pb_backupbuddy::status('details', 'mysqlbuddy: Non-boolean response (usually means resume is needed): `' . $result . '`'); return $result; // Dont fallback if this happens. Usually means resume is needed to finish. } } } if ($return === true) { // Success. pb_backupbuddy::status('message', 'Database import procedure succeeded.'); return true; } else { // Overall failure. pb_backupbuddy::status('error', 'Database import procedure did not complete or failed.'); return false; } }
<?php backupbuddy_core::verifyAjaxAccess(); // Check db integrity of a table. /* db_check() * * Check database integrity on a specific table. Used on server info page. * * @return null */ $table = base64_decode(pb_backupbuddy::_GET('table')); $check_level = 'MEDIUM'; global $wpdb; pb_backupbuddy::$ui->ajax_header(); echo '<h2>Database Table Check</h2>'; echo 'Checking table `' . $table . '` using ' . $check_level . ' scan...<br><br>'; $rows = $wpdb->get_results("CHECK TABLE `" . backupbuddy_core::dbEscape($table) . "` " . $check_level, ARRAY_A); echo '<b>Results:</b><br><br>'; echo '<table class="widefat">'; foreach ($rows as $row) { echo '<tr>'; echo '<td>' . $row['Msg_type'] . '</td>'; echo '<td>' . $row['Msg_text'] . '</td>'; echo '</tr>'; } unset($rows); echo '</table>'; pb_backupbuddy::$ui->ajax_footer(); die;
public function db_repair() { $table = base64_decode(pb_backupbuddy::_GET('table')); global $wpdb; pb_backupbuddy::$ui->ajax_header(); echo '<h2>Database Table Repair</h2>'; echo 'Repairing table `' . $table . '`...<br><br>'; $rows = $wpdb->get_results("REPAIR TABLE `" . backupbuddy_core::dbEscape($table) . "`", ARRAY_A); echo '<b>Results:</b><br><br>'; echo '<table class="widefat">'; foreach ($rows as $row) { echo '<tr>'; echo '<td>' . $row['Msg_type'] . '</td>'; echo '<td>' . $row['Msg_text'] . '</td>'; echo '</tr>'; } unset($rows); echo '</table>'; pb_backupbuddy::$ui->ajax_footer(); die; }
<?php if (!is_admin()) { die('Access denied.'); } // Repair db integrity of a table. /* db_repair() * * Repair specific table. Used on server info page. * * @return null */ $table = base64_decode(pb_backupbuddy::_GET('table')); global $wpdb; pb_backupbuddy::$ui->ajax_header(); echo '<h2>Database Table Repair</h2>'; echo 'Repairing table `' . $table . '`...<br><br>'; $rows = $wpdb->get_results("REPAIR TABLE `" . backupbuddy_core::dbEscape($table) . "`", ARRAY_A); echo '<b>Results:</b><br><br>'; echo '<table class="widefat">'; foreach ($rows as $row) { echo '<tr>'; echo '<td>' . $row['Msg_type'] . '</td>'; echo '<td>' . $row['Msg_text'] . '</td>'; echo '</tr>'; } unset($rows); echo '</table>'; pb_backupbuddy::$ui->ajax_footer(); die;
private static function _render_insert($table_no_prefix, $column, $value) { global $wpdb; $table = $wpdb->prefix . $table_no_prefix; $query = "SELECT * FROM `{$table}` WHERE {$column} = %s"; $table_query = $wpdb->get_results($wpdb->prepare($query, array($value)), ARRAY_N); if ($table_query === false) { pb_backupbuddy::status('error', 'ERROR #237273. Unable to retrieve data from table `' . $table . '`. This table may be corrupt (try repairing the database) or too large to hold in memory (increase mysql and/or PHP memory). Check your PHP error log for further errors which may provide further information. Not continuing database dump to insure backup integrity.'); return false; } $tableCount = count($table_query); pb_backupbuddy::status('details', 'Got `' . $tableCount . '` rows from `' . $table . '`.'); $insert_sql = $wpdb->prepare("DELETE FROM `{$table}` WHERE {$column} = %s", array($value)) . ";\n"; // Initially delete everything that matches this query to get ready for insert. $columns = $wpdb->get_col_info(); $num_fields = count($columns); foreach ($table_query as $fetch_row) { $insert_sql .= "INSERT INTO `{$table}` VALUES("; for ($n = 1; $n <= $num_fields; $n++) { $m = $n - 1; if ($fetch_row[$m] === NULL) { $insert_sql .= "NULL, "; } else { $insert_sql .= "'" . backupbuddy_core::dbEscape($fetch_row[$m]) . "', "; } } $insert_sql = substr($insert_sql, 0, -2); $insert_sql .= ");\n"; /* $writeReturn = fwrite( $file_handle, $insert_sql ); if ( ( false === $writeReturn ) || ( 0 == $writeReturn ) ) { pb_backupbuddy::status( 'error', 'Error #549546: Unable to write to SQL file. Return error/bytes written: `' . $writeReturn . '`.' ); @fclose( $file_handle ); return false; } */ //$insert_sql = ''; } // end foreach table row. return $insert_sql; }
$rows_changed = 0; foreach ($tables as $table) { pb_backupbuddy::status('message', 'Replacing in table `' . $table . '`.'); $rows_changed += $dbreplace->bruteforce_table($table, array($needle), array($replacement)); } pb_backupbuddy::status('message', 'Total rows updated across all tables: ' . $rows_changed . '.'); pb_backupbuddy::status('message', 'Replacement finished.'); } elseif (pb_backupbuddy::_POST('table_selection') == 'single_table') { $table = backupbuddy_core::dbEscape(pb_backupbuddy::_POST('table')); // Single specified table. pb_backupbuddy::status('message', 'Replacing in single table `' . $table . '` based on settings.'); $dbreplace->bruteforce_table($table, array($needle), array($replacement)); pb_backupbuddy::status('message', 'Replacement finished.'); } elseif (pb_backupbuddy::_POST('table_selection') == 'prefix') { // Matching table prefix. $prefix = backupbuddy_core::dbEscape(pb_backupbuddy::_POST('table_prefix')); pb_backupbuddy::status('message', 'Replacing in all tables matching prefix `' . $prefix . '`.'); $tables = array(); $escaped_prefix = str_replace('_', '\\_', $prefix); $rows = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE table_name LIKE '{$escaped_prefix}%' AND table_schema = DATABASE()", ARRAY_A); foreach ($rows as $row) { $tables[] = $row['table_name']; } unset($rows); $rows_changed = 0; foreach ($tables as $table) { pb_backupbuddy::status('message', 'Replacing in table `' . $table . '`.'); $rows_changed += $dbreplace->bruteforce_table($table, array($needle), array($replacement)); } pb_backupbuddy::status('message', 'Total rows updated across all tables: ' . $rows_changed . '.'); pb_backupbuddy::status('message', 'Replacement finished.');
/** * bruteforce_table() * * !!! HANDLES SERIALIZED DATA !!!! * Replaces text, serialized or not, within the entire table. Bruteforce method iterates through every row & column in the entire table and replaces if needed. * * @param string $table Text (possibly serialized) to update. * @param mixed $olds Text to search for to replace. May be an array of strings to search for. * @param mixed $news New value(s) to be replaced with. May be a string or array. If array there must be the same number of values as $olds. * @return int Number of rows changed. * */ function bruteforce_table($table, $olds, $news, $rows_start = '') { pb_backupbuddy::status('message', 'Starting brute force data migration for table `' . $table . '`...'); if (!is_array($olds)) { $olds = array($olds); } if (!is_array($news)) { $news = array($news); } $count_items_checked = 0; $count_items_changed = 0; global $wpdb; // Get the total row count for this table $total_rows = 0; $tables_status = $wpdb->get_results("SHOW TABLE STATUS", ARRAY_A); foreach ($tables_status as $table_status) { if ($table === $table_status['Name']) { // Fix up row count and average row length for InnoDB engine which returns inaccurate // (and changing) values for these if ('InnoDB' === $table_status['Engine']) { if (false !== ($count = $wpdb->get_var("SELECT COUNT(1) FROM `{$table_status['Name']}`"))) { $table_status['Rows'] = $count; } } $total_rows = $table_status['Rows']; } } $fields = $wpdb->get_results("DESCRIBE `{$table}`", ARRAY_A); $index_fields = ''; // Reset fields for each table. $column_name = ''; $table_index = ''; $i = 0; $found_primary_key = false; foreach ($fields as $field) { $column_name[$i++] = $field['Field']; if ($field['Key'] == 'PRI') { $table_index[$i] = true; $found_primary_key = true; } } // Skips migration of this table if there is no primary key. Modifying on any other key is not safe. mysql automatically returns a PRIMARY if a UNIQUE non-primary is found according to http://dev.mysql.com/doc/refman/5.1/en/create-table.html @since 2.2.32. if ($found_primary_key === false) { pb_backupbuddy::status('warning', 'Error #9029b: Warning only! Table `' . $table . '` does not contain a primary key; BackupBuddy cannot safely modify the contents of this table. Skipping migration of this table. (bruteforce_table()).'); return true; } $row_loop = 0; $rows_remain = true; // More rows remaining / aka another query for more rows needed. if ('' == $rows_start) { $rows_start = 0; } while (true === $rows_remain) { // Keep looping through rows until none remain. Looping through like this to limit memory usage as wpdb classes loads all results into memory. $data = $wpdb->get_results("SELECT * FROM `{$table}` LIMIT {$rows_start}," . self::MAX_ROWS_PER_SELECT, ARRAY_A); if (false === $data) { pb_backupbuddy::status('error', 'ERROR #44545343 ... SQL ERROR: ' . $wpdb->last_error); } // Provide an update on progress $rowsCount = count($data); if (0 === $rowsCount) { pb_backupbuddy::status('details', 'Table: `' . $table . '` - processing ' . $rowsCount . ' rows ( of ' . $total_rows . ' )'); } else { pb_backupbuddy::status('details', 'Table: `' . $table . '` - processing ' . $rowsCount . ' rows ( Rows ' . $rows_start . '-' . ($rows_start + $rowsCount - 1) . ' of ' . $total_rows . ' )'); } $rows_start += self::MAX_ROWS_PER_SELECT; // Next loop we will begin at this offset. if (0 == $rowsCount || $rowsCount < self::MAX_ROWS_PER_SELECT) { $rows_remain = false; } foreach ($data as $row) { $need_to_update = false; $UPDATE_SQL = 'UPDATE `' . $table . '` SET '; $WHERE_SQL = ' WHERE '; $j = 0; foreach ($column_name as $current_column) { $j++; $count_items_checked++; $data_to_fix = $row[$current_column]; if (false !== ($edited_data = $this->replace_maybe_serialized($data_to_fix, $olds, $news))) { // no change needed $count_items_changed++; if ($need_to_update != false) { // If this isn't our first time here, we need to add a comma. $UPDATE_SQL = $UPDATE_SQL . ','; } $UPDATE_SQL = $UPDATE_SQL . ' ' . $current_column . ' = "' . backupbuddy_core::dbEscape($edited_data) . '"'; $need_to_update = true; // Only set if we need to update - avoids wasted UPDATE statements. } if (isset($table_index[$j])) { $WHERE_SQL = $WHERE_SQL . '`' . $current_column . '` = "' . $row[$current_column] . '" AND '; } } if ($need_to_update) { $WHERE_SQL = substr($WHERE_SQL, 0, -4); // Strip off the excess AND - the easiest way to code this without extra flags, etc. $UPDATE_SQL = $UPDATE_SQL . $WHERE_SQL; $result = $wpdb->query($UPDATE_SQL); if (false === $result) { pb_backupbuddy::status('error', 'ERROR: mysql error updating db: ' . mysql_error() . '. SQL Query: ' . htmlentities($UPDATE_SQL)); } } } unset($data); // See how we are doing on time. Trigger chunking if needed. if (microtime(true) - $this->startTime + $this->timeWiggleRoom >= $this->maxExecutionTime) { return array($rows_start); } } pb_backupbuddy::status('message', 'Brute force data migration for table `' . $table . '` complete. Checked ' . $count_items_checked . ' items; ' . $count_items_changed . ' changed.'); return true; }
if (mysql_error() != '') { pb_backupbuddy::status('error', 'mysql error: ' . mysql_error()); } mysql_query("UPDATE `" . $new_prefix . "options` SET option_name = '" . $new_prefix . "user_roles' WHERE option_name ='" . $old_prefix . "user_roles' LIMIT 1"); pb_backupbuddy::status('details', 'Modified ' . mysql_affected_rows() . ' row(s) while updating option_name user_roles DB prefix in [subsite if multisite] options table to `' . backupbuddy_core::dbEscape($new_prefix) . '`.'); if (mysql_error() != '') { pb_backupbuddy::status('error', 'mysql error: ' . mysql_error()); } pb_backupbuddy::status('message', 'Updated prefix META data.'); } // LASTLY UPDATE SITE/HOME URLS to prevent double replacement; just in case! // Update SITEURL in options table. Usually mass replacement will cover this but set these here just in case. mysql_query("UPDATE `" . $destination_db_prefix . "options` SET option_value='" . backupbuddy_core::dbEscape($destination_siteurl) . "' WHERE option_name='siteurl' LIMIT 1"); pb_backupbuddy::status('details', 'Modified ' . mysql_affected_rows() . ' row(s) while updating Site URL in options table `' . $destination_db_prefix . 'options` to `' . $destination_siteurl . '`.'); if (mysql_error() != '') { pb_backupbuddy::status('error', 'mysql error: ' . mysql_error()); } // Update HOME URL in options table. Usually mass replacement will cover this but set these here just in case. if ($destination_home != '') { mysql_query("UPDATE `" . $destination_db_prefix . "options` SET option_value='" . backupbuddy_core::dbEscape($destination_home) . "' WHERE option_name='home' LIMIT 1"); pb_backupbuddy::status('details', 'Modified ' . mysql_affected_rows() . ' row(s) while updating Home URL in options table to `' . $destination_home . '`.'); if (mysql_error() != '') { pb_backupbuddy::status('error', 'mysql error: ' . mysql_error()); } } pb_backupbuddy::status('message', 'Migrated ' . count($bruteforce_tables) . ' tables via brute force.'); pb_backupbuddy::status('message', 'Took ' . round(microtime(true) - pb_backupbuddy::$start_time, 3) . ' seconds. Done.'); pb_backupbuddy::status('message', 'Database content migrated.'); $return = true; // Needed for importbuddy since the following return does not trigger since it's in an include. return true;
public static function remove_temp_tables($forceSerial = '') { global $wpdb; if ('' != $forceSerial) { $cleanups = array($forceSerial => 0); } else { $cleanups = pb_backupbuddy::$options['rollback_cleanups']; } foreach ($cleanups as $cleanup_serial => $start_time) { if (time() - $start_time > backupbuddy_constants::CLEANUP_MAX_STATUS_LOG_AGE) { $results = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE ( ( table_name LIKE 'bbnew-" . substr($cleanup_serial, 0, 4) . "\\_%' ) OR ( table_name LIKE 'bbold-" . substr($cleanup_serial, 0, 4) . "\\_%' ) ) AND table_schema = DATABASE()", ARRAY_A); if (count($results) > 0) { foreach ($results as $result) { if (false === $wpdb->query("DROP TABLE `" . backupbuddy_core::dbEscape($result['table_name']) . "`")) { return $this->_error('Error #372837683: Unable to copy over BackupBuddy settings from live site to incoming database in temp table. Details: `' . $wpdb->last_error . '`.'); pb_backupbuddy::status('details', 'Error #83947944: Unable to drop temp rollback/deploy table `' . $result['table_name'] . '`. Details: `' . $wpdb->last_error . '`.'); } } } unset(pb_backupbuddy::$options['rollback_cleanups'][$cleanup_serial]); pb_backupbuddy::save(); } } // end foreach. // Delete any undo PHP files. $undoFiles = glob(ABSPATH . 'backupbuddy_deploy_undo-*.php'); if (!is_array($undoFiles)) { $undoFiles = array(); } foreach ($undoFiles as $undoFile) { @unlink($undoFile); } return; }
if (!isset($restore->_state['databaseSettings']['importResumeFiles']) && '' == $restore->_state['databaseSettings']['importResumePoint']) { // Only do this if not in process of resuming. pb_backupbuddy::status('message', 'Wiping ALL existing database tables based on settings (use with caution)...'); if (TRUE !== pb_backupbuddy::$classes['import']->wipeDatabase(TRUE)) { pb_backupbuddy::status('error', 'Unable to wipe entire database as configured in the settings.'); } } } // Restore the database. if ('true' == pb_backupbuddy::_GET('deploy')) { // Drop any previous incomplete deployment / rollback tables. pb_backupbuddy::status('details', 'Dropping any existing temporary deployment or rollback tables.'); $results = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE ( ( table_name LIKE 'bbnew-\\_%' ) OR ( table_name LIKE 'bbold-\\_%' ) ) AND table_schema = DATABASE()", ARRAY_A); if (count($results) > 0) { foreach ($results as $result) { if (false === $wpdb->query("DROP TABLE `" . backupbuddy_core::dbEscape($result['table_name']) . "`")) { return $this->_error('Error #372837683: Unable to copy over BackupBuddy settings from live site to incoming database in temp table. Details: `' . $wpdb->last_error . '`.'); pb_backupbuddy::status('details', 'Error #8493984: Unable to drop temp rollback/deploy table `' . $result['table_name'] . '`. Details: `' . $wpdb->last_error . '`.'); } } } $restore->_state['databaseSettings']['tempPrefix'] = 'bbnew-' . substr($restore->_state['serial'], 0, 4) . '_' . $restore->_state['databaseSettings']['prefix']; } pb_backupbuddy::status('details', 'About to restore database.'); $restoreResult = $restore->restoreDatabase($restore->_state['databaseSettings']['tempPrefix']); if ('true' == pb_backupbuddy::_GET('deploy')) { if (is_array($restoreResult)) { // Chunking. Resume same step. $nextStepNum = 4; } else { // Next step.
// Update comment author IDs with the user's new user ID. pb_backupbuddy::status('details', 'Updating comment author ID from old user ID `' . $old_user_id . '` to new ID `' . $user_id . '` in table `' . $new_db_prefix . 'comments`.'); $rows_updated = $wpdb->update($new_db_prefix . 'comments', array('user_id' => absint($user_id)), array('user_id' => $old_user_id), array('%d')); pb_backupbuddy::status('details', 'Row(s) modified: `' . $rows_updated . '`.'); } // Handle usermeta. $sql = "select meta_key,meta_value from `{$new_db_prefix}usermeta` WHERE user_id={$old_user_id} AND meta_key NOT LIKE '%\\_capabilities'"; pb_backupbuddy::status('details', 'Getting usermeta data. SQL query: `' . $sql . '`.'); $usermetas = $wpdb->get_results($sql); // Users to import. if (is_array($usermetas)) { pb_backupbuddy::status('message', 'Found usermeta data to migrate for this user (old ID: `' . $old_user_id . '`). Importing & migrating...'); $user_meta_rows_count = 0; foreach ($usermetas as $usermeta) { $meta_key = backupbuddy_core::dbEscape($usermeta->meta_key); $meta_value = backupbuddy_core::dbEscape($usermeta->meta_value); $sql = "INSERT INTO `{$wpdb->base_prefix}usermeta` (user_id,meta_key,meta_value) VALUES('{$user_id}','{$meta_key}','{$meta_value}');"; pb_backupbuddy::status('details', 'Copying usermeta row. SQL query: `' . $sql . '`.'); $rows_modified = $wpdb->query($sql); // $wpdb->base_prefix gives the network prefix. pb_backupbuddy::status('details', 'Row(s) modified: `' . $rows_modified . '`.'); $user_meta_rows_count++; } pb_backupbuddy::status('details', 'Copied and migrated `' . $user_meta_rows_count . '` usermeta rows.'); } } else { // User already exists. pb_backupbuddy::status('warning', 'Username `' . $user->user_login . '` or email `' . $user->user_email . '` already exists with user ID `' . $user_id . '`. User skipped.'); $users_skipped++; } }