From 2e28c13a5c34db4c6cef640c068d7bab892d235d Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Wed, 25 Mar 2026 14:50:31 +0000 Subject: [PATCH] fix: drop orphaned per-site agent tables after network migration The network migration (datamachine_migrate_agents_to_network_scope) consolidated agent data into base_prefix tables but left the per-site copies behind. These orphans are never queried (all repos use base_prefix) and their presence causes confusion about which table is canonical. Adds datamachine_drop_orphaned_agent_tables() which runs after the migration during activation. Guarded by its own site option flag. --- data-machine.php | 3 ++ inc/migrations.php | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/data-machine.php b/data-machine.php index 354b4110..37c27fd5 100644 --- a/data-machine.php +++ b/data-machine.php @@ -553,6 +553,9 @@ function datamachine_activate_for_site() { // Migrate per-site agents to network-scoped tables (idempotent). datamachine_migrate_agents_to_network_scope(); + // Drop orphaned per-site agent tables left behind by the migration (idempotent). + datamachine_drop_orphaned_agent_tables(); + // Regenerate SITE.md with enriched content and clean up legacy SiteContext transient. datamachine_regenerate_site_md(); delete_transient( 'datamachine_site_context_data' ); diff --git a/inc/migrations.php b/inc/migrations.php index 3e8cabe4..3467b6a4 100644 --- a/inc/migrations.php +++ b/inc/migrations.php @@ -2224,3 +2224,77 @@ function datamachine_migrate_agents_to_network_scope() { ); } } + +/** + * Drop orphaned per-site agent tables after network migration. + * + * After datamachine_migrate_agents_to_network_scope() has consolidated + * all agent data into the network-scoped tables (base_prefix), the + * per-site copies (e.g. c8c_7_datamachine_agents) serve no purpose. + * They can't be queried (all repositories use base_prefix) and their + * presence is confusing. + * + * This function drops the orphaned per-site agent, access, and token + * tables for every subsite. Idempotent — safe to call multiple times. + * Only runs on multisite after the network migration flag is set. + * + * @since 0.43.0 + */ +function datamachine_drop_orphaned_agent_tables() { + if ( ! is_multisite() ) { + return; + } + + if ( ! get_site_option( 'datamachine_agents_network_migrated' ) ) { + return; + } + + if ( get_site_option( 'datamachine_orphaned_agent_tables_dropped' ) ) { + return; + } + + global $wpdb; + + $table_suffixes = array( + 'datamachine_agents', + 'datamachine_agent_access', + 'datamachine_agent_tokens', + ); + + $sites = get_sites( array( 'fields' => 'ids' ) ); + $dropped = 0; + + foreach ( $sites as $blog_id ) { + $site_prefix = $wpdb->get_blog_prefix( $blog_id ); + + // Skip the main site — its prefix IS the base_prefix, + // so these are the canonical network tables. + if ( $site_prefix === $wpdb->base_prefix ) { + continue; + } + + foreach ( $table_suffixes as $suffix ) { + $table_name = $site_prefix . $suffix; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ); + + if ( $exists ) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wpdb->query( "DROP TABLE `{$table_name}`" ); + ++$dropped; + } + } + } + + update_site_option( 'datamachine_orphaned_agent_tables_dropped', true ); + + if ( $dropped > 0 ) { + do_action( + 'datamachine_log', + 'info', + 'Dropped orphaned per-site agent tables after network migration', + array( 'tables_dropped' => $dropped ) + ); + } +}