fix(2meet-data-optimizer): do not recreate a deleted entity's flat row

- Deleting a post / user / term / comment drops its flat row in the adapter delete hook, then WordPress deletes each meta row; sync_from_native upserted NULL for each and brought the row back (512 orphan rows on dev36). With no native value it now only clears the column on an existing row
- Verify: tests/live/run.php resync (25/25, entity deletes in dual_write and shadow_read leave no flat row); mut.php T8 flat_row NO-ROW; integration 516
- PR: PR-057b
This commit is contained in:
2026-09-26 20:34:28 +08:00
parent 4fc59b757e
commit a7134bfb80
3 changed files with 77 additions and 2 deletions
+33 -2
View File
@@ -201,7 +201,8 @@ final class TMDO_Hook_Bus {
* (NULL when native has no row). Runs in dual_write / shadow_read only,
* after WP wrote native, so flat always ends up equal to native — also
* when WP skipped or refused the write, and when an earlier direct SQL
* write had left flat stale.
* write had left flat stale. A NULL never creates a row: deleting an
* entity drops its flat row before WP deletes the meta rows one by one.
*
* @param string $type Entity type.
* @param int $object_id Entity ID.
@@ -222,7 +223,9 @@ final class TMDO_Hook_Bus {
$before = self::maybe_read_before_value( $type, $object_id, $meta_key, $field_def, $op );
$native = self::native_first_value( $type, $object_id, $meta_key );
$result = self::perform_upsert( $type, $object_id, $meta_key, null === $native ? null : maybe_unserialize( $native ), $field_def );
$result = null === $native
? self::clear_column( $type, $object_id, $meta_key, $field_def )
: self::perform_upsert( $type, $object_id, $meta_key, maybe_unserialize( $native ), $field_def );
if ( 'delete' === $op ) {
do_action( 'wpdo_after_delete', $type, $object_id, $meta_key, $meta_value, $result, $delete_all, $before );
@@ -522,6 +525,34 @@ final class TMDO_Hook_Bus {
return 'flat';
}
/**
* Set a flat column to NULL on the entity's existing row; never insert one.
*
* @param string $type Entity type.
* @param int $entity_id Entity ID.
* @param string $meta_key Meta key.
* @param array<string, mixed> $field_def Registered field.
* @return bool|null Null when there is no adapter or table.
*/
private static function clear_column( string $type, int $entity_id, string $meta_key, array $field_def ): ?bool {
global $wpdb;
$adapter = TMDO_Entity_Registry::get_adapter( $type );
$table = TMDO_Schema_Manager::get_table_name( $type, $field_def['group'] );
if ( ! $adapter || ! TMDO_Schema_Manager::table_exists( $table ) ) {
return null;
}
$col = TMDO_Schema_Manager::sanitize_column_name( $meta_key );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$result = $wpdb->update( $table, array( $col => null ), array( $adapter->get_entity_id_column() => $entity_id ) );
TMDO_Cache_Orchestrator::invalidate( $type, $entity_id, $field_def['group'] );
self::touch_posts_last_changed( $type );
return false !== $result;
}
/**
* 執行 Upsert 操作
*/