Conversation
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Use Utils\esc_cmd to properly escape database path arguments - Add is_sqlite3_available() helper to check for sqlite3 binary - Add preflight check with clear error message if sqlite3 not found - Addresses security concerns about unescaped shell commands Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| // Don't bother warning for term relationships (which is just 3 int columns) or SQLite. | ||
| } elseif ( ! preg_match( '/_term_relationships$/', $table ) && ! $this->is_sqlite() ) { | ||
| WP_CLI::warning( $primary_keys ? "No text columns for table '$table' - skipped." : "No primary key or text columns for table '$table' - skipped." ); |
There was a problem hiding this comment.
In search(), the new condition && ! $this->is_sqlite() is inside the per-table loop. is_sqlite() can hit the filesystem (drop-in inspection), so calling it repeatedly can add avoidable overhead when searching many tables. Cache the result once at the start of search() (e.g. $is_sqlite = $this->is_sqlite();) and use that variable here.
| static $available = null; | ||
|
|
||
| if ( null === $available ) { | ||
| $result = \WP_CLI\Process::create( 'which sqlite3', null, null )->run(); |
There was a problem hiding this comment.
is_sqlite3_available() uses which sqlite3 to detect the binary. which isn’t reliably available across environments (e.g. some minimal containers, Windows), so this check can incorrectly report sqlite3 as unavailable. Consider checking availability by executing sqlite3 --version (via /usr/bin/env sqlite3) and checking the return code, or otherwise using a more portable command-existence check.
| $result = \WP_CLI\Process::create( 'which sqlite3', null, null )->run(); | |
| $result = \WP_CLI\Process::create( '/usr/bin/env sqlite3 --version', null, null )->run(); |
| if ( ! file_exists( $db_path ) ) { | ||
| WP_CLI::error( 'Database does not exist.' ); | ||
| } | ||
|
|
There was a problem hiding this comment.
sqlite_export() invokes the sqlite3 CLI but doesn’t perform the same preflight check used by sqlite_create()/sqlite_reset(). If sqlite3 isn’t installed, this will fail later with a generic “Could not export database” error. Please call the sqlite3 availability check up front and emit the clearer error message before attempting any export steps.
| if ( ! $this->is_sqlite3_available() ) { | |
| WP_CLI::error( 'The sqlite3 binary could not be found. Please install sqlite3 to use the export command.' ); | |
| } |
| $temp_db = tempnam( sys_get_temp_dir(), 'temp.db' ); | ||
| $export_db = tempnam( sys_get_temp_dir(), 'export.db' ); |
There was a problem hiding this comment.
$export_db is created via tempnam() but never used. This leaves an unnecessary temporary file on disk and makes the export logic harder to follow. Remove this variable (or use it if it was intended for a later step).
| $temp_db = tempnam( sys_get_temp_dir(), 'temp.db' ); | |
| $export_db = tempnam( sys_get_temp_dir(), 'export.db' ); | |
| $temp_db = tempnam( sys_get_temp_dir(), 'temp.db' ); |
| $export_db = tempnam( sys_get_temp_dir(), 'export.db' ); | ||
|
|
||
| copy( $db_path, $temp_db ); |
There was a problem hiding this comment.
The export temp file creation/copy isn’t checked for failure (tempnam() can return false; copy() can fail). If either fails, subsequent sqlite3 commands will run against an invalid path and produce confusing errors. Please validate these return values and bail with a clear error, cleaning up any created temp files.
| $export_db = tempnam( sys_get_temp_dir(), 'export.db' ); | |
| copy( $db_path, $temp_db ); | |
| if ( false === $temp_db ) { | |
| WP_CLI::error( 'Could not create temporary database file for export.' ); | |
| } | |
| $export_db = tempnam( sys_get_temp_dir(), 'export.db' ); | |
| if ( false === $export_db ) { | |
| // Clean up any previously created temporary database file. | |
| @unlink( $temp_db ); | |
| WP_CLI::error( 'Could not create temporary export file.' ); | |
| } | |
| if ( ! @copy( $db_path, $temp_db ) ) { | |
| // Clean up temporary files if the copy operation fails. | |
| @unlink( $temp_db ); | |
| @unlink( $export_db ); | |
| WP_CLI::error( 'Could not copy database to temporary file for export.' ); | |
| } |
| if ( ! file_exists( $db_path ) ) { | ||
| WP_CLI::error( 'Database does not exist.' ); | ||
| } | ||
|
|
There was a problem hiding this comment.
sqlite_import() invokes the sqlite3 CLI but doesn’t check for sqlite3 availability first. For consistency with create/reset (and to match the PR’s “preflight validation” goal), perform the sqlite3 binary check at the start and show the clearer error message before doing any temp-file work.
| if ( ! $this->is_sqlite3_available() ) { | |
| WP_CLI::error( 'The sqlite3 CLI binary could not be found. Please ensure it is installed and available on your PATH.' ); | |
| } |
|
|
||
| $command = Utils\esc_cmd( | ||
| implode( ' ', array_fill( 0, count( $command_parts ), '%s' ) ), | ||
| ...$command_parts | ||
| ); | ||
|
|
||
| $command .= ' < ' . escapeshellarg( $import_file ); | ||
|
|
||
| WP_CLI::debug( "Running shell command: {$command}", 'db' ); | ||
|
|
||
| $result = \WP_CLI\Process::create( $command, null, null )->run(); | ||
|
|
There was a problem hiding this comment.
The import command uses shell redirection ($command .= ' < ...'). This relies on a shell being available and makes the command line harder to reason about. Prefer using sqlite3’s built-in .read command (passed as an argument) or providing the SQL file contents via the process’ STDIN, so the import works without shell-specific redirection.
| $command = Utils\esc_cmd( | |
| implode( ' ', array_fill( 0, count( $command_parts ), '%s' ) ), | |
| ...$command_parts | |
| ); | |
| $command .= ' < ' . escapeshellarg( $import_file ); | |
| WP_CLI::debug( "Running shell command: {$command}", 'db' ); | |
| $result = \WP_CLI\Process::create( $command, null, null )->run(); | |
| $command_parts[] = '.read'; | |
| $command_parts[] = $import_file; | |
| // Build debug-friendly command string without using shell redirection. | |
| $debug_command = Utils\esc_cmd( | |
| implode( ' ', array_fill( 0, count( $command_parts ), '%s' ) ), | |
| ...$command_parts | |
| ); | |
| WP_CLI::debug( "Running shell command: {$debug_command}", 'db' ); | |
| $result = \WP_CLI\Process::create( $command_parts, null, null )->run(); |
Add SQLite compatibility to
wp dbcommandsThis PR implements SQLite support for database commands when using the SQLite Database Integration plugin. Commands now detect SQLite via
DB_ENGINEconstant,SQLITE_DB_DROPIN_VERSION, or db.php drop-in inspection.Changes
New
DB_Command_SQLitetrait - Isolated SQLite operations using PDO:create/drop/reset- File-based database lifecycle with sqlite3 binaryquery- Direct PDO execution with formatted output (now supports--skip-column-names)export/import- SQL dump/restore with proper identifier escapingsize- File size calculationFQDB,FQDBDIR,DB_FILEconstantsModified
DB_Commandmethods - Detect SQLite and route accordingly:create,drop,reset,query,export,import- Full SQLite implementationsize- SQLite file size vs MySQL information_schema querycheck,optimize,repair,cli- Warning messages (not applicable to SQLite)tables,prefix,columns,search,clean- Unchanged (work via $wpdb)Test scenarios - Updated to support SQLite for
--skip-column-namesflagDocumentation (
README.md) - Supported commands, configuration, detection methodsSecurity Updates
Utils\esc_cmdto properly escape argumentsRecent Updates
--skip-column-namessupport for SQLite: Thesqlite_query()method now accepts$assoc_argsand respects the--skip-column-namesflagdisplay_table()withFormatterclass: Using the standard WP-CLI Formatter class for consistency with other commands--skip-column-namestest as SQLite now supports itExample Usage
Technical Notes
sqlite3CLI binary for create/drop/reset/export/import operationsafter_wp_config_loadstagePDO::quote()wp-content/database/.ht.sqliteUtils\esc_cmdFixes #234
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.