Skip to content

Commit b5b46dd

Browse files
Merge branch 'main' into drop-legacy-driver-support
2 parents 5448bad + 3c4358b commit b5b46dd

8 files changed

Lines changed: 310 additions & 32 deletions

File tree

‎features/bootstrap/SQLiteFeatureContext.php‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@ public function theFileShouldExist( $filename ) {
7171

7272
private function connectToDatabase() {
7373
if ( ! $this->db ) {
74-
$run_dir = $this->variables['RUN_DIR'];
75-
$db_file = $run_dir . '/wp-content/database/.ht.sqlite';
74+
$result = $this->proc( 'wp eval \'echo defined( "DB_PATH" ) ? DB_PATH : FQDB;\'' )->run_check();
75+
$db_file = trim( $result->stdout );
76+
if ( ! is_file( $db_file ) ) {
77+
throw new Exception( 'The WordPress SQLite database does not exist.' );
78+
}
7679
$this->db = new PDO( 'sqlite:' . $db_file );
7780
$this->db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
7881
}

‎features/sqlite-import.feature‎

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ Feature: WP-CLI SQLite Import Command
4545
And the SQLite database should contain the imported data
4646

4747
@require-sqlite
48-
Scenario: Import a file with escape sequences
48+
Scenario: Import a file with MySQL backslash escape sequences
4949
Given a SQL dump file named "test_import.sql" with content:
5050
"""
51-
SET sql_mode='NO_BACKSLASH_ESCAPES';
5251
CREATE TABLE test_table (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT);
5352
INSERT INTO test_table (name) VALUES ('Test that escaping a backslash \\ works');
5453
INSERT INTO test_table (name) VALUES ('Test that escaping multiple backslashes \\\\\\ works');
@@ -62,11 +61,11 @@ Feature: WP-CLI SQLite Import Command
6261
Success: Imported from 'test_import.sql'.
6362
"""
6463
And the SQLite database should contain a table named "test_table"
65-
And the "test_table" should contain a row with name "Test that escaping a backslash \\ works"
66-
And the "test_table" should contain a row with name "Test that escaping multiple backslashes \\\\\\ works"
67-
And the "test_table" should contain a row with name "Test that escaping a character \a works"
68-
And the "test_table" should contain a row with name "Test that escaping a backslash followed by a character \\a works"
69-
And the "test_table" should contain a row with name "Test that escaping a backslash and a character \\\a works"
64+
And the "test_table" should contain a row with name "Test that escaping a backslash \ works"
65+
And the "test_table" should contain a row with name "Test that escaping multiple backslashes \\\ works"
66+
And the "test_table" should contain a row with name "Test that escaping a character a works"
67+
And the "test_table" should contain a row with name "Test that escaping a backslash followed by a character \a works"
68+
And the "test_table" should contain a row with name "Test that escaping a backslash and a character \a works"
7069

7170
@require-sqlite
7271
Scenario: Import a file with newlines in strings
@@ -150,7 +149,7 @@ Feature: WP-CLI SQLite Import Command
150149
Success: Imported from 'test_import.sql'.
151150
"""
152151

153-
And the SQLite database should contain a table named "a'strange`identifier\name"
152+
And the SQLite database should contain a table named "a'strange`identifier\\name"
154153

155154
@require-sqlite
156155
Scenario: Import a file with whitespace and empty lines

‎features/sqlite-storage.feature‎

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
@require-sqlite
2+
Feature: SQLite database storage
3+
Commands use the same database as WordPress before its database drop-in loads.
4+
5+
Background:
6+
Given a WP installation
7+
8+
Scenario: Use an existing legacy database without migrating storage
9+
Given a SQL dump file named "test_import.sql" with content:
10+
"""
11+
CREATE TABLE test_table (id INTEGER PRIMARY KEY, name TEXT);
12+
INSERT INTO test_table VALUES (1, 'Imported data');
13+
"""
14+
When I run `wp eval 'echo defined( "DB_PATH" ) ? DB_PATH : FQDB;'`
15+
Then save STDOUT as {DATABASE_PATH}
16+
When I run `cp "{DATABASE_PATH}" original.sqlite`
17+
And I run `rm -r wp-content/database`
18+
And I run `mkdir wp-content/database`
19+
And I run `mv original.sqlite wp-content/database/.ht.sqlite`
20+
And I run `wp sqlite export backup.sql`
21+
Then the backup.sql file should contain:
22+
"""
23+
WP CLI Site
24+
"""
25+
When I run `wp sqlite import test_import.sql`
26+
And I run `wp sqlite export -`
27+
Then STDOUT should contain:
28+
"""
29+
Imported data
30+
"""
31+
And the wp-content/database/.ht.sqlite file should exist
32+
And the wp-content/database/db-path.php file should not exist
33+
34+
Scenario: Only import initializes fresh default storage
35+
When I run `wp sqlite export backup.sql`
36+
And I run `rm -r wp-content/database`
37+
And I try `wp sqlite export missing.sql`
38+
Then the return code should be 1
39+
And the missing.sql file should not exist
40+
When I try `wp sqlite tables`
41+
Then the return code should be 1
42+
And the wp-content/database directory should not exist
43+
When I try `wp sqlite import missing.sql`
44+
Then the return code should be 1
45+
And STDERR should contain:
46+
"""
47+
Unable to open file: missing.sql
48+
"""
49+
And the wp-content/database directory should not exist
50+
When I run `mkdir wp-content/database`
51+
And I run `wp sqlite import backup.sql`
52+
Then STDOUT should contain:
53+
"""
54+
Success: Imported from 'backup.sql'.
55+
"""
56+
When I run `wp sqlite tables`
57+
Then STDOUT should contain:
58+
"""
59+
wp_options
60+
"""
61+
When I run `wp eval 'echo get_option( "blogname" );'`
62+
Then STDOUT should be:
63+
"""
64+
WP CLI Site
65+
"""
66+
67+
Scenario: Use DB_PATH without changing existing storage
68+
When I run `wp eval 'echo defined( "DB_PATH" ) ? DB_PATH : FQDB;'`
69+
Then save STDOUT as {DATABASE_PATH}
70+
When I run `mkdir custom-database`
71+
And I run `cp "{DATABASE_PATH}" 'custom-database/custom;database.sqlite'`
72+
And I run `wp config set DB_PATH "dirname( __FILE__ ) . '/custom-database/custom;database.sqlite'" --raw`
73+
And I run `wp sqlite export backup.sql`
74+
And I run `wp sqlite import backup.sql`
75+
Then the custom-database/db-path.php file should not exist
76+
And the custom-database/.htaccess file should not exist
77+
And the custom-database/.ht.sqlite.lock file should not exist
78+
When I run `wp sqlite export -`
79+
Then STDOUT should contain:
80+
"""
81+
WP CLI Site
82+
"""
83+
84+
Scenario: Only import creates a missing explicit database
85+
Given a SQL dump file named "test_import.sql" with content:
86+
"""
87+
CREATE TABLE test_table (id INTEGER PRIMARY KEY, name TEXT);
88+
INSERT INTO test_table VALUES (1, 'Imported data');
89+
"""
90+
When I run `wp config set DB_PATH '{RUN_DIR}/new.sqlite'`
91+
And I try `wp sqlite export backup.sql`
92+
Then the return code should be 1
93+
And STDERR should contain:
94+
"""
95+
The SQLite database does not exist.
96+
"""
97+
And the new.sqlite file should not exist
98+
And the backup.sql file should not exist
99+
When I try `wp sqlite tables`
100+
Then the return code should be 1
101+
And the new.sqlite file should not exist
102+
When I try `wp sqlite import missing.sql`
103+
Then the return code should be 1
104+
And STDERR should contain:
105+
"""
106+
Unable to open file: missing.sql
107+
"""
108+
And the new.sqlite file should not exist
109+
When I run `wp sqlite import test_import.sql`
110+
And I run `wp sqlite tables`
111+
Then STDOUT should contain:
112+
"""
113+
test_table
114+
"""
115+
When I run `wp sqlite export -`
116+
Then STDOUT should contain:
117+
"""
118+
Imported data
119+
"""
120+
121+
Scenario: Use the legacy FQDB override
122+
When I run `wp eval 'echo defined( "DB_PATH" ) ? DB_PATH : FQDB;'`
123+
Then save STDOUT as {DB_PATH}
124+
When I run `cp "{DB_PATH}" custom.sqlite`
125+
And I run `wp config set FQDB '{RUN_DIR}/custom.sqlite'`
126+
And I run `wp sqlite export backup.sql`
127+
And I run `wp sqlite import backup.sql`
128+
And I run `wp eval 'echo get_option( "blogname" );'`
129+
Then STDOUT should be:
130+
"""
131+
WP CLI Site
132+
"""
133+
134+
Scenario: Reject an invalid DB_PATH without falling back to existing storage
135+
Given a SQL dump file named "test_import.sql" with content:
136+
"""
137+
CREATE TABLE test_table (id INTEGER PRIMARY KEY);
138+
"""
139+
When I run `wp config set DB_PATH null --raw`
140+
And I try `wp sqlite import test_import.sql`
141+
Then the return code should be 1
142+
And STDERR should contain:
143+
"""
144+
Error: Could not open the SQLite database:
145+
"""
146+
When I try `wp sqlite export backup.sql`
147+
Then the return code should be 1
148+
And the backup.sql file should not exist
149+
When I try `wp sqlite tables`
150+
Then the return code should be 1
151+
When I run `wp config delete DB_PATH`
152+
And I run `wp sqlite tables`
153+
Then STDOUT should not contain:
154+
"""
155+
test_table
156+
"""
157+
158+
Scenario: Reject an in-memory database
159+
Given a SQL dump file named "test_import.sql" with content:
160+
"""
161+
CREATE TABLE test_table (id INTEGER PRIMARY KEY);
162+
"""
163+
When I run `wp config set DB_PATH ':memory:'`
164+
And I try `wp sqlite import test_import.sql`
165+
Then the return code should be 1
166+
And STDERR should contain:
167+
"""
168+
In-memory databases are not supported.
169+
"""
170+
When I try `wp sqlite export backup.sql`
171+
Then the return code should be 1
172+
And STDERR should contain:
173+
"""
174+
In-memory databases are not supported.
175+
"""
176+
And the backup.sql file should not exist
177+
When I try `wp sqlite tables`
178+
Then the return code should be 1
179+
And STDERR should contain:
180+
"""
181+
In-memory databases are not supported.
182+
"""
183+
184+
Scenario: Report a database that cannot be opened
185+
Given a SQL dump file named "test_import.sql" with content:
186+
"""
187+
CREATE TABLE test_table (id INTEGER PRIMARY KEY);
188+
"""
189+
When I run `wp config set DB_PATH '{RUN_DIR}/missing-directory/database.sqlite'`
190+
And I try `wp sqlite import test_import.sql`
191+
Then the return code should be 1
192+
And STDERR should contain:
193+
"""
194+
Error: Could not open the SQLite database:
195+
"""

‎phpcs.xml.dist‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
<!-- For help understanding the `testVersion` configuration setting:
4040
https://github.com/PHPCompatibility/PHPCompatibility#sniffing-your-code-for-compatibility-with-specific-php-versions -->
41-
<config name="testVersion" value="5.6-"/>
41+
<config name="testVersion" value="7.4-"/>
4242

4343
<!-- Verify that everything in the global namespace is either namespaced or prefixed.
4444
See: https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#naming-conventions-prefix-everything-in-the-global-namespace -->

‎phpstan/stubs.php‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound
1010
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
11-
// phpcs:disable PHPCompatibility.FunctionDeclarations.NewReturnTypeDeclarations
12-
// phpcs:disable PHPCompatibility.FunctionDeclarations.NewParamTypeDeclarations
1311

1412
class WP_SQLite_Connection {
1513
/**
@@ -25,3 +23,9 @@ class WP_MySQL_On_SQLite extends PDO {
2523
public function get_connection(): WP_SQLite_Connection {}
2624
public function execute_sqlite_query( string $sql, array $params = array() ): PDOStatement {}
2725
}
26+
27+
class WP_SQLite_Storage {
28+
public static function with_secret_path( string $root ): self {}
29+
public function initialize(): string {}
30+
private function __construct() {}
31+
}

‎src/Import.php‎

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ class Import {
1818

1919
public function __construct() {
2020
SQLiteDatabaseIntegrationLoader::load_plugin();
21-
$this->driver = SQLiteDriverFactory::create_driver();
2221
}
2322

2423
/**
@@ -34,6 +33,13 @@ public function run( $sql_file_path, $args ) {
3433

3534
$is_stdin = '-' === $sql_file_path;
3635
$import_file = $is_stdin ? 'php://stdin' : $sql_file_path;
36+
$handle = fopen( $import_file, 'r' );
37+
if ( ! $handle ) {
38+
WP_CLI::error( "Unable to open file: $import_file" );
39+
}
40+
41+
// Create the driver after opening the input, so a missing file doesn't create a database.
42+
$this->driver = SQLiteDriverFactory::create_driver( true );
3743

3844
/*
3945
* Set default SQL mode and other options as per the "mysqldump" command.
@@ -59,7 +65,8 @@ public function run( $sql_file_path, $args ) {
5965
*/
6066
$this->driver->execute_sqlite_query( 'PRAGMA foreign_keys = OFF' );
6167

62-
$this->execute_statements( $import_file );
68+
$this->execute_statements( $handle );
69+
fclose( $handle );
6370

6471
/*
6572
* Re-enable foreign key constraints and verify integrity.
@@ -81,13 +88,13 @@ public function run( $sql_file_path, $args ) {
8188
/**
8289
* Execute SQL statements from an SQL dump file.
8390
*
84-
* @param $import_file
91+
* @param resource $handle The SQL dump file handle.
8592
*
8693
* @return void
8794
* @throws Exception
8895
*/
89-
protected function execute_statements( $import_file ) {
90-
foreach ( $this->parse_statements( $import_file ) as $statement ) {
96+
protected function execute_statements( $handle ) {
97+
foreach ( $this->parse_statements( $handle ) as $statement ) {
9198
try {
9299
$this->driver->query( $statement );
93100
} catch ( Exception $e ) {
@@ -112,18 +119,11 @@ protected function execute_statements( $import_file ) {
112119

113120
/**
114121
* Parse SQL statements from an SQL dump file.
115-
* @param string $sql_file_path The path to the SQL dump file.
122+
* @param resource $handle The SQL dump file handle.
116123
*
117124
* @return Generator A generator that yields SQL statements.
118125
*/
119-
public function parse_statements( $sql_file_path ) {
120-
121-
$handle = fopen( $sql_file_path, 'r' );
122-
123-
if ( ! $handle ) {
124-
WP_CLI::error( "Unable to open file: $sql_file_path" );
125-
}
126-
126+
public function parse_statements( $handle ) {
127127
$starting_quote = null;
128128
$in_comment = false;
129129
$buffer = '';
@@ -200,7 +200,5 @@ public function parse_statements( $sql_file_path ) {
200200
if ( ! empty( $buffer ) ) {
201201
yield $buffer;
202202
}
203-
204-
fclose( $handle );
205203
}
206204
}

‎src/SQLiteDatabaseIntegrationLoader.php‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,17 @@ public static function load_plugin() {
8686
define( 'SQLITE_DB_DROPIN_VERSION', $sqlite_plugin_version ); // phpcs:ignore
8787
}
8888

89-
require_once $plugin_directory . '/constants.php';
89+
try {
90+
require_once $plugin_directory . '/constants.php';
91+
} catch ( \Throwable $exception ) {
92+
// Newer plugin releases reject invalid database settings.
93+
WP_CLI::error( 'Could not open the SQLite database: ' . $exception->getMessage() );
94+
}
95+
$storage_file = $plugin_directory . '/wp-includes/sqlite/class-wp-sqlite-storage.php';
96+
if ( file_exists( $storage_file ) ) {
97+
require_once $storage_file;
98+
}
99+
90100
require_once $plugin_directory . '/wp-includes/database/load.php';
91101
}
92102
}

0 commit comments

Comments
 (0)