deliciousbrains / wp-migrate-db

WordPress plugin that exports your database, does a find and replace on URLs and file paths, then allows you to save it to your computer.
https://wordpress.org/plugins/wp-migrate-db/
341 stars 515 forks source link

Some exports of databases with large InnoDB tables fail due to row count estimation #145

Open jormaster3k opened 1 year ago

jormaster3k commented 1 year ago

We encountered a bug while running wp migrated export on one of our databases with many rows in the wp_postmeta table.

wp --allow-root migratedb export exportfile.sql  --path=/path/to/wordpress-site

Error

Initiating migration...
PHP Fatal error:  Uncaught ValueError: str_repeat(): Argument #2 ($times) must be greater than or equal to 0 in phar:///usr/local/bin/wp/vendor/wp-cli/php-cli-tools/lib/cli/progress/Bar.php:64================================] 7:07 / 8:35
Stack trace:
#0 phar:///usr/local/bin/wp/vendor/wp-cli/php-cli-tools/lib/cli/progress/Bar.php(64): str_repeat()
#1 phar:///usr/local/bin/wp/vendor/wp-cli/php-cli-tools/lib/cli/Notify.php(182): cli\progress\Bar->display()
#2 phar:///usr/local/bin/wp/vendor/wp-cli/php-cli-tools/lib/cli/progress/Bar.php(83): cli\Notify->tick()
#3 /path/to/wordpress//wp-content/plugins/wp-migrate-db/class/Common/Cli/Cli.php(573): cli\progress\Bar->tick()
#4 /path/to/wordpress//wp-content/plugins/wp-migrate-db/class/Common/Cli/Cli.php(266): DeliciousBrains\WPMDB\Common\Cli\Cli->migrate_tables()
#5 /path/to/wordpress//wp-content/plugins/wp-migrate-db/class/Common/Cli/Command.php(212): DeliciousBrains\WPMDB\Common\Cli\Cli->cli_migration()
#6 /path/to/wordpress//wp-content/plugins/wp-migrate-db/class/Common/Cli/Command.php(92): DeliciousBrains\WPMDB\Common\Cli\Command->_perform_cli_migration()
#7 [internal function]: DeliciousBrains\WPMDB\Common\Cli\Command->export()
#8 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Dispatcher/CommandFactory.php(100): call_user_func()
#9 [internal function]: WP_CLI\Dispatcher\CommandFactory::WP_CLI\Dispatcher\{closure}()
#10 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Dispatcher/Subcommand.php(491): call_user_func()
#11 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Runner.php(417): WP_CLI\Dispatcher\Subcommand->invoke()
#12 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Runner.php(440): WP_CLI\Runner->run_command()
#13 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Runner.php(1237): WP_CLI\Runner->run_command_and_exit()
#14 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Bootstrap/LaunchRunner.php(28): WP_CLI\Runner->start()
#15 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/bootstrap.php(78): WP_CLI\Bootstrap\LaunchRunner->process()
#16 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/wp-cli.php(27): WP_CLI\bootstrap()
#17 phar:///usr/local/bin/wp/php/boot-phar.php(11): include('...')
#18 /usr/local/bin/wp(4): include('...')
#19 {main}
  thrown in phar:///usr/local/bin/wp/vendor/wp-cli/php-cli-tools/lib/cli/progress/Bar.php on line 64

Further research

In DeliciousBrains\WPMDB\Common\Cli\Cli->migrate_tables(), we're updating the progress bar by comparing the rows processed to the total number of rows in the table. The total number of rows in the table is obtained by querying TABLE_ROWS from INFORMATION_SCHEMA.TABLES. The problem is that according to the MySQL INFORMATION_SCHEMA documentation, for InnoDB, the TABLE_ROWS value is estimated.

For tables with a large number of rows, this may cause the actual number of rows processed to exceed the total number of rows in the database, leading to the "must be greater than or equal to 0" error.

Workaround / Fix

As a simple fix, we can check that $increment is greater than or equal to zero before calling tick():

 572                     if (null !== $notify && $increment >=0) {
 573                         $notify->tick($increment);
 574                     }

The progress bar may not be accurate, but at least the entire export will not fail.

A better fix would be to call select count(*) to obtain the precise number of rows in the table, but that may come with performance implications.

Lastly, I think it would make sense to add a flag to disable the progress bar if desired.