fix(2meet-data-optimizer): archive sweep copies each trashed meta row once

- sweep() skips meta rows already in wpdo_archive (NOT EXISTS on
  original_meta_id): it copied the same trashed meta again every day and
  wpdo_archive has no unique key (dev20: 300 rows for 150 meta rows)
- TMDO_Zone_Archive::remove_duplicates() keeps the first copy per
  original_meta_id (rows without one are left alone); install() runs it,
  so `wp tmdo install` cleans existing sites
- wpdo_archive gets KEY idx_original_meta for the lookup (new installs;
  existing sites via `wp tmdo install`). SCHEMA_VERSION is not bumped, so
  no web request runs dbDelta
- Verify: ZoneArchiveIntegrationTest (2 new); dev20 read-only check: 150 of
  300 rows would be removed; unit 709, integration 608, phpcs 0, stan OK
- PR: PR-136 (manual F-6)
This commit is contained in:
2026-09-28 18:17:47 +08:00
parent 86ac84e07c
commit 6687d54bef
3 changed files with 81 additions and 1 deletions
@@ -45,6 +45,55 @@ class ZoneArchiveIntegrationTest extends TestCase {
$GLOBALS['_wp_postmeta'] = [];
}
// ── sweep / duplicates (2.4.2 PR-136, manual F-6) ─────────────────────
/** Posts and postmeta for sweep(): post 10 trashed 200 days ago, post 11 published. */
private function seed_trashed_post(): void {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->posts}`" );
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->postmeta}`" );
$wpdb->query( "CREATE TABLE `{$wpdb->posts}` ( ID BIGINT UNSIGNED NOT NULL, post_type VARCHAR(20) NOT NULL DEFAULT 'post', post_status VARCHAR(20) NOT NULL DEFAULT 'publish', post_modified_gmt DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', PRIMARY KEY (ID) ) ENGINE=InnoDB" );
$wpdb->query( "CREATE TABLE `{$wpdb->postmeta}` ( meta_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, post_id BIGINT UNSIGNED NOT NULL DEFAULT 0, meta_key VARCHAR(255) DEFAULT NULL, meta_value LONGTEXT, PRIMARY KEY (meta_id) ) ENGINE=InnoDB" );
$old = gmdate( 'Y-m-d H:i:s', time() - 200 * DAY_IN_SECONDS );
$wpdb->query( "INSERT INTO `{$wpdb->posts}` VALUES (10, 'hp_listing', 'trash', '{$old}'), (11, 'hp_listing', 'publish', '{$old}')" );
$wpdb->query( "INSERT INTO `{$wpdb->postmeta}` (post_id, meta_key, meta_value) VALUES (10, 'hp_price', '5'), (10, 'hp_rating', '4'), (11, 'hp_price', '9')" );
}
private function drop_posts(): void {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->posts}`" );
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->postmeta}`" );
}
private function archive_rows(): int {
global $wpdb;
return (int) $wpdb->get_var( 'SELECT COUNT(*) FROM `' . self::TABLE . '`' );
}
public function test_sweep_copies_each_trashed_meta_row_once(): void {
$this->seed_trashed_post();
try {
$this->assertSame( 2, WPDO_Zone_Archive::sweep( 90, false ) );
$this->assertSame( 0, WPDO_Zone_Archive::sweep( 90, false ), 'already archived' );
$this->assertSame( 2, $this->archive_rows() );
} finally {
$this->drop_posts();
}
}
public function test_remove_duplicates_keeps_the_first_copy_of_each_meta_row(): void {
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_price', '5', 101 );
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_price', '5', 101 );
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_rating', '4', 102 );
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_rating', '4', 102 );
WPDO_Zone_Archive::archive( 12, 'hp_listing', 'hp_price', '7' ); // original_meta_id 0: no identity, kept.
WPDO_Zone_Archive::archive( 12, 'hp_listing', 'hp_price', '7' );
$this->assertSame( 2, WPDO_Zone_Archive::remove_duplicates() );
$this->assertSame( 4, $this->archive_rows() );
$this->assertSame( 0, WPDO_Zone_Archive::remove_duplicates() );
}
// ── archive / get ─────────────────────────────────────────────────────
public function test_archive_stores_plain_entry(): void {