Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add/sqlite import export support #259

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions features/db-export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Feature: Export a WordPress database
-- Dump completed on
"""

@require-mysql
Scenario: Export database with mysql defaults to STDOUT
Given a WP install

Expand All @@ -52,6 +53,7 @@ Feature: Export a WordPress database
-- Dump completed on
"""

@require-mysql
Scenario: Export database with mysql --no-defaults to STDOUT
Given a WP install

Expand All @@ -61,6 +63,7 @@ Feature: Export a WordPress database
-- Dump completed on
"""

@require-mysql
Scenario: Export database with passed-in options
Given a WP install

Expand All @@ -78,6 +81,7 @@ Feature: Export a WordPress database
"""
And STDOUT should be empty

@require-mysql
Scenario: MySQL defaults are available as appropriate with --defaults flag
Given a WP install

Expand Down
26 changes: 17 additions & 9 deletions features/db-import.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Feature: Import a WordPress database
Success: Imported from 'wp_cli_test.sql'.
"""

@require-mysql
Scenario: Import from database name path by default with mysql defaults
Given a WP install

Expand All @@ -24,6 +25,7 @@ Feature: Import a WordPress database
Success: Imported from 'wp_cli_test.sql'.
"""

@require-mysql
Scenario: Import from database name path by default with --no-defaults
Given a WP install

Expand All @@ -36,15 +38,19 @@ Feature: Import a WordPress database
Success: Imported from 'wp_cli_test.sql'.
"""

Scenario: Import from STDIN
Given a WP install
Scenario: Import from STDIN
Given a WP install

When I run `wp db import -`
Then STDOUT should be:
"""
Success: Imported from 'STDIN'.
"""
When I run `wp db export wp_cli_test.sql`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch and fix. I see this test was there for 5 years. How did it even work before?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it depends on the environment. We both experienced this issue locally but the CI seems to be fine with it.

Then the wp_cli_test.sql file should exist

When I run `cat wp_cli_test.sql | wp db import -`
Then STDOUT should be:
"""
Success: Imported from 'STDIN'.
"""

@require-mysql
Scenario: Import from database name path by default and skip speed optimization
Given a WP install

Expand All @@ -56,7 +62,7 @@ Feature: Import a WordPress database
"""
Success: Imported from 'wp_cli_test.sql'.
"""

@require-mysql
Scenario: Import from database name path by default with passed-in dbuser/dbpass
Given a WP install

Expand Down Expand Up @@ -91,6 +97,7 @@ Feature: Import a WordPress database
Success: Imported from 'debug.sql'.
"""

@require-mysql
Scenario: Help runs properly at various points of a functional WP install
Given an empty directory

Expand Down Expand Up @@ -128,6 +135,7 @@ Feature: Import a WordPress database
"""
wp db import
"""
@require-mysql
Scenario: MySQL defaults are available as appropriate with --defaults flag
Given a WP install

Expand All @@ -152,7 +160,7 @@ Feature: Import a WordPress database
Debug (db): Running shell command: /usr/bin/env mysql --no-defaults --no-auto-rehash
"""

@require-wp-4.2
@require-wp-4.2 @require-mysql
Scenario: Import db that has emoji in post
Given a WP install

Expand Down
8 changes: 4 additions & 4 deletions features/db-query.feature
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,23 @@ Feature: Query the database with WordPress' MySQL config
Scenario: MySQL defaults are available as appropriate with --defaults flag
Given a WP install

When I try `wp db query --defaults --debug`
When I try `"select 1" | wp db query --defaults --debug`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the context of this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test command expects STDIN, none was given so it would cause the command to freeze indefinitely and the test would be stuck (at least locally).

Then STDERR should contain:
"""
Debug (db): Running shell command: /usr/bin/env mysql --no-auto-rehash
"""

When I try `wp db query --debug`
When I try `"select 1" | wp db query --debug`
Then STDERR should contain:
"""
Debug (db): Running shell command: /usr/bin/env mysql --no-defaults --no-auto-rehash
"""

When I try `wp db query --no-defaults --debug`
When I try `"select 1" | wp db query --no-defaults --debug`
Then STDERR should contain:
"""
Debug (db): Running shell command: /usr/bin/env mysql --no-defaults --no-auto-rehash
"""
"""

Scenario: SQL modes do not include any of the modes incompatible with WordPress
Given a WP install
Expand Down
18 changes: 17 additions & 1 deletion src/DB_Command.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use WP_CLI\DB\SQLite\Export;
use WP_CLI\DB\SQLite\Import;
use WP_CLI\Formatter;
use WP_CLI\Utils;

Expand Down Expand Up @@ -596,6 +598,7 @@ public function export( $args, $assoc_args ) {
$result_file = sprintf( '%s-%s-%s.sql', DB_NAME, date( 'Y-m-d' ), $hash ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date

}

$stdout = ( '-' === $result_file );
$porcelain = Utils\get_flag_value( $assoc_args, 'porcelain' );

Expand All @@ -604,6 +607,13 @@ public function export( $args, $assoc_args ) {
WP_CLI::error( 'Porcelain is not allowed when output mode is STDOUT.' );
}

// Check if SQLite is enabled and use it if it is.
if ( Export::get_sqlite_plugin_version() ) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check for a minimal supported version of the SQLite plugin and fail with the error if it's older? Or do we rely on the logic inside load_dependencies(), which is part of import and export classes?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment we check the version of the SQLite plugin when we load the dependencies. We can do this here as well. I assume you still want it to trigger the same error right?

$export = new Export();
$export->run( $result_file, $assoc_args );
return;
}

