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.
| 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 <
B622
code class="notranslate">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.' ); | |
| } |
|
|
||
| $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(); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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.