Skip to content

Conversation

Copy link

Copilot AI commented Feb 1, 2026

Adds single command to retrieve database health and configuration info, consolidating what previously required running wp db size, wp db tables, wp db prefix, and wp db check separately.

Implementation

  • New status() method in DB_Command class

    • Queries information_schema.TABLES for size, engine, charset, collation
    • Runs mysqlcheck for health status
    • Formats output as aligned key-value pairs
  • Command registration in composer.json

  • Behat test coverage for output validation

Output

Database Name:     wp_cli_test
Tables:            54
Total Size:        312 KB
Prefix:            wp_
Engine:            InnoDB
Charset:           utf8mb4
Collation:         utf8mb4_unicode_ci
Check Status:      OK

Notes

  • Uses SI units (1000-based) for size formatting, matching existing wp db size --human-readable behavior
  • Handles edge cases: zero/null size, missing tables, check failures
  • Works without loading WordPress (@when after_wp_load), suitable for automation
Original prompt

This section details on the original issue you should resolve

<issue_title>Add wp db status command for quick database health overview</issue_title>
<issue_description>I would like to suggesta new WP-CLI command: wp db status

The command would provide a concise, read-only overview of the current WordPress database health and configuration in a single output, reducing the need to run multiple separate commands.

For support, debugging, and hosting diagnostics, it would be beneficial to have a single, non-destructive command that aggregates key database information into one quick report.

Currently, checking database status requires running several commands such as: wp db size, wp db tables, wp db prefix, wp db check, etc.

My Proposed Command:

wp db status

Output Example:

Database Name:     wp_mehrazmorshed
Tables:            54
Total Size:        312 MB
Prefix:            wp_
Engine:            InnoDB
Charset:           utf8mb4
Collation:         utf8mb4_unicode_ci
Check Status:      OK
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@ernilambar</author><body>
It will be included in next "3.0.0" version.</body></comment_new>
<comment_new><author>@swissspidy</author><body>
Yeah you can already try it out in the nightly release.

One difference is that the site-health command involves loading WordPress, whereas the db commands usually also work when WordPress is not even installed yet.</body></comment_new>
<comment_new><author>@ernilambar</author><body>
Although database info is available from "site-health" command I am positive is adding specific db command which gives info without loading whole WordPress. It would be helpful in scripting.</body></comment_new>
</comments>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits February 1, 2026 16:10
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Copilot AI changed the title [WIP] Add wp db status command for database health overview Add wp db status command for database health overview Feb 1, 2026
Copilot AI requested a review from swissspidy February 1, 2026 16:13
@codecov
Copy link

codecov bot commented Feb 1, 2026

Codecov Report

❌ Patch coverage is 98.03922% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/DB_Command.php 98.03% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@swissspidy swissspidy requested a review from Copilot February 2, 2026 01:50
@swissspidy
Copy link
Member

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new wp db status command, which provides a convenient overview of the database's health and configuration. The implementation is solid and consolidates information from several other commands. My review focuses on improving the new Behat tests for better robustness and refactoring parts of the new status() method to enhance code clarity, reduce duplication, and improve maintainability.

Comment on lines +3 to +56
Scenario: Display database status for a WordPress install
Given a WP install