if ( ! $stdout ) {
$assoc_args['result-file'] = $result_file;
}
Expand Down Expand Up @@ -762,6 +772,13 @@ public function import( $args, $assoc_args ) {
$result_file = sprintf( '%s.sql', DB_NAME );
}

// Check if SQLite is enabled and use it if it is.
if ( Import::get_sqlite_plugin_version() ) {
$importer = new Import();
$importer->run( $result_file, $assoc_args );
return;
}

// Process options to MySQL.
$mysql_args = array_merge(
[ 'database' => DB_NAME ],
Expand Down Expand Up @@ -842,7 +859,6 @@ public function import( $args, $assoc_args ) {
* # Export only tables for a single site
* $ wp db export --tables=$(wp db tables --url=sub.example.com --format=csv)
* Success: Exported to wordpress_dbase.sql
*
* @when after_wp_load
*/
public function tables( $args, $assoc_args ) {
Expand Down
121 changes: 121 additions & 0 deletions src/SQLite/Base.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php
namespace WP_CLI\DB\SQLite;

use WP_CLI;

class Base {

protected $unsupported_arguments = [];

/**
* Get the version of the SQLite integration plugin if it is installed
* and activated.
*
* @return false|string The version of the SQLite integration plugin or false if not found/activated.
*/
public static function get_sqlite_plugin_version() {
// Check if there is a db.php file in the wp-content directory.
if ( ! file_exists( ABSPATH . '/wp-content/db.php' ) ) {
return false;
}

// If the file is found, we need to check that it is the sqlite integration plugin.
$plugin_file = file_get_contents( ABSPATH . '/wp-content/db.php' );
if ( ! preg_match( '/define\( \'SQLITE_DB_DROPIN_VERSION\', \'([0-9.]+)\' \)/', $plugin_file ) ) {
return false;
}

$plugin_path = self::get_plugin_directory();
if ( ! $plugin_path ) {
return false;
}

// Try to get the version number from readme.txt
$plugin_file = file_get_contents( $plugin_path . '/readme.txt' );

preg_match( '/^Stable tag:\s*?(.+)$/m', $plugin_file, $matches );

return isset( $matches[1] ) ? trim( $matches[1] ) : false;
}

/**
* Find the directory where the SQLite integration plugin is installed.
*
* @return string|null The directory where the SQLite integration plugin is installed or null if not found.
*/
protected static function get_plugin_directory() {
$plugin_folders = [
ABSPATH . '/wp-content/plugins/sqlite-database-integration',
ABSPATH . '/wp-content/mu-plugins/sqlite-database-integration',
];

foreach ( $plugin_folders as $folder ) {
if ( file_exists( $folder ) && is_dir( $folder ) ) {
return $folder;
}
}

return null;
}

/**
* Load the necessary classes from the SQLite integration plugin.
*
* @return void
* @throws WP_CLI\ExitException
*/
protected function load_dependencies() {
$plugin_directory = self::get_plugin_directory();
if ( ! $plugin_directory ) {
WP_CLI::error( 'Could not locate the SQLite integration plugin.' );
}

$sqlite_plugin_version = self::get_sqlite_plugin_version();
if ( ! $sqlite_plugin_version ) {
WP_CLI::error( 'Could not determine the version of the SQLite integration plugin.' );
}

if ( version_compare( $sqlite_plugin_version, '2.1.11', '<' ) ) {
WP_CLI::error( 'The SQLite integration plugin must be version 2.1.11 or higher.' );
}

// Load the translator class from the plugin.
if ( ! defined( 'SQLITE_DB_DROPIN_VERSION' ) ) {
define( 'SQLITE_DB_DROPIN_VERSION', $sqlite_plugin_version ); // phpcs:ignore
}

# WordPress is not loaded during the execution of the export and import commands.
# The SQLite database integration plugin uses do_action and apply_filters to hook
# into the WordPress core. To prevent a fatal error, we can define these functions
# as no-op functions.
require_once __DIR__ . '/noop.php';

// We also need to selectively load the necessary classes from the plugin.
require_once $plugin_directory . '/php-polyfills.php';
require_once $plugin_directory . '/constants.php';
require_once $plugin_directory . '/wp-includes/sqlite/class-wp-sqlite-lexer.php';
require_once $plugin_directory . '/wp-includes/sqlite/class-wp-sqlite-query-rewriter.php';
require_once $plugin_directory . '/wp-includes/sqlite/class-wp-sqlite-translator.php';
require_once $plugin_directory . '/wp-includes/sqlite/class-wp-sqlite-token.php';
require_once $plugin_directory . '/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php';
}

/**
* Check if the arguments passed to the command are supported.
*
* @param $args
*
* @return void
* @throws WP_CLI\ExitException
*/
protected function check_arguments( $args ) {
if ( array_intersect_key( $args, array_flip( $this->unsupported_arguments ) ) ) {
WP_CLI::error(
sprintf(
'The following arguments are not supported by SQLite exports: %s',
implode( ', ', $this->unsupported_arguments )
)
);
}
}
}
Loading
Loading