diff --git a/src/wp-includes/option.php b/src/wp-includes/option.php index 7cb4736c2840b..5754ecf891564 100644 --- a/src/wp-includes/option.php +++ b/src/wp-includes/option.php @@ -1633,53 +1633,84 @@ function set_transient( $transient, $value, $expiration = 0 ) { * * @global wpdb $wpdb WordPress database abstraction object. * - * @param bool $force_db Optional. Force cleanup to run against the database even when an external object cache is used. + * @param bool $force_db Optional. Force cleanup to run against the database even when an + * external object cache is used. Default false. + * @param string|bool $prefix Optional. Transient name prefix. When provided, only expired transients + * matching this prefix will be deleted. This parameter is only applied + * when not using an external object cache or when `$force_db` is true. + * Default false. */ -function delete_expired_transients( $force_db = false ) { +function delete_expired_transients( $force_db = false, $prefix = false ) { global $wpdb; + // Skip DB cleanup when using external object cache unless forced. if ( ! $force_db && wp_using_ext_object_cache() ) { return; } + $transient_like = $wpdb->esc_like( + '_transient_' . ( $prefix ? $prefix : '' ) + ) . '%'; + + $timeout_like = $wpdb->esc_like( '_transient_timeout_' ) . '%'; + $wpdb->query( $wpdb->prepare( - "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b - WHERE a.option_name LIKE %s - AND a.option_name NOT LIKE %s - AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) - AND b.option_value < %d", - $wpdb->esc_like( '_transient_' ) . '%', - $wpdb->esc_like( '_transient_timeout_' ) . '%', + "DELETE a, b + FROM {$wpdb->options} a + JOIN {$wpdb->options} b + ON b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) + WHERE a.option_name LIKE %s + AND a.option_name NOT LIKE %s + AND b.option_value < %d", + $transient_like, + $timeout_like, time() ) ); if ( ! is_multisite() ) { - // Single site stores site transients in the options table. + // Single-site: site transients stored in options table. + $site_transient_like = $wpdb->esc_like( + '_site_transient_' . ( $prefix ? $prefix : '' ) + ) . '%'; + + $site_timeout_like = $wpdb->esc_like( '_site_transient_timeout_' ) . '%'; + $wpdb->query( $wpdb->prepare( - "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b - WHERE a.option_name LIKE %s - AND a.option_name NOT LIKE %s - AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) ) - AND b.option_value < %d", - $wpdb->esc_like( '_site_transient_' ) . '%', - $wpdb->esc_like( '_site_transient_timeout_' ) . '%', + "DELETE a, b + FROM {$wpdb->options} a + JOIN {$wpdb->options} b + ON b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) ) + WHERE a.option_name LIKE %s + AND a.option_name NOT LIKE %s + AND b.option_value < %d", + $site_transient_like, + $site_timeout_like, time() ) ); + } elseif ( is_main_site() && is_main_network() ) { - // Multisite stores site transients in the sitemeta table. + // Multisite: site transients stored in sitemeta. + $site_transient_like = $wpdb->esc_like( + '_site_transient_' . ( $prefix ? $prefix : '' ) + ) . '%'; + + $site_timeout_like = $wpdb->esc_like( '_site_transient_timeout_' ) . '%'; + $wpdb->query( $wpdb->prepare( - "DELETE a, b FROM {$wpdb->sitemeta} a, {$wpdb->sitemeta} b - WHERE a.meta_key LIKE %s - AND a.meta_key NOT LIKE %s - AND b.meta_key = CONCAT( '_site_transient_timeout_', SUBSTRING( a.meta_key, 17 ) ) - AND b.meta_value < %d", - $wpdb->esc_like( '_site_transient_' ) . '%', - $wpdb->esc_like( '_site_transient_timeout_' ) . '%', + "DELETE a, b + FROM {$wpdb->sitemeta} a + JOIN {$wpdb->sitemeta} b + ON b.meta_key = CONCAT( '_site_transient_timeout_', SUBSTRING( a.meta_key, 17 ) ) + WHERE a.meta_key LIKE %s + AND a.meta_key NOT LIKE %s + AND b.meta_value < %d", + $site_transient_like, + $site_timeout_like, time() ) );