When I run `wp db status`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Tables:
"""
And STDOUT should contain:
"""
Total Size:
"""
And STDOUT should contain:
"""
Prefix: wp_
"""
And STDOUT should contain:
"""
Engine:
"""
And STDOUT should contain:
"""
Charset:
"""
And STDOUT should contain:
"""
Collation:
"""
And STDOUT should contain:
"""
Check Status:
"""

Scenario: Verify database status shows correct database name
Given a WP install

When I run `wp db status`
Then STDOUT should contain:
"""
wp_cli_test
"""

Scenario: Verify database status shows check status as OK
Given a WP install

When I run `wp db status`
Then STDOUT should contain:
"""
Check Status: OK
"""

Choose a reason for hiding this comment

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

medium

The Behat tests are split across multiple scenarios, each testing a small part of the command's output. This makes the tests less robust as they don't verify the output structure as a whole. Combining these into a single, more comprehensive scenario would improve test clarity and strength. Using should match with regular expressions for dynamic values like table count and size will also make the assertions more precise and resilient to minor changes.

  Scenario: Display database status for a WordPress install
    Given a WP install

    When I run `wp db status`
    Then STDOUT should contain:
      """
      Database Name:     wp_cli_test
      """
    And STDOUT should match /^Tables:\s+\d+$/m
    And STDOUT should match /^Total Size:\s+[\d.]+ \wB$/m
    And STDOUT should contain:
      """
      Prefix:            wp_
      """
    And STDOUT should match /^Engine:\s+\w+$/m
    And STDOUT should match /^Charset:\s+\w+$/m
    And STDOUT should match /^Collation:\s+\w+$/m
    And STDOUT should contain:
      """
      Check Status:      OK
      """

Comment on lines +1290 to +1298
if ( empty( $db_size_bytes ) || $db_size_bytes <= 0 ) {
$db_size = '0 B';
} else {
$size_key = floor( log( (float) $db_size_bytes ) / log( 1000 ) );
$sizes = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
$size_format = isset( $sizes[ $size_key ] ) ? $sizes[ $size_key ] : $sizes[0];
$divisor = pow( 1000, $size_key );
$db_size = round( (int) $db_size_bytes / $divisor, 2 ) . ' ' . $size_format;
}

Choose a reason for hiding this comment

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

medium

This block for formatting the database size into a human-readable string is very similar to logic within the size() method. To avoid code duplication and improve maintainability, consider extracting this logic into a private helper function that can be reused in both places. For now, the logic can be slightly simplified for better readability by inverting the conditional and using the null coalescing operator.

		if ( ! empty( $db_size_bytes ) && $db_size_bytes > 0 ) {
			$size_key    = floor( log( (float) $db_size_bytes ) / log( 1000 ) );
			$sizes       = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
			$size_format = $sizes[ $size_key ] ?? $sizes[0];
			$divisor     = pow( 1000, $size_key );
			$db_size     = round( (int) $db_size_bytes / $divisor, 2 ) . ' ' . $size_format;
		} else {
			$db_size = '0 B';
		}

Comment on lines +1339 to +1346
WP_CLI::log( sprintf( '%-18s %s', 'Database Name:', $db_name ) );
WP_CLI::log( sprintf( '%-18s %d', 'Tables:', $table_count ) );
WP_CLI::log( sprintf( '%-18s %s', 'Total Size:', $db_size ) );
WP_CLI::log( sprintf( '%-18s %s', 'Prefix:', $prefix ) );
WP_CLI::log( sprintf( '%-18s %s', 'Engine:', $engine ) );
WP_CLI::log( sprintf( '%-18s %s', 'Charset:', $charset ) );
WP_CLI::log( sprintf( '%-18s %s', 'Collation:', $collation ) );
WP_CLI::log( sprintf( '%-18s %s', 'Check Status:', $check_status ) );

Choose a reason for hiding this comment

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

medium

The block of WP_CLI::log() calls for outputting the status is repetitive. This can be refactored to be data-driven by storing the status information in an associative array and then iterating over it to generate the output. This approach is more maintainable and makes it easier to add or modify status fields in the future.

		$status_items = [
			'Database Name' => $db_name,
			'Tables'        => $table_count,
			'Total Size'    => $db_size,
			'Prefix'        => $prefix,
			'Engine'        => $engine,
			'Charset'       => $charset,
			'Collation'     => $collation,
			'Check Status'  => $check_status,
		];

		foreach ( $status_items as $label => $value ) {
			WP_CLI::log( sprintf( '%-18s %s', $label . ':', $value ) );
		}

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new wp db status command that provides a consolidated view of database health and configuration information in a single command, eliminating the need to run multiple separate database commands (wp db size, wp db tables, wp db prefix, wp db check).

Changes:

  • New status() method in DB_Command class that queries database metadata and runs health checks
  • Command registration in composer.json
  • Behat test coverage validating output format

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
src/DB_Command.php Implements the status() method that retrieves database name, table count, size, prefix, engine, charset, collation, and check status, formatting output as aligned key-value pairs
features/db-status.feature Adds Behat test scenarios to verify the command outputs all expected fields and displays correct values
composer.json Registers the new db status command in the commands array

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

);

$check_status = ( 0 === $exit_code ) ? 'OK' : 'Error';

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The database check silently ignores stderr output. When the check fails (exit_code is non-zero), the user only sees "Error" but doesn't get any details about what went wrong. Consider logging or displaying the stderr output when the check fails to provide more actionable information for debugging database issues. This would be especially helpful since this command is intended for database health diagnostics.

Suggested change
if ( 0 !== $exit_code && ! empty( trim( (string) $stderr ) ) ) {
WP_CLI::log( sprintf( '%-18s %s', 'Check Details:', trim( (string) $stderr ) ) );
}

Copilot uses AI. Check for mistakes.
Comment on lines +1303 to +1321
// Get engine, charset, and collation from information_schema (using a common table).
$table_info = $wpdb->get_row(
$wpdb->prepare(
'SELECT ENGINE as engine, CCSA.character_set_name as charset, TABLE_COLLATION as collation '
. 'FROM information_schema.TABLES T '
. 'LEFT JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY CCSA '
. 'ON CCSA.collation_name = T.table_collation '
. 'WHERE T.table_schema = %s '
. 'AND T.table_name LIKE %s '
. 'LIMIT 1',
DB_NAME,
$wpdb->esc_like( $prefix ) . '%'
)
);

$engine = $table_info && isset( $table_info->engine ) ? $table_info->engine : 'N/A';
$charset = $table_info && isset( $table_info->charset ) ? $table_info->charset : 'N/A';
$collation = $table_info && isset( $table_info->collation ) ? $table_info->collation : 'N/A';

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The query for engine, charset, and collation retrieves information from only one table (the first table matching the prefix). In a multi-engine or mixed-charset database, this could be misleading. If tables have different engines or charsets, the command would only show the values from one arbitrary table. Consider either documenting this limitation in the command's docblock (e.g., "Engine, charset, and collation are determined from the first table with the configured prefix") or implementing logic to detect if multiple values exist and display "Mixed" when appropriate.

Suggested change
// Get engine, charset, and collation from information_schema (using a common table).
$table_info = $wpdb->get_row(
$wpdb->prepare(
'SELECT ENGINE as engine, CCSA.character_set_name as charset, TABLE_COLLATION as collation '
. 'FROM information_schema.TABLES T '
. 'LEFT JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY CCSA '
. 'ON CCSA.collation_name = T.table_collation '
. 'WHERE T.table_schema = %s '
. 'AND T.table_name LIKE %s '
. 'LIMIT 1',
DB_NAME,
$wpdb->esc_like( $prefix ) . '%'
)
);
$engine = $table_info && isset( $table_info->engine ) ? $table_info->engine : 'N/A';
$charset = $table_info && isset( $table_info->charset ) ? $table_info->charset : 'N/A';
$collation = $table_info && isset( $table_info->collation ) ? $table_info->collation : 'N/A';
// Get engine, charset, and collation from information_schema across all tables with the prefix.
$table_info = $wpdb->get_row(
$wpdb->prepare(
'SELECT '
. 'COUNT(DISTINCT ENGINE) AS engine_count, '
. 'MIN(ENGINE) AS engine, '
. 'COUNT(DISTINCT CCSA.character_set_name) AS charset_count, '
. 'MIN(CCSA.character_set_name) AS charset, '
. 'COUNT(DISTINCT TABLE_COLLATION) AS collation_count, '
. 'MIN(TABLE_COLLATION) AS collation '
. 'FROM information_schema.TABLES T '
. 'LEFT JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY CCSA '
. 'ON CCSA.collation_name = T.table_collation '
. 'WHERE T.table_schema = %s '
. 'AND T.table_name LIKE %s',
DB_NAME,
$wpdb->esc_like( $prefix ) . '%'
)
);
if ( $table_info ) {
$engine_count = isset( $table_info->engine_count ) ? (int) $table_info->engine_count : 0;
$charset_count = isset( $table_info->charset_count ) ? (int) $table_info->charset_count : 0;
$collation_count = isset( $table_info->collation_count ) ? (int) $table_info->collation_count : 0;
if ( $engine_count > 1 ) {
$engine = 'Mixed';
} elseif ( isset( $table_info->engine ) && '' !== $table_info->engine ) {
$engine = $table_info->engine;
} else {
$engine = 'N/A';
}
if ( $charset_count > 1 ) {
$charset = 'Mixed';
} elseif ( isset( $table_info->charset ) && '' !== $table_info->charset ) {
$charset = $table_info->charset;
} else {
$charset = 'N/A';
}
if ( $collation_count > 1 ) {
$collation = 'Mixed';
} elseif ( isset( $table_info->collation ) && '' !== $table_info->collation ) {
$collation = $table_info->collation;
} else {
$collation = 'N/A';
}
} else {
$engine = 'N/A';
$charset = 'N/A';
$collation = 'N/A';
}

Copilot uses AI. Check for mistakes.
Comment on lines +1323 to +1337
$check_args = [];
$command = sprintf(
'/usr/bin/env %s%s %s',
Utils\get_sql_check_command(),
$this->get_defaults_flag_string( $check_args ),
'%s'
);
list( $stdout, $stderr, $exit_code ) = self::run(
Utils\esc_cmd( $command, DB_NAME ),
[ 'check' => true ],
false
);

$check_status = ( 0 === $exit_code ) ? 'OK' : 'Error';

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

When a database has no tables with the configured prefix, the query at lines 1304-1316 will return NULL for $table_info, and lines 1318-1320 will correctly set engine, charset, and collation to 'N/A'. However, when there are NO tables at all in the database (not even with a different prefix), this query would still return NULL but the Check Status would likely fail or behave unexpectedly since mysqlcheck needs tables to check. Consider adding a check for zero tables and either skipping the database check or displaying 'N/A' for Check Status when $table_count is 0.

Suggested change
$check_args = [];
$command = sprintf(
'/usr/bin/env %s%s %s',
Utils\get_sql_check_command(),
$this->get_defaults_flag_string( $check_args ),
'%s'
);
list( $stdout, $stderr, $exit_code ) = self::run(
Utils\esc_cmd( $command, DB_NAME ),
[ 'check' => true ],
false
);
$check_status = ( 0 === $exit_code ) ? 'OK' : 'Error';
if ( $table_count > 0 ) {
$check_args = [];
$command = sprintf(
'/usr/bin/env %s%s %s',
Utils\get_sql_check_command(),
$this->get_defaults_flag_string( $check_args ),
'%s'
);
list( $stdout, $stderr, $exit_code ) = self::run(
Utils\esc_cmd( $command, DB_NAME ),
[ 'check' => true ],
false
);
$check_status = ( 0 === $exit_code ) ? 'OK' : 'Error';
} else {
// No tables to check; mark status as not applicable.
$check_status = 'N/A';
}

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +56
Feature: Display database status overview

Scenario: Display database status for a WordPress install
Given a WP install

When I run `wp db status`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Tables:
"""
And STDOUT should contain:
"""
Total Size:
"""
And STDOUT should contain:
"""
Prefix: wp_
"""
And STDOUT should contain:
"""
Engine:
"""
And STDOUT should contain:
"""
Charset:
"""
And STDOUT should contain:
"""
Collation:
"""
And STDOUT should contain:
"""
Check Status:
"""

