This is an automated email from the ASF dual-hosted git repository.

chenjinbao1989 pushed a commit to branch cbdb-postgres-merge
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit ec26821b683b662ec7237d39a882709cda563055
Author: liushengsong <[email protected]>
AuthorDate: Wed Mar 11 11:19:44 2026 +0800

    fix: ensure dropped database pgstat entry is cleaned up and use 
gp_stat_force_next_flush in test
    
    - Add missing pgstat_drop_database() call in dropdb() that was lost
      during PG16 merge, ensuring database stats are cleaned up on DROP DATABASE
    - Replace pg_sleep(0.77) with gp_stat_force_next_flush() in
      autovacuum-analyze isolation2 test for reliable and faster stats flush
    - Use HTAB instead of List for zeroed_rels in pgstat_combine_from_qe()
      to avoid O(N²) lookup when many tables are modified across segments
    - Move pgstat_send_qd_tabstats() before PortalDrop() in exec_mpp_query()
      to avoid potential issues if PortalDrop triggers subtransaction cleanup
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 src/backend/commands/dbcommands.c                  |  5 ++++
 src/backend/tcop/postgres.c                        | 13 +++++-----
 src/backend/utils/activity/pgstat_relation.c       | 17 ++++++++++---
 .../isolation2/input/autovacuum-analyze.source     | 14 +++++------
 .../isolation2/output/autovacuum-analyze.source    | 29 ++++++++++++++++------
 5 files changed, 54 insertions(+), 24 deletions(-)

diff --git a/src/backend/commands/dbcommands.c 
b/src/backend/commands/dbcommands.c
index 6b5a0ae371a..1c4a6afa8b3 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -1873,6 +1873,11 @@ dropdb(const char *dbname, bool missing_ok, bool force)
         */
        dropDatabaseDependencies(db_id);
 
+       /*
+        * Tell the cumulative stats system to forget it immediately, too.
+        */
+       pgstat_drop_database(db_id);
+
        /*
         * Drop db-specific replication slots.
         */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 92f744e673f..4014ad8bac2 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1496,16 +1496,17 @@ exec_mpp_query(const char *query_string,
 
                (*receiver->rDestroy) (receiver);
 
-               PortalDrop(portal, false);
-
                /*
-                * GPDB: Send pending relation stats to QD before closing the
-                * transaction.  The stats are in pgStatXactStack 
(transaction-level
-                * counts); finish_xact_command() will call AtEOXact_PgStat() 
which
-                * NULLs pgStatXactStack, so we must capture the stats first.
+                * GPDB: Send pending relation stats to QD before PortalDrop and
+                * finish_xact_command().  The stats are in pgStatXactStack
+                * (transaction-level counts); finish_xact_command() calls
+                * AtEOXact_PgStat() which NULLs pgStatXactStack.  We also send
+                * before PortalDrop to avoid any subtransaction cleanup side 
effects.
                 */
                pgstat_send_qd_tabstats();
 
+               PortalDrop(portal, false);
+
                /*
                 * Close down transaction statement before reporting 
command-complete.
                 * This is so that any end-of-transaction errors are reported 
before
diff --git a/src/backend/utils/activity/pgstat_relation.c 
b/src/backend/utils/activity/pgstat_relation.c
index 33532b0030e..45bbf9abcb7 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -22,6 +22,7 @@
 #include "catalog/partition.h"
 #include "libpq-int.h"
 #include "postmaster/autovacuum.h"
+#include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/pgstat_internal.h"
 #include "utils/rel.h"
@@ -985,11 +986,19 @@ pgstat_combine_from_qe(CdbDispatchResults *primaryResults)
        int             i,
                        j;
        int             nest_level = GetCurrentTransactionNestLevel();
-       List   *zeroed_rels = NIL;
+       HASHCTL hctl;
+       HTAB   *zeroed_rels;
 
        if (primaryResults == NULL)
                return;
 
+       /* Use a hash table for O(1) lookup instead of O(N) list scan */
+       hctl.keysize = sizeof(Oid);
+       hctl.entrysize = sizeof(Oid);
+       hctl.hcxt = CurrentMemoryContext;
+       zeroed_rels = hash_create("pgstat_combine zeroed_rels", 64, &hctl,
+                                                         HASH_ELEM | 
HASH_BLOBS | HASH_CONTEXT);
+
        for (i = 0; i < primaryResults->resultCount; i++)
        {
                CdbDispatchResult  *dispResult = 
&primaryResults->resultArray[i];
@@ -1040,13 +1049,13 @@ pgstat_combine_from_qe(CdbDispatchResults 
*primaryResults)
                                trans = tabstat->trans;
 
                                /* Zero on first encounter to undo previous 
merge */
-                               if (!list_member_oid(zeroed_rels, rec->t_id))
+                               if (!hash_search(zeroed_rels, &rec->t_id, 
HASH_FIND, NULL))
                                {
                                        trans->tuples_inserted = 0;
                                        trans->tuples_updated = 0;
                                        trans->tuples_deleted = 0;
                                        trans->truncdropped = false;
-                                       zeroed_rels = lappend_oid(zeroed_rels, 
rec->t_id);
+                                       hash_search(zeroed_rels, &rec->t_id, 
HASH_ENTER, NULL);
                                }
 
                                /* Accumulate QE counts from this segment */
@@ -1059,5 +1068,5 @@ pgstat_combine_from_qe(CdbDispatchResults *primaryResults)
                }
        }
 
