Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"db search",
"db tables",
"db size",
"db columns"
"db columns",
"db status"
]
},
"autoload": {
Expand Down
122 changes: 122 additions & 0 deletions features/db-status.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
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
"""
Comment on lines +3 to +56

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
      """


Scenario: Run db status with MySQL defaults to check the database
Given a WP install

When I run `wp db status --defaults`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Check Status: OK
"""

Scenario: Run db status with --no-defaults to check the database
Given a WP install

When I run `wp db status --no-defaults`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Check Status: OK
"""

Scenario: Run db status with passed-in options
Given a WP install

When I run `wp db status --dbuser=wp_cli_test`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Check Status: OK
"""

When I run `wp db status --dbpass=password1`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Check Status: OK
"""

When I run `wp db status --dbuser=wp_cli_test --dbpass=password1`
Then STDOUT should contain:
"""
Database Name:
"""
And STDOUT should contain:
"""
Check Status: OK
"""

When I try `wp db status --dbuser=no_such_user`
Then the return code should not be 0

When I try `wp db status --dbpass=no_such_pass`
Then the return code should not be 0

146 changes: 146 additions & 0 deletions src/DB_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,152 @@ public function prefix() {
WP_CLI::log( $wpdb->prefix );
}

/**
* 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.
*
* ## OPTIONS
*
* [--dbuser=<value>]
* : Username to pass to mysql. Defaults to DB_USER.
*
* [--dbpass=<value>]
* : Password to pass to mysql. Defaults to DB_PASSWORD.
*
* [--defaults]
* : Loads the environment's MySQL option files. Default behavior is to skip loading them to avoid failures due to misconfiguration.
*
* ## 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
*
* @when before_wp_load
*/
public function status( $_, $assoc_args ) {
global $wpdb;

// Get database name.
$db_name = DB_NAME;

// Get table count.
$table_count = count( Utils\wp_get_table_names( [], [ 'scope' => 'all' ] ) );

// Get total database size.
$db_size_bytes = $wpdb->get_var(
$wpdb->prepare(
'SELECT SUM(data_length + index_length) FROM information_schema.TABLES where table_schema = %s GROUP BY table_schema;',
DB_NAME
)
);

// Format size to human-readable.
if ( empty( $db_size_bytes ) || $db_size_bytes <= 0 ) {
$db_size = '0 B';
} else {
$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;
}

// Get prefix.
$prefix = $wpdb->prefix;

// 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';
}
// Run database check silently to get status.
if ( $table_count > 0 ) {
$command = sprintf(
'/usr/bin/env %s%s %s',
Utils\get_sql_check_command(),
$this->get_defaults_flag_string( $assoc_args ),
'%s'
);
list( $stdout, $stderr, $exit_code ) = self::run(
Utils\esc_cmd( $command, DB_NAME ),
array_merge( [ 'check' => true ], $assoc_args ),
false
);
$check_status = ( 0 === $exit_code ) ? 'OK' : 'Error';
} else {
// No tables to check; mark status as not applicable.
$check_status = 'N/A';
}
// Output formatted status.
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 ) );
Comment on lines +1386 to +1393

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 ) );
		}

}

/**
* Finds a string in the database.
*
Expand Down
Loading