Scenario: Verify database status shows correct database name
Given a WP install

When I run `wp db status`
Then STDOUT should contain:
"""
wp_cli_test
"""

Scenario: Verify database status shows check status as OK
Given a WP install

When I run `wp db status`
Then STDOUT should contain:
"""
Check Status: OK
"""
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The test scenarios don't verify that the --dbuser, --dbpass, and --defaults options work with the status command. Looking at similar commands like db check (features/db-check.feature:42-138), these options are tested to ensure they properly override credentials and handle authentication failures. Consider adding test scenarios to verify these parameters work correctly with wp db status, especially since the current implementation has a bug where these parameters aren't being passed through.

Copilot uses AI. Check for mistakes.
Comment on lines +1323 to +1334
$check_args = [];
$command = sprintf(
'/usr/bin/env %s%s %s',
Utils\get_sql_check_command(),
$this->get_defaults_flag_string( $check_args ),
'%s'
);
list( $stdout, $stderr, $exit_code ) = self::run(
Utils\esc_cmd( $command, DB_NAME ),
[ 'check' => true ],
false
);
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

Empty $check_args array is passed to get_defaults_flag_string(), but this array is never populated with potential dbuser or dbpass parameters that might have been passed to the command. If the status command is invoked with --dbuser or --dbpass options (similar to other db commands like check or optimize), these credentials won't be passed to the mysqlcheck command, causing authentication failures. The status method should accept $assoc_args parameters and pass them through to the check command, similar to how the check() method works at line 252-266.