-       list_free(zeroed_rels);
+       hash_destroy(zeroed_rels);
 }
diff --git a/src/test/isolation2/input/autovacuum-analyze.source 
b/src/test/isolation2/input/autovacuum-analyze.source
index 19187b107c2..2445c4178b8 100644
--- a/src/test/isolation2/input/autovacuum-analyze.source
+++ b/src/test/isolation2/input/autovacuum-analyze.source
@@ -171,13 +171,13 @@ SELECT 
gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+2: select gp_stat_force_next_flush();
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend 
before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
--- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 
1000 since ANALYZE executed
--- in same transaction for the insert statement.
+-- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 
1000 since ANALYZE
+-- executed in same transaction for the insert statement.
 select analyze_count, autoanalyze_count, n_mod_since_analyze from 
pg_stat_all_tables where relname = 'autostatstbl';
 
 -- Wait until autovacuum is triggered
@@ -205,7 +205,7 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 
'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', 
'', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+2: select gp_stat_force_next_flush();
 
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend 
before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
@@ -238,12 +238,12 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 
'skip', '', '', 'autosta
 SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', 
'', 'autostatstbl', 1, -1, 0, 1);
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
+2: select gp_stat_force_next_flush();
 
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we 
suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
--- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 
1000 since ANALYZE executed
--- in same transaction for the insert statement.
+-- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 
1000 since ANALYZE
+-- executed in same transaction for the insert statement.
 select analyze_count, autoanalyze_count, n_mod_since_analyze from 
pg_stat_all_tables where relname = 'autostatstbl';
 
 -- Wait until autovacuum is triggered
diff --git a/src/test/isolation2/output/autovacuum-analyze.source 
b/src/test/isolation2/output/autovacuum-analyze.source
index b8727a10a51..457ec1b38fb 100644
--- a/src/test/isolation2/output/autovacuum-analyze.source
+++ b/src/test/isolation2/output/autovacuum-analyze.source
@@ -408,7 +408,12 @@ SELECT 
gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 -- with auto_stats, the auto-ANALYZE still trigger
 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+2: select gp_stat_force_next_flush();
+ gp_stat_force_next_flush 
+--------------------------
+                          
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend 
before finish ANALYZE.
 SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass;
  count 
@@ -420,8 +425,8 @@ select relpages, reltuples from pg_class where oid = 
'autostatstbl'::regclass;
 ----------+-----------
  3        | 1000      
 (1 row)
--- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 
1000 since ANALYZE executed
--- in same transaction for the insert statement.
+-- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 
1000 since ANALYZE
+-- executed in same transaction for the insert statement.
 select analyze_count, autoanalyze_count, n_mod_since_analyze from 
pg_stat_all_tables where relname = 'autostatstbl';
  analyze_count | autoanalyze_count | n_mod_since_analyze 
 ---------------+-------------------+---------------------
@@ -493,7 +498,12 @@ SELECT 
gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+2: select gp_stat_force_next_flush();
+ gp_stat_force_next_flush 
+--------------------------
+                          
+(1 row)
+
 -- auto_stats executed but auto-ANALYZE not execute yet since we suspend 
before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
@@ -572,15 +582,20 @@ SELECT 
gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '
 
 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i;
 INSERT 1000
-2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. 
+2: select gp_stat_force_next_flush();
+ gp_stat_force_next_flush 
+--------------------------
+                          
+(1 row)
+
 -- auto_stats should not executed and auto-ANALYZE not execute yet since we 
suspend before finish ANALYZE.
 select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass;
  relpages | reltuples 
 ----------+-----------
  3        | 2000      
 (1 row)
--- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 
1000 since ANALYZE executed
--- in same transaction for the insert statement.
+-- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 
1000 since ANALYZE
+-- executed in same transaction for the insert statement.
 select analyze_count, autoanalyze_count, n_mod_since_analyze from 
pg_stat_all_tables where relname = 'autostatstbl';
  analyze_count | autoanalyze_count | n_mod_since_analyze 
 ---------------+-------------------+---------------------


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to