Edit File by line
/home/barbar84/www/wp-conte.../plugins/updraftp...
File: backup.php
global $updraftplus;
[2000] Fix | Delete
$description = $via_count ? 'via COUNT' : 'approximate';
[2001] Fix | Delete
$updraftplus->log("Table $table: Total expected rows ($description): ".$expected_rows);
[2002] Fix | Delete
$this->stow("# Approximate rows expected in table: $expected_rows\n");
[2003] Fix | Delete
if ($expected_rows > UPDRAFTPLUS_WARN_DB_ROWS) {
[2004] Fix | Delete
$this->many_rows_warning = true;
[2005] Fix | Delete
$updraftplus->log(sprintf(__("Table %s has very many rows (%s) - we hope your web hosting company gives you enough resources to dump out that table in the backup.", 'updraftplus'), $table, $expected_rows).' '.__('If not, you will need to either remove data from this table, or contact your hosting company to request more resources.', 'updraftplus'), 'warning', 'manyrows_'.$this->whichdb_suffix.$table);
[2006] Fix | Delete
}
[2007] Fix | Delete
}
[2008] Fix | Delete
[2009] Fix | Delete
/**
[2010] Fix | Delete
* This function will return a SQL WHERE clause to exclude updraft jobdata
[2011] Fix | Delete
*
[2012] Fix | Delete
* @param array $where - an array of where clauses to add to
[2013] Fix | Delete
* @param string $table - the table we want to add a where clause for
[2014] Fix | Delete
*
[2015] Fix | Delete
* @return array - returns an array of where clauses for the table
[2016] Fix | Delete
*/
[2017] Fix | Delete
public function backup_exclude_jobdata($where, $table) {
[2018] Fix | Delete
// Don't include the job data for any backups - so that when the database is restored, it doesn't continue an apparently incomplete backup
[2019] Fix | Delete
global $updraftplus;
[2020] Fix | Delete
$table_prefix = $updraftplus->get_table_prefix(false); // or we can just use $this->table_prefix_raw ??
[2021] Fix | Delete
if ('wp' == $this->whichdb && (!empty($table_prefix) && strtolower($table_prefix.'sitemeta') == strtolower($table))) {
[2022] Fix | Delete
$where[] = 'meta_key NOT LIKE "updraft_jobdata_%"';
[2023] Fix | Delete
} elseif ('wp' == $this->whichdb && (!empty($table_prefix) && strtolower($table_prefix.'options') == strtolower($table))) {
[2024] Fix | Delete
// These might look similar, but the quotes are different
[2025] Fix | Delete
if ('win' == strtolower(substr(PHP_OS, 0, 3))) {
[2026] Fix | Delete
$updraft_jobdata = "'updraft_jobdata_%'";
[2027] Fix | Delete
$site_transient_update = "'_site_transient_update_%'";
[2028] Fix | Delete
} else {
[2029] Fix | Delete
$updraft_jobdata = '"updraft_jobdata_%"';
[2030] Fix | Delete
$site_transient_update = '"_site_transient_update_%"';
[2031] Fix | Delete
}
[2032] Fix | Delete
[2033] Fix | Delete
$where[] = 'option_name NOT LIKE '.$updraft_jobdata.' AND option_name NOT LIKE '.$site_transient_update.'';
[2034] Fix | Delete
}
[2035] Fix | Delete
[2036] Fix | Delete
return $where;
[2037] Fix | Delete
}
[2038] Fix | Delete
[2039] Fix | Delete
/**
[2040] Fix | Delete
* Produce a dump of the table using a mysqldump binary
[2041] Fix | Delete
*
[2042] Fix | Delete
* @param String $potsql - the path to the mysqldump binary
[2043] Fix | Delete
* @param String $table_name - the name of the table being dumped
[2044] Fix | Delete
*
[2045] Fix | Delete
* @return Boolean - success status
[2046] Fix | Delete
*/
[2047] Fix | Delete
private function backup_table_bindump($potsql, $table_name) {
[2048] Fix | Delete
[2049] Fix | Delete
$microtime = microtime(true);
[2050] Fix | Delete
[2051] Fix | Delete
global $updraftplus, $wpdb;
[2052] Fix | Delete
[2053] Fix | Delete
// Deal with Windows/old MySQL setups with erroneous table prefixes differing in case
[2054] Fix | Delete
// Can't get binary mysqldump to make this transformation
[2055] Fix | Delete
// $dump_as_table = ($this->duplicate_tables_exist == false && stripos($table, $this->table_prefix) === 0 && strpos($table, $this->table_prefix) !== 0) ? $this->table_prefix.substr($table, strlen($this->table_prefix)) : $table;
[2056] Fix | Delete
[2057] Fix | Delete
$pfile = md5(time().rand()).'.tmp';
[2058] Fix | Delete
file_put_contents($this->updraft_dir.'/'.$pfile, "[mysqldump]\npassword=\"".addslashes($this->dbinfo['pass'])."\"\n");
[2059] Fix | Delete
[2060] Fix | Delete
$where_array = apply_filters('updraftplus_backup_table_sql_where', array(), $table_name, $this);
[2061] Fix | Delete
if ('win' === strtolower(substr(PHP_OS, 0, 3))) {
[2062] Fix | Delete
// On Windows, the PHP escapeshellarg() replaces % char with white space, so we change the % char to [percent_sign] but change it back later after escapeshellarg finish processsing it
[2063] Fix | Delete
$where_array = str_replace('%', '[percent_sign]', $where_array);
[2064] Fix | Delete
}
[2065] Fix | Delete
$where = '';
[2066] Fix | Delete
[2067] Fix | Delete
if (!empty($where_array) && is_array($where_array)) {
[2068] Fix | Delete
// N.B. Don't add a WHERE prefix here; most versions of mysqldump silently strip it out, but one was encountered that didn't.
[2069] Fix | Delete
$first_loop = true;
[2070] Fix | Delete
foreach ($where_array as $condition) {
[2071] Fix | Delete
if (!$first_loop) $where .= " AND ";
[2072] Fix | Delete
$where .= $condition;
[2073] Fix | Delete
$first_loop = false;
[2074] Fix | Delete
}
[2075] Fix | Delete
}
[2076] Fix | Delete
[2077] Fix | Delete
// Note: escapeshellarg() adds quotes around the string
[2078] Fix | Delete
if ($where) $where = "--where=".escapeshellarg($where);
[2079] Fix | Delete
if ('' !== $where && 'win' === strtolower(substr(PHP_OS, 0, 3))) {
[2080] Fix | Delete
// change the [percent_sign] back to % char
[2081] Fix | Delete
$where = str_replace('[percent_sign]', '%', $where);
[2082] Fix | Delete
}
[2083] Fix | Delete
[2084] Fix | Delete
if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
[2085] Fix | Delete
$exec = "cd ".escapeshellarg(str_replace('/', '\\', $this->updraft_dir))." & ";
[2086] Fix | Delete
} else {
[2087] Fix | Delete
$exec = "cd ".escapeshellarg($this->updraft_dir)."; ";
[2088] Fix | Delete
}
[2089] Fix | Delete
[2090] Fix | Delete
// Allow --max_allowed_packet to be configured via constant. Experience has shown some customers with complex CMS or pagebuilder setups can have very large postmeta entries.
[2091] Fix | Delete
$msqld_max_allowed_packet = (defined('UPDRAFTPLUS_MYSQLDUMP_MAX_ALLOWED_PACKET') && (is_int(UPDRAFTPLUS_MYSQLDUMP_MAX_ALLOWED_PACKET) || is_string(UPDRAFTPLUS_MYSQLDUMP_MAX_ALLOWED_PACKET))) ? UPDRAFTPLUS_MYSQLDUMP_MAX_ALLOWED_PACKET : '12M';
[2092] Fix | Delete
[2093] Fix | Delete
$exec .= "$potsql --defaults-file=$pfile $where --max-allowed-packet=$msqld_max_allowed_packet --quote-names --add-drop-table";
[2094] Fix | Delete
[2095] Fix | Delete
static $mysql_version = null;
[2096] Fix | Delete
if (null === $mysql_version) {
[2097] Fix | Delete
$mysql_version = $wpdb->get_var('SELECT VERSION()');
[2098] Fix | Delete
if ('' == $mysql_version) $mysql_version = $wpdb->db_version();
[2099] Fix | Delete
}
[2100] Fix | Delete
if ($mysql_version && version_compare($mysql_version, '5.1', '>=')) {
[2101] Fix | Delete
$exec .= " --no-tablespaces";
[2102] Fix | Delete
}
[2103] Fix | Delete
[2104] Fix | Delete
$exec .= " --skip-comments --skip-set-charset --allow-keywords --dump-date --extended-insert --user=".escapeshellarg($this->dbinfo['user'])." ";
[2105] Fix | Delete
[2106] Fix | Delete
$host = $this->dbinfo['host'];
[2107] Fix | Delete
[2108] Fix | Delete
if (preg_match('#^(.*):(\d+)$#', $host, $matches)) {
[2109] Fix | Delete
// The escapeshellarg() on $matches[2] is only to avoid tripping static analysis tools
[2110] Fix | Delete
$exec .= "--host=".escapeshellarg($matches[1])." --port=".escapeshellarg($matches[2])." ";
[2111] Fix | Delete
} elseif (preg_match('#^(.*):(.*)$#', $host, $matches) && file_exists($matches[2])) {
[2112] Fix | Delete
$exec .= "--host=".escapeshellarg($matches[1])." --socket=".escapeshellarg($matches[2])." ";
[2113] Fix | Delete
} else {
[2114] Fix | Delete
$exec .= "--host=".escapeshellarg($host)." ";
[2115] Fix | Delete
}
[2116] Fix | Delete
[2117] Fix | Delete
$exec .= $this->dbinfo['name']." ".escapeshellarg($table_name);
[2118] Fix | Delete
[2119] Fix | Delete
$ret = false;
[2120] Fix | Delete
$any_output = false;
[2121] Fix | Delete
$writes = 0;
[2122] Fix | Delete
$write_bytes = 0;
[2123] Fix | Delete
$handle = function_exists('popen') ? popen($exec, 'r') : false;
[2124] Fix | Delete
if ($handle) {
[2125] Fix | Delete
while (!feof($handle)) {
[2126] Fix | Delete
$w = fgets($handle, 1048576);
[2127] Fix | Delete
if (is_string($w) && $w) {
[2128] Fix | Delete
$this->stow($w);
[2129] Fix | Delete
$writes++;
[2130] Fix | Delete
$write_bytes += strlen($w);
[2131] Fix | Delete
$any_output = true;
[2132] Fix | Delete
}
[2133] Fix | Delete
}
[2134] Fix | Delete
$ret = pclose($handle);
[2135] Fix | Delete
// The manual page for pclose() claims that only -1 indicates an error, but this is untrue
[2136] Fix | Delete
if (0 != $ret) {
[2137] Fix | Delete
$updraftplus->log("Binary mysqldump: error (code: $ret)");
[2138] Fix | Delete
// Keep counter of failures? Change value of binsqldump?
[2139] Fix | Delete
$ret = false;
[2140] Fix | Delete
} else {
[2141] Fix | Delete
if ($any_output) {
[2142] Fix | Delete
$updraftplus->log("Table $table_name: binary mysqldump finished (writes: $writes, bytes $write_bytes, return code $ret) in ".sprintf("%.02f", max(microtime(true)-$microtime, 0.00001))." seconds");
[2143] Fix | Delete
$ret = true;
[2144] Fix | Delete
}
[2145] Fix | Delete
}
[2146] Fix | Delete
} else {
[2147] Fix | Delete
$updraftplus->log("Binary mysqldump error: bindump popen failed");
[2148] Fix | Delete
}
[2149] Fix | Delete
[2150] Fix | Delete
// Clean temporary files
[2151] Fix | Delete
@unlink($this->updraft_dir.'/'.$pfile);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
[2152] Fix | Delete
[2153] Fix | Delete
return $ret;
[2154] Fix | Delete
[2155] Fix | Delete
}
[2156] Fix | Delete
[2157] Fix | Delete
/**
[2158] Fix | Delete
* Write out the initial backup information for a table to the currently open file
[2159] Fix | Delete
*
[2160] Fix | Delete
* @param String $table - Full name of database table to backup
[2161] Fix | Delete
* @param String $dump_as_table - Table name to use when writing out
[2162] Fix | Delete
* @param String $table_type - Table type - 'VIEW' is supported; otherwise it is treated as an ordinary table
[2163] Fix | Delete
* @param Array $table_structure - Table structure as returned by a DESCRIBE command
[2164] Fix | Delete
*/
[2165] Fix | Delete
private function write_table_backup_beginning($table, $dump_as_table, $table_type, $table_structure) {
[2166] Fix | Delete
[2167] Fix | Delete
$this->stow("\n# Delete any existing table ".UpdraftPlus_Manipulation_Functions::backquote($table)."\n\nDROP TABLE IF EXISTS " . UpdraftPlus_Manipulation_Functions::backquote($dump_as_table).";\n");
[2168] Fix | Delete
[2169] Fix | Delete
if ('VIEW' == $table_type) {
[2170] Fix | Delete
$this->stow("DROP VIEW IF EXISTS " . UpdraftPlus_Manipulation_Functions::backquote($dump_as_table) . ";\n");
[2171] Fix | Delete
}
[2172] Fix | Delete
[2173] Fix | Delete
$description = ('VIEW' == $table_type) ? 'view' : 'table';
[2174] Fix | Delete
[2175] Fix | Delete
$this->stow("\n# Table structure of $description ".UpdraftPlus_Manipulation_Functions::backquote($table)."\n\n");
[2176] Fix | Delete
[2177] Fix | Delete
$create_table = $this->wpdb_obj->get_results("SHOW CREATE TABLE ".UpdraftPlus_Manipulation_Functions::backquote($table), ARRAY_N);
[2178] Fix | Delete
if (false === $create_table) {
[2179] Fix | Delete
$this->stow("#\n# Error with SHOW CREATE TABLE for $table\n#\n");
[2180] Fix | Delete
}
[2181] Fix | Delete
$create_line = UpdraftPlus_Manipulation_Functions::str_lreplace('TYPE=', 'ENGINE=', $create_table[0][1]);
[2182] Fix | Delete
[2183] Fix | Delete
// Remove PAGE_CHECKSUM parameter from MyISAM - was internal, undocumented, later removed (so causes errors on import)
[2184] Fix | Delete
if (preg_match('/ENGINE=([^\s;]+)/', $create_line, $eng_match)) {
[2185] Fix | Delete
$engine = $eng_match[1];
[2186] Fix | Delete
if ('myisam' == strtolower($engine)) {
[2187] Fix | Delete
$create_line = preg_replace('/PAGE_CHECKSUM=\d\s?/', '', $create_line, 1);
[2188] Fix | Delete
}
[2189] Fix | Delete
}
[2190] Fix | Delete
[2191] Fix | Delete
if ($dump_as_table !== $table) $create_line = UpdraftPlus_Manipulation_Functions::str_replace_once($table, $dump_as_table, $create_line);
[2192] Fix | Delete
[2193] Fix | Delete
$this->stow($create_line.' ;');
[2194] Fix | Delete
[2195] Fix | Delete
if (false === $table_structure) {
[2196] Fix | Delete
$this->stow("#\n# Error getting $description structure of $table\n#\n");
[2197] Fix | Delete
}
[2198] Fix | Delete
[2199] Fix | Delete
// Add a comment preceding the beginning of the data
[2200] Fix | Delete
$this->stow("\n\n# ".sprintf("Data contents of $description %s", UpdraftPlus_Manipulation_Functions::backquote($table))."\n\n");
[2201] Fix | Delete
[2202] Fix | Delete
}
[2203] Fix | Delete
[2204] Fix | Delete
/**
[2205] Fix | Delete
* Suggest a beginning value for how many rows to fetch in each SELECT statement (before taking into account resumptions)
[2206] Fix | Delete
*
[2207] Fix | Delete
* @param String $table - the full table name
[2208] Fix | Delete
*
[2209] Fix | Delete
* @return Integer
[2210] Fix | Delete
*/
[2211] Fix | Delete
private function get_rows_on_first_fetch($table) {
[2212] Fix | Delete
[2213] Fix | Delete
// In future, we could run over the table definition; if it is all non-massive defined lengths, we could base a calculation on that.
[2214] Fix | Delete
[2215] Fix | Delete
if ($this->table_prefix_raw.'term_relationships' == $table) {
[2216] Fix | Delete
// This table is known to have very small data lengths
[2217] Fix | Delete
$rows = 100000;
[2218] Fix | Delete
} elseif (preg_match('/meta$/i', $table)) {
[2219] Fix | Delete
// Meta-data rows tend to be short *on average*. 10MB / 4000 rows = 2.6KB/row, so this is still quite conservative.
[2220] Fix | Delete
$rows = 4000;
[2221] Fix | Delete
} else {
[2222] Fix | Delete
// The very conservative default
[2223] Fix | Delete
$rows = 1000;
[2224] Fix | Delete
}
[2225] Fix | Delete
[2226] Fix | Delete
return $rows;
[2227] Fix | Delete
[2228] Fix | Delete
}
[2229] Fix | Delete
[2230] Fix | Delete
/**
[2231] Fix | Delete
* Suggest how many rows to fetch in each SELECT statement
[2232] Fix | Delete
*
[2233] Fix | Delete
* @param String $table - the table being fetched
[2234] Fix | Delete
* @param Boolean $allow_further_reductions - whether to enable a second level of reductions (i.e. even less rows)
[2235] Fix | Delete
* @param Boolean $is_first_fetch_for_table - whether this is the first fetch on this table
[2236] Fix | Delete
* @param Integer|Boolean $expected_rows - if an integer, an estimate of the number of rows
[2237] Fix | Delete
* @param Boolean $expected_via_count - if $expected_rows is an integer, then this indicates whether the estimate was made via a SELECT COUNT() statement
[2238] Fix | Delete
*
[2239] Fix | Delete
* @return Integer
[2240] Fix | Delete
*/
[2241] Fix | Delete
private function number_of_rows_to_fetch($table, $allow_further_reductions, $is_first_fetch_for_table, $expected_rows = false, $expected_via_count = false) {
[2242] Fix | Delete
[2243] Fix | Delete
global $updraftplus;
[2244] Fix | Delete
[2245] Fix | Delete
// This used to be fixed at 500; but we (after a long time) saw a case that looked like an out-of-memory even at this level. Now that we have implemented resumptions, the risk of timeouts is much lower (we just need to process enough rows).
[2246] Fix | Delete
// October 2020: added further reductions
[2247] Fix | Delete
// Listed in increasing order due to the handling below. At the end it gets quite drastic. Note, though, that currently we don't store this in the job-data.
[2248] Fix | Delete
// A future improvement could, when things get drastic, grab and log data on the size of what is required, so that we can respond more dynamically. The strategy currently here will run out of road if memory falls short multiple times. See: https://stackoverflow.com/questions/4524019/how-to-get-the-byte-size-of-resultset-in-an-sql-query
[2249] Fix | Delete
$fetch_rows_reductions = array(500, 250, 200, 100);
[2250] Fix | Delete
[2251] Fix | Delete
$default_on_first_fetch = $this->get_rows_on_first_fetch($table);
[2252] Fix | Delete
[2253] Fix | Delete
$known_bigger_than_table = (!is_bool($expected_rows) && $expected_rows && $expected_via_count && $default_on_first_fetch > 2 * $expected_rows);
[2254] Fix | Delete
[2255] Fix | Delete
if ($known_bigger_than_table) $allow_further_reductions = true;
[2256] Fix | Delete
[2257] Fix | Delete
if ($allow_further_reductions) {
[2258] Fix | Delete
// If we're relying on LIMIT with offsets, then we have to be mindful of how that performs
[2259] Fix | Delete
$fetch_rows_reductions = array_merge($fetch_rows_reductions, array(50, 20, 5));
[2260] Fix | Delete
}
[2261] Fix | Delete
[2262] Fix | Delete
// Remove any that are far out of range
[2263] Fix | Delete
if ($known_bigger_than_table) {
[2264] Fix | Delete
foreach ($fetch_rows_reductions as $k => $reduce_to) {
[2265] Fix | Delete
if ($reduce_to > $expected_rows * 2 && count($fetch_rows_reductions) > 2) {
[2266] Fix | Delete
unset($fetch_rows_reductions[$k]);
[2267] Fix | Delete
}
[2268] Fix | Delete
}
[2269] Fix | Delete
}
[2270] Fix | Delete
[2271] Fix | Delete
// If this is not the first fetch on a table, then get what was stored last time we set it (if we ever did). On the first fetch, reset back to the starting value (we presume problems are table-specific).
[2272] Fix | Delete
// This means that the same value will persist whilst the table is being backed up, both during the current resumption, and subsequent ones
[2273] Fix | Delete
$fetch_rows = $is_first_fetch_for_table ? $default_on_first_fetch : $updraftplus->jobdata_get('fetch_rows', $default_on_first_fetch);
[2274] Fix | Delete
[2275] Fix | Delete
$fetch_rows_at_start = $fetch_rows;
[2276] Fix | Delete
[2277] Fix | Delete
$resumptions_since_last_successful = $updraftplus->current_resumption - $updraftplus->last_successful_resumption;
[2278] Fix | Delete
[2279] Fix | Delete
// Do we need to reduce the number of rows we attempt to fetch?
[2280] Fix | Delete
// If something useful has happened on this run, then we don't try any reductions (we save them for a resumption after one on which nothing useful happened)
[2281] Fix | Delete
if ($known_bigger_than_table || (!$updraftplus->something_useful_happened && !empty($updraftplus->current_resumption) && $resumptions_since_last_successful > 1)) {
[2282] Fix | Delete
[2283] Fix | Delete
$break_after = $is_first_fetch_for_table ? max($resumptions_since_last_successful - 1, 1) : 1;
[2284] Fix | Delete
[2285] Fix | Delete
foreach ($fetch_rows_reductions as $reduce_to) {
[2286] Fix | Delete
if ($fetch_rows > $reduce_to) {
[2287] Fix | Delete
// Go down one level
[2288] Fix | Delete
$fetch_rows = $reduce_to;
[2289] Fix | Delete
$break_after--;
[2290] Fix | Delete
if ($break_after < 1) break;
[2291] Fix | Delete
}
[2292] Fix | Delete
}
[2293] Fix | Delete
[2294] Fix | Delete
$log_start = $updraftplus->current_resumption ? "Last successful resumption was $resumptions_since_last_successful runs ago" : "Table is relatively small";
[2295] Fix | Delete
$updraftplus->log("$log_start; fetch_rows will thus be: $fetch_rows (allow_further_reductions=$allow_further_reductions, is_first_fetch=$is_first_fetch_for_table, known_bigger_than_table=$known_bigger_than_table)");
[2296] Fix | Delete
}
[2297] Fix | Delete
[2298] Fix | Delete
// If it has changed, then preserve it in the job for the next resumption (of this table)
[2299] Fix | Delete
if ($fetch_rows_at_start !== $fetch_rows || $is_first_fetch_for_table) $updraftplus->jobdata_set('fetch_rows', $fetch_rows);
[2300] Fix | Delete
[2301] Fix | Delete
return $fetch_rows;
[2302] Fix | Delete
[2303] Fix | Delete
}
[2304] Fix | Delete
[2305] Fix | Delete
/**
[2306] Fix | Delete
* Return a list of primary keys (N.B. the method should not be called unless the caller already knows that the table has a single/simple primary key) for rows that have "over-sized" data.
[2307] Fix | Delete
* Currently this only examines the "posts" table and any other table with a longtext type, which are the primary causes of problems. If others are revealed in future, it can be generalised (e.g. examine the whole definition/all cells).
[2308] Fix | Delete
*
[2309] Fix | Delete
* @param String $table - the full table name
[2310] Fix | Delete
* @param Array $structure - the table structure, as from WPDB::get_results("DESCRIBE ...");
[2311] Fix | Delete
* @param String $primary_key - the primary key to use; required if $structure is set (slightly redundant, since it can be derived from structure)
[2312] Fix | Delete
*
[2313] Fix | Delete
* @return Array - list of IDs
[2314] Fix | Delete
*/
[2315] Fix | Delete
private function get_oversized_rows($table, $structure = array(), $primary_key = '') {
[2316] Fix | Delete
[2317] Fix | Delete
if ($this->table_prefix_raw.'posts' != $table) {
[2318] Fix | Delete
if (empty($structure) || '' === $primary_key) return array();
[2319] Fix | Delete
foreach ($structure as $item) {
[2320] Fix | Delete
if ('' !== $item->Field && 'longtext' === $item->Type) {
[2321] Fix | Delete
$use_field = $item->Field;
[2322] Fix | Delete
}
[2323] Fix | Delete
}
[2324] Fix | Delete
} else {
[2325] Fix | Delete
$primary_key = 'id';
[2326] Fix | Delete
$use_field = 'post_content';
[2327] Fix | Delete
}
[2328] Fix | Delete
[2329] Fix | Delete
if (!isset($use_field)) return array();
[2330] Fix | Delete
[2331] Fix | Delete
global $updraftplus;
[2332] Fix | Delete
[2333] Fix | Delete
// Look for the jobdata_delete() call elsewhere in this class - the key name needs to match
[2334] Fix | Delete
$jobdata_key = 'oversized_rows_'.$table;
[2335] Fix | Delete
[2336] Fix | Delete
$oversized_list = $updraftplus->jobdata_get($jobdata_key);
[2337] Fix | Delete
[2338] Fix | Delete
if (is_array($oversized_list)) return $oversized_list;
[2339] Fix | Delete
[2340] Fix | Delete
$oversized_list = array();
[2341] Fix | Delete
[2342] Fix | Delete
// Allow over-ride via a constant
[2343] Fix | Delete
$oversized_row_size = defined('UPDRAFTPLUS_OVERSIZED_ROW_SIZE') ? UPDRAFTPLUS_OVERSIZED_ROW_SIZE : 2048576;
[2344] Fix | Delete
[2345] Fix | Delete
$sql = $this->wpdb_obj->prepare("SELECT ".UpdraftPlus_Manipulation_Functions::backquote($primary_key)." FROM ".UpdraftPlus_Manipulation_Functions::backquote($table)." WHERE LENGTH(".UpdraftPlus_Manipulation_Functions::backquote($use_field).") > %d ORDER BY ".UpdraftPlus_Manipulation_Functions::backquote($primary_key)." ASC", $oversized_row_size);
[2346] Fix | Delete
[2347] Fix | Delete
$oversized_rows = $this->wpdb_obj->get_col($sql);
[2348] Fix | Delete
[2349] Fix | Delete
// Upon an error, just return an empty list
[2350] Fix | Delete
if (!is_array($oversized_rows)) return array();
[2351] Fix | Delete
[2352] Fix | Delete
$updraftplus->jobdata_set($jobdata_key, $oversized_rows);
[2353] Fix | Delete
[2354] Fix | Delete
return $oversized_rows;
[2355] Fix | Delete
[2356] Fix | Delete
}
[2357] Fix | Delete
[2358] Fix | Delete
/**
[2359] Fix | Delete
* Original version taken partially from phpMyAdmin and partially from Alain Wolf, Zurich - Switzerland to use the WordPress $wpdb object
[2360] Fix | Delete
* Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
[2361] Fix | Delete
* Modified by Scott Merrill (http://www.skippy.net/)
[2362] Fix | Delete
* Subsequently heavily improved and modified
[2363] Fix | Delete
*
[2364] Fix | Delete
* This method should be called in a loop for a complete table backup (see the information for the returned parameter). The method may implement whatever strategy it likes for deciding when to return (the assumption is that when it does return with some results, the caller should register that something useful happened).
[2365] Fix | Delete
*
[2366] Fix | Delete
* @param String $table - Full name of database table to backup
[2367] Fix | Delete
* @param String $table_type - Table type - 'VIEW' is supported; otherwise it is treated as an ordinary table
[2368] Fix | Delete
* @param Integer|Boolean $start_record - Specify the starting record, or true to start at the beginning. Our internal page size is fixed at 1000 (though within that we might actually query in smaller batches).
[2369] Fix | Delete
* @param Boolean $can_use_primary_key - Whether it is allowed to perform quicker SELECTS based on the primary key. The intended use case for false is to support backups running during a version upgrade. N.B. This "can" is not absolute; there may be other constraints dealt with within this method.
[2370] Fix | Delete
*
[2371] Fix | Delete
* @return Integer|Array|WP_Error - a WP_Error to indicate an error; an array indicates that it finished (if it includes 'next_record' that means it finished via producing something); an integer to indicate the next page the case that there are more to do.
[2372] Fix | Delete
*/
[2373] Fix | Delete
private function backup_table($table, $table_type = 'BASE TABLE', $start_record = true, $can_use_primary_key = true) {
[2374] Fix | Delete
$process_pages = 100;
[2375] Fix | Delete
[2376] Fix | Delete
// Preserve the passed-in value
[2377] Fix | Delete
$original_start_record = $start_record;
[2378] Fix | Delete
[2379] Fix | Delete
global $updraftplus;
[2380] Fix | Delete
[2381] Fix | Delete
$microtime = microtime(true);
[2382] Fix | Delete
$total_rows = 0;
[2383] Fix | Delete
[2384] Fix | Delete
// Deal with Windows/old MySQL setups with erroneous table prefixes differing in case
[2385] Fix | Delete
$dump_as_table = (false == $this->duplicate_tables_exist && 0 === stripos($table, $this->table_prefix) && 0 !== strpos($table, $this->table_prefix)) ? $this->table_prefix.substr($table, strlen($this->table_prefix)) : $table;
[2386] Fix | Delete
[2387] Fix | Delete
$table_structure = $this->wpdb_obj->get_results("DESCRIBE ".UpdraftPlus_Manipulation_Functions::backquote($table));
[2388] Fix | Delete
if (!$table_structure) {
[2389] Fix | Delete
// $updraftplus->log(__('Error getting table details', 'updraftplus') . ": $table", 'error');
[2390] Fix | Delete
$error_message = '';
[2391] Fix | Delete
if ($this->wpdb_obj->last_error) $error_message .= ' ('.$this->wpdb_obj->last_error.')';
[2392] Fix | Delete
return new WP_Error('table_details_error', $error_message);
[2393] Fix | Delete
}
[2394] Fix | Delete
[2395] Fix | Delete
// If at the beginning of the dump for a table, then add the DROP and CREATE statements
[2396] Fix | Delete
if (true === $start_record) {
[2397] Fix | Delete
$this->write_table_backup_beginning($table, $dump_as_table, $table_type, $table_structure);
[2398] Fix | Delete
}
[2399] Fix | Delete
[2400] Fix | Delete
// Some tables have optional data, and should be skipped if they do not work
[2401] Fix | Delete
$table_sans_prefix = substr($table, strlen($this->table_prefix_raw));
[2402] Fix | Delete
$data_optional_tables = ('wp' == $this->whichdb) ? apply_filters('updraftplus_data_optional_tables', explode(',', UPDRAFTPLUS_DATA_OPTIONAL_TABLES)) : array();
[2403] Fix | Delete
if (in_array($table_sans_prefix, $data_optional_tables)) {
[2404] Fix | Delete
if (!$updraftplus->something_useful_happened && !empty($updraftplus->current_resumption) && ($updraftplus->current_resumption - $updraftplus->last_successful_resumption > 2)) {
[2405] Fix | Delete
$updraftplus->log("Table $table: Data skipped (previous attempts failed, and table is marked as non-essential)");
[2406] Fix | Delete
return array();
[2407] Fix | Delete
}
[2408] Fix | Delete
}
[2409] Fix | Delete
[2410] Fix | Delete
$table_data = array();
[2411] Fix | Delete
if ('VIEW' != $table_type) {
[2412] Fix | Delete
$fields = array();
[2413] Fix | Delete
$defs = array();
[2414] Fix | Delete
$integer_fields = array();
[2415] Fix | Delete
$binary_fields = array();
[2416] Fix | Delete
$bit_fields = array();
[2417] Fix | Delete
$bit_field_exists = false;
[2418] Fix | Delete
[2419] Fix | Delete
// false means "not yet set"; a string means what it was set to; null means that there are multiple (and so not useful to us). If it is not a string, then $primary_key_type is invalid and should not be used.
[2420] Fix | Delete
$primary_key = false;
[2421] Fix | Delete
$primary_key_type = false;
[2422] Fix | Delete
[2423] Fix | Delete
// $table_structure was from "DESCRIBE $table"
[2424] Fix | Delete
foreach ($table_structure as $struct) {
[2425] Fix | Delete
[2426] Fix | Delete
if (isset($struct->Key) && 'PRI' == $struct->Key && '' != $struct->Field) {
[2427] Fix | Delete
$primary_key = (false === $primary_key) ? $struct->Field : null;
[2428] Fix | Delete
$primary_key_type = $struct->Type;
[2429] Fix | Delete
}
[2430] Fix | Delete
[2431] Fix | Delete
if ((0 === strpos($struct->Type, 'tinyint')) || (0 === strpos(strtolower($struct->Type), 'smallint'))
[2432] Fix | Delete
|| (0 === strpos(strtolower($struct->Type), 'mediumint')) || (0 === strpos(strtolower($struct->Type), 'int')) || (0 === strpos(strtolower($struct->Type), 'bigint'))
[2433] Fix | Delete
) {
[2434] Fix | Delete
$defs[strtolower($struct->Field)] = (null === $struct->Default) ? 'NULL' : $struct->Default;
[2435] Fix | Delete
$integer_fields[strtolower($struct->Field)] = true;
[2436] Fix | Delete
}
[2437] Fix | Delete
[2438] Fix | Delete
if ((0 === strpos(strtolower($struct->Type), 'binary')) || (0 === strpos(strtolower($struct->Type), 'varbinary')) || (0 === strpos(strtolower($struct->Type), 'tinyblob')) || (0 === strpos(strtolower($struct->Type), 'mediumblob')) || (0 === strpos(strtolower($struct->Type), 'blob')) || (0 === strpos(strtolower($struct->Type), 'longblob'))) {
[2439] Fix | Delete
$binary_fields[strtolower($struct->Field)] = true;
[2440] Fix | Delete
}
[2441] Fix | Delete
[2442] Fix | Delete
if (preg_match('/^bit(?:\(([0-9]+)\))?$/i', trim($struct->Type), $matches)) {
[2443] Fix | Delete
if (!$bit_field_exists) $bit_field_exists = true;
[2444] Fix | Delete
$bit_fields[strtolower($struct->Field)] = !empty($matches[1]) ? max(1, (int) $matches[1]) : 1;
[2445] Fix | Delete
// the reason why if bit fields are found then the fields need to be cast into binary type is that if mysqli_query function is being used, mysql will convert the bit field value to a decimal number and represent it in a string format whereas, if mysql_query function is being used, mysql will not convert it to a decimal number but instead will keep it retained as it is
[2446] Fix | Delete
$struct->Field = "CAST(".UpdraftPlus_Manipulation_Functions::backquote(str_replace('`', '``', $struct->Field))." AS BINARY) AS ".UpdraftPlus_Manipulation_Functions::backquote(str_replace('`', '``', $struct->Field));
[2447] Fix | Delete
$fields[] = $struct->Field;
[2448] Fix | Delete
} else {
[2449] Fix | Delete
$fields[] = UpdraftPlus_Manipulation_Functions::backquote(str_replace('`', '``', $struct->Field));
[2450] Fix | Delete
}
[2451] Fix | Delete
}
[2452] Fix | Delete
[2453] Fix | Delete
$expected_via_count = false;
[2454] Fix | Delete
[2455] Fix | Delete
// N.B. At this stage this is for optimisation, mainly targets what is used on the core WP tables (bigint(20)); a value can be relied upon, but false is not definitive. N.B. https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/numeric-type-syntax.html (retrieved Aug 2021): "As of MySQL 8.0.17, the display width attribute is deprecated for integer data types; you should expect support for it to be removed in a future version of MySQL." MySQL 8.0.20 is not returning it.
[2456] Fix | Delete
$use_primary_key = false;
[2457] Fix | Delete
if ($can_use_primary_key && is_string($primary_key) && preg_match('#^(small|medium|big)?int(\(| |$)#i', $primary_key_type)) {
[2458] Fix | Delete
$use_primary_key = true;
[2459] Fix | Delete
[2460] Fix | Delete
// We don't bother re-counting if it's likely to be so large that we're not going to do anything with the result
[2461] Fix | Delete
if (is_bool($this->expected_rows) || $this->expected_rows < 1000) {
[2462] Fix | Delete
$expected_rows = $this->wpdb_obj->get_var('SELECT COUNT('.UpdraftPlus_Manipulation_Functions::backquote($primary_key).') FROM '.UpdraftPlus_Manipulation_Functions::backquote($table));
[2463] Fix | Delete
if (!is_bool($expected_rows)) {
[2464] Fix | Delete
$this->expected_rows = $expected_rows;
[2465] Fix | Delete
$expected_via_count = true;
[2466] Fix | Delete
}
[2467] Fix | Delete
}
[2468] Fix | Delete
[2469] Fix | Delete
$oversized_rows = $this->get_oversized_rows($table, $table_structure, $primary_key);
[2470] Fix | Delete
[2471] Fix | Delete
if (preg_match('# unsigned$#i', $primary_key_type)) {
[2472] Fix | Delete
if (true === $start_record) $start_record = -1;
[2473] Fix | Delete
} else {
[2474] Fix | Delete
if (true === $start_record) {
[2475] Fix | Delete
$min_value = $this->wpdb_obj->get_var('SELECT MIN('.UpdraftPlus_Manipulation_Functions::backquote($primary_key).') FROM '.UpdraftPlus_Manipulation_Functions::backquote($table));
[2476] Fix | Delete
$start_record = (is_numeric($min_value) && $min_value) ? (int) $min_value - 1 : -1;
[2477] Fix | Delete
}
[2478] Fix | Delete
}
[2479] Fix | Delete
}
[2480] Fix | Delete
[2481] Fix | Delete
if (!is_bool($this->expected_rows)) {
[2482] Fix | Delete
$this->log_expected_rows($table, $this->expected_rows, $expected_via_count);
[2483] Fix | Delete
}
[2484] Fix | Delete
[2485] Fix | Delete
$search = array("\x00", "\x0a", "\x0d", "\x1a");
[2486] Fix | Delete
$replace = array('\0', '\n', '\r', '\Z');
[2487] Fix | Delete
[2488] Fix | Delete
$where_array = apply_filters('updraftplus_backup_table_sql_where', array(), $table, $this);
[2489] Fix | Delete
$where = '';
[2490] Fix | Delete
if (!empty($where_array) && is_array($where_array)) {
[2491] Fix | Delete
$where = 'WHERE '.implode(' AND ', $where_array);
[2492] Fix | Delete
}
[2493] Fix | Delete
[2494] Fix | Delete
// Experimentation here shows that on large tables (we tested with 180,000 rows) on MyISAM, 1000 makes the table dump out 3x faster than the previous value of 100. After that, the benefit diminishes (increasing to 4000 only saved another 12%)
[2495] Fix | Delete
[2496] Fix | Delete
$fetch_rows = $this->number_of_rows_to_fetch($table, $use_primary_key || $start_record < 500000, true === $original_start_record, $this->expected_rows, $expected_via_count);
[2497] Fix | Delete
[2498] Fix | Delete
if (!is_bool($this->expected_rows)) $this->expected_rows = true;
[2499] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function