Copilot uses AI. Check for mistakes.
*
* @when after_wp_load
*/
public function status() {
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The method signature is missing parameters. Looking at other command methods in the class, they accept $_ (positional arguments) and $assoc_args (associative arguments). The status method should have the signature public function status( $_, $assoc_args ) to be consistent with other commands in this class and to support passing options like --dbuser, --dbpass, and --defaults as documented in similar commands.

Suggested change
public function status() {
public function status( $_, $assoc_args ) {

Copilot uses AI. Check for mistakes.
Comment on lines +1250 to +1269
/**
* Displays a quick database status overview.
*
* Shows key database information including name, table count, size,
* prefix, engine, charset, collation, and health check status. This
* command is useful for getting a quick snapshot of database health
* without needing to run multiple separate commands.
*
* ## EXAMPLES
*
* $ wp db status
* Database Name: wp_cli_test
* Tables: 54
* Total Size: 312 KB
* Prefix: wp_
* Engine: InnoDB
* Charset: utf8mb4
* Collation: utf8mb4_unicode_ci
* Check Status: OK
*
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

The command documentation should include OPTIONS section documenting the --dbuser, --dbpass, and --defaults parameters that are supported by other database commands. These options are standard across all db commands that interact with MySQL directly (check, optimize, repair, etc.) and users would expect them to work here as well for consistency.

Copilot uses AI. Check for mistakes.
* Collation: utf8mb4_unicode_ci
* Check Status: OK
*
* @when after_wp_load
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

There's a discrepancy between the PR description and the code. The PR description states the command "Works without loading WordPress (@when after_wp_load), suitable for automation", but having @when after_wp_load means WordPress DOES get loaded. The statement seems to be backwards. Either the annotation should be removed (to inherit after_wp_config_load from the class level), or the PR description needs correction to accurately reflect that WordPress is loaded.

Copilot uses AI. Check for mistakes.
Comment on lines +1293 to +1297
$size_key = floor( log( (float) $db_size_bytes ) / log( 1000 ) );
$sizes = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
$size_format = isset( $sizes[ $size_key ] ) ? $sizes[ $size_key ] : $sizes[0];
$divisor = pow( 1000, $size_key );
$db_size = round( (int) $db_size_bytes / $divisor, 2 ) . ' ' . $size_format;
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

Type casting issue: The size calculation casts $db_size_bytes to (int) which could overflow for very large databases on 32-bit systems. Additionally, the log calculation uses (float) cast, but on line 1297 the division uses (int) cast. For consistency with the existing size command implementation (see line 1196 in the codebase), the division should cast to (int) the bytes value, or better yet, avoid casting altogether since PHP handles numeric strings correctly in arithmetic operations.

Suggested change
$size_key = floor( log( (float) $db_size_bytes ) / log( 1000 ) );
$sizes = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
$size_format = isset( $sizes[ $size_key ] ) ? $sizes[ $size_key ] : $sizes[0];
$divisor = pow( 1000, $size_key );
$db_size = round( (int) $db_size_bytes / $divisor, 2 ) . ' ' . $size_format;
$size_key = floor( log( $db_size_bytes ) / log( 1000 ) );
$sizes = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
$size_format = isset( $sizes[ $size_key ] ) ? $sizes[ $size_key ] : $sizes[0];
$divisor = pow( 1000, $size_key );
$db_size = round( $db_size_bytes / $divisor, 2 ) . ' ' . $size_format;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add wp db status command for quick database health overview

2 participants