PostgreSQL Source Code  git master
pgstat_relation.c
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------
2  *
3  * pgstat_relation.c
4  * Implementation of relation statistics.
5  *
6  * This file contains the implementation of function relation. It is kept
7  * separate from pgstat.c to enforce the line between the statistics access /
8  * storage implementation and the details about individual types of
9  * statistics.
10  *
11  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
12  *
13  * IDENTIFICATION
14  * src/backend/utils/activity/pgstat_relation.c
15  * -------------------------------------------------------------------------
16  */
17 
18 #include "postgres.h"
19 
20 #include "access/twophase_rmgr.h"
21 #include "access/xact.h"
22 #include "catalog/partition.h"
23 #include "postmaster/autovacuum.h"
24 #include "utils/memutils.h"
25 #include "utils/pgstat_internal.h"
26 #include "utils/rel.h"
27 #include "utils/timestamp.h"
28 
29 
30 /* Record that's written to 2PC state file when pgstat state is persisted */
31 typedef struct TwoPhasePgStatRecord
32 {
33  PgStat_Counter tuples_inserted; /* tuples inserted in xact */
34  PgStat_Counter tuples_updated; /* tuples updated in xact */
35  PgStat_Counter tuples_deleted; /* tuples deleted in xact */
36  /* tuples i/u/d prior to truncate/drop */
40  Oid t_id; /* table's OID */
41  bool t_shared; /* is it a shared catalog? */
42  bool t_truncdropped; /* was the relation truncated/dropped? */
44 
45 
46 static PgStat_TableStatus *pgstat_prep_relation_pending(Oid rel_id, bool isshared);
47 static void add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level);
48 static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info);
49 static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool is_drop);
51 
52 
53 /*
54  * Copy stats between relations. This is used for things like REINDEX
55  * CONCURRENTLY.
56  */
57 void
59 {
60  PgStat_StatTabEntry *srcstats;
61  PgStatShared_Relation *dstshstats;
62  PgStat_EntryRef *dst_ref;
63 
64  srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
65  RelationGetRelid(src));
66  if (!srcstats)
67  return;
68 
70  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
71  RelationGetRelid(dst),
72  false);
73 
74  dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
75  dstshstats->stats = *srcstats;
76 
77  pgstat_unlock_entry(dst_ref);
78 }
79 
80 /*
81  * Initialize a relcache entry to count access statistics. Called whenever a
82  * relation is opened.
83  *
84  * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
85  * when the relcache entry is made; thereafter it is long-lived data.
86  *
87  * This does not create a reference to a stats entry in shared memory, nor
88  * allocate memory for the pending stats. That happens in
89  * pgstat_assoc_relation().
90  */
91 void
93 {
94  char relkind = rel->rd_rel->relkind;
95 
96  /*
97  * We only count stats for relations with storage and partitioned tables
98  */
99  if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
100  {
101  rel->pgstat_enabled = false;
102  rel->pgstat_info = NULL;
103  return;
104  }
105 
106  if (!pgstat_track_counts)
107  {
108  if (rel->pgstat_info)
110 
111  /* We're not counting at all */
112  rel->pgstat_enabled = false;
113  rel->pgstat_info = NULL;
114  return;
115  }
116 
117  rel->pgstat_enabled = true;
118 }
119 
120 /*
121  * Prepare for statistics for this relation to be collected.
122  *
123  * This ensures we have a reference to the stats entry before stats can be
124  * generated. That is important because a relation drop in another connection
125  * could otherwise lead to the stats entry being dropped, which then later
126  * would get recreated when flushing stats.
127  *
128  * This is separate from pgstat_init_relation() as it is not uncommon for
129  * relcache entries to be opened without ever getting stats reported.
130  */
131 void
133 {
134  Assert(rel->pgstat_enabled);
135  Assert(rel->pgstat_info == NULL);
136 
137  /* Else find or make the PgStat_TableStatus entry, and update link */
139  rel->rd_rel->relisshared);
140 
141  /* don't allow link a stats to multiple relcache entries */
142  Assert(rel->pgstat_info->relation == NULL);
143 
144  /* mark this relation as the owner */
145  rel->pgstat_info->relation = rel;
146 }
147 
148 /*
149  * Break the mutual link between a relcache entry and pending stats entry.
150  * This must be called whenever one end of the link is removed.
151  */
152 void
154 {
155  /* remove the link to stats info if any */
156  if (rel->pgstat_info == NULL)
157  return;
158 
159  /* link sanity check */
160  Assert(rel->pgstat_info->relation == rel);
161  rel->pgstat_info->relation = NULL;
162  rel->pgstat_info = NULL;
163 }
164 
165 /*
166  * Ensure that stats are dropped if transaction aborts.
167  */
168 void
170 {
172  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
173  RelationGetRelid(rel));
174 }
175 
176 /*
177  * Ensure that stats are dropped if transaction commits.
178  */
179 void
181 {
182  int nest_level = GetCurrentTransactionNestLevel();
183  PgStat_TableStatus *pgstat_info;
184 
186  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
187  RelationGetRelid(rel));
188 
190  return;
191 
192  /*
193  * Transactionally set counters to 0. That ensures that accesses to
194  * pg_stat_xact_all_tables inside the transaction show 0.
195  */
196  pgstat_info = rel->pgstat_info;
197  if (pgstat_info->trans &&
198  pgstat_info->trans->nest_level == nest_level)
199  {
200  save_truncdrop_counters(pgstat_info->trans, true);
201  pgstat_info->trans->tuples_inserted = 0;
202  pgstat_info->trans->tuples_updated = 0;
203  pgstat_info->trans->tuples_deleted = 0;
204  }
205 }
206 
207 /*
208  * Report that the table was just vacuumed.
209  */
210 void
211 pgstat_report_vacuum(Oid tableoid, bool shared,
212  PgStat_Counter livetuples, PgStat_Counter deadtuples)
213 {
214  PgStat_EntryRef *entry_ref;
215  PgStatShared_Relation *shtabentry;
216  PgStat_StatTabEntry *tabentry;
217  Oid dboid = (shared ? InvalidOid : MyDatabaseId);
218  TimestampTz ts;
219 
220  if (!pgstat_track_counts)
221  return;
222 
223  /* Store the data in the table's hash table entry. */
224  ts = GetCurrentTimestamp();
225 
226  /* block acquiring lock for the same reason as pgstat_report_autovac() */
228  dboid, tableoid, false);
229 
230  shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
231  tabentry = &shtabentry->stats;
232 
233  tabentry->n_live_tuples = livetuples;
234  tabentry->n_dead_tuples = deadtuples;
235 
236  /*
237  * It is quite possible that a non-aggressive VACUUM ended up skipping
238  * various pages, however, we'll zero the insert counter here regardless.
239  * It's currently used only to track when we need to perform an "insert"
240  * autovacuum, which are mainly intended to freeze newly inserted tuples.
241  * Zeroing this may just mean we'll not try to vacuum the table again
242  * until enough tuples have been inserted to trigger another insert
243  * autovacuum. An anti-wraparound autovacuum will catch any persistent
244  * stragglers.
245  */
246  tabentry->inserts_since_vacuum = 0;
247 
249  {
250  tabentry->autovac_vacuum_timestamp = ts;
251  tabentry->autovac_vacuum_count++;
252  }
253  else
254  {
255  tabentry->vacuum_timestamp = ts;
256  tabentry->vacuum_count++;
257  }
258 
259  pgstat_unlock_entry(entry_ref);
260 }
261 
262 /*
263  * Report that the table was just analyzed.
264  *
265  * Caller must provide new live- and dead-tuples estimates, as well as a
266  * flag indicating whether to reset the changes_since_analyze counter.
267  */
268 void
270  PgStat_Counter livetuples, PgStat_Counter deadtuples,
271  bool resetcounter)
272 {
273  PgStat_EntryRef *entry_ref;
274  PgStatShared_Relation *shtabentry;
275  PgStat_StatTabEntry *tabentry;
276  Oid dboid = (rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId);
277 
278  if (!pgstat_track_counts)
279  return;
280 
281  /*
282  * Unlike VACUUM, ANALYZE might be running inside a transaction that has
283  * already inserted and/or deleted rows in the target table. ANALYZE will
284  * have counted such rows as live or dead respectively. Because we will
285  * report our counts of such rows at transaction end, we should subtract
286  * off these counts from the update we're making now, else they'll be
287  * double-counted after commit. (This approach also ensures that the
288  * shared stats entry ends up with the right numbers if we abort instead
289  * of committing.)
290  *
291  * Waste no time on partitioned tables, though.
292  */
293  if (pgstat_should_count_relation(rel) &&
294  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
295  {
297 
298  for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
299  {
300  livetuples -= trans->tuples_inserted - trans->tuples_deleted;
301  deadtuples -= trans->tuples_updated + trans->tuples_deleted;
302  }
303  /* count stuff inserted by already-aborted subxacts, too */
304  deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
305  /* Since ANALYZE's counts are estimates, we could have underflowed */
306  livetuples = Max(livetuples, 0);
307  deadtuples = Max(deadtuples, 0);
308  }
309 
310  /* block acquiring lock for the same reason as pgstat_report_autovac() */
312  RelationGetRelid(rel),
313  false);
314  /* can't get dropped while accessed */
315  Assert(entry_ref != NULL && entry_ref->shared_stats != NULL);
316 
317  shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
318  tabentry = &shtabentry->stats;
319 
320  tabentry->n_live_tuples = livetuples;
321  tabentry->n_dead_tuples = deadtuples;
322 
323  /*
324  * If commanded, reset changes_since_analyze to zero. This forgets any
325  * changes that were committed while the ANALYZE was in progress, but we
326  * have no good way to estimate how many of those there were.
327  */
328  if (resetcounter)
329  tabentry->changes_since_analyze = 0;
330 
332  {
334  tabentry->autovac_analyze_count++;
335  }
336  else
337  {
339  tabentry->analyze_count++;
340  }
341 
342  pgstat_unlock_entry(entry_ref);
343 }
344 
345 /*
346  * count a tuple insertion of n tuples
347  */
348 void
350 {
352  {
353  PgStat_TableStatus *pgstat_info = rel->pgstat_info;
354 
355  ensure_tabstat_xact_level(pgstat_info);
356  pgstat_info->trans->tuples_inserted += n;
357  }
358 }
359 
360 /*
361  * count a tuple update
362  */
363 void
365 {
367  {
368  PgStat_TableStatus *pgstat_info = rel->pgstat_info;
369 
370  ensure_tabstat_xact_level(pgstat_info);
371  pgstat_info->trans->tuples_updated++;
372 
373  /* t_tuples_hot_updated is nontransactional, so just advance it */
374  if (hot)
375  pgstat_info->t_counts.t_tuples_hot_updated++;
376  }
377 }
378 
379 /*
380  * count a tuple deletion
381  */
382 void
384 {
386  {
387  PgStat_TableStatus *pgstat_info = rel->pgstat_info;
388 
389  ensure_tabstat_xact_level(pgstat_info);
390  pgstat_info->trans->tuples_deleted++;
391  }
392 }
393 
394 /*
395  * update tuple counters due to truncate
396  */
397 void
399 {
401  {
402  PgStat_TableStatus *pgstat_info = rel->pgstat_info;
403 
404  ensure_tabstat_xact_level(pgstat_info);
405  save_truncdrop_counters(pgstat_info->trans, false);
406  pgstat_info->trans->tuples_inserted = 0;
407  pgstat_info->trans->tuples_updated = 0;
408  pgstat_info->trans->tuples_deleted = 0;
409  }
410 }
411 
412 /*
413  * update dead-tuples count
414  *
415  * The semantics of this are that we are reporting the nontransactional
416  * recovery of "delta" dead tuples; so t_delta_dead_tuples decreases
417  * rather than increasing, and the change goes straight into the per-table
418  * counter, not into transactional state.
419  */
420 void
422 {
424  {
425  PgStat_TableStatus *pgstat_info = rel->pgstat_info;
426 
427  pgstat_info->t_counts.t_delta_dead_tuples -= delta;
428  }
429 }
430 
431 /*
432  * Support function for the SQL-callable pgstat* functions. Returns
433  * the collected statistics for one table or NULL. NULL doesn't mean
434  * that the table doesn't exist, just that there are no statistics, so the
435  * caller is better off to report ZERO instead.
436  */
439 {
440  PgStat_StatTabEntry *tabentry;
441 
442  tabentry = pgstat_fetch_stat_tabentry_ext(false, relid);
443  if (tabentry != NULL)
444  return tabentry;
445 
446  /*
447  * If we didn't find it, maybe it's a shared table.
448  */
449  tabentry = pgstat_fetch_stat_tabentry_ext(true, relid);
450  return tabentry;
451 }
452 
453 /*
454  * More efficient version of pgstat_fetch_stat_tabentry(), allowing to specify
455  * whether the to-be-accessed table is a shared relation or not.
456  */
459 {
460  Oid dboid = (shared ? InvalidOid : MyDatabaseId);
461 
462  return (PgStat_StatTabEntry *)
464 }
465 
466 /*
467  * find any existing PgStat_TableStatus entry for rel
468  *
469  * Find any existing PgStat_TableStatus entry for rel_id in the current
470  * database. If not found, try finding from shared tables.
471  *
472  * If no entry found, return NULL, don't create a new one
473  */
476 {
477  PgStat_EntryRef *entry_ref;
478 
480  if (!entry_ref)
482 
483  if (entry_ref)
484  return entry_ref->pending;
485  return NULL;
486 }
487 
488 /*
489  * Perform relation stats specific end-of-transaction work. Helper for
490  * AtEOXact_PgStat.
491  *
492  * Transfer transactional insert/update counts into the base tabstat entries.
493  * We don't bother to free any of the transactional state, since it's all in
494  * TopTransactionContext and will go away anyway.
495  */
496 void
498 {
500 
501  for (trans = xact_state->first; trans != NULL; trans = trans->next)
502  {
503  PgStat_TableStatus *tabstat;
504 
505  Assert(trans->nest_level == 1);
506  Assert(trans->upper == NULL);
507  tabstat = trans->parent;
508  Assert(tabstat->trans == trans);
509  /* restore pre-truncate/drop stats (if any) in case of aborted xact */
510  if (!isCommit)
512  /* count attempted actions regardless of commit/abort */
513  tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
514  tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
515  tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
516  if (isCommit)
517  {
518  tabstat->t_counts.t_truncdropped = trans->truncdropped;
519  if (trans->truncdropped)
520  {
521  /* forget live/dead stats seen by backend thus far */
522  tabstat->t_counts.t_delta_live_tuples = 0;
523  tabstat->t_counts.t_delta_dead_tuples = 0;
524  }
525  /* insert adds a live tuple, delete removes one */
526  tabstat->t_counts.t_delta_live_tuples +=
527  trans->tuples_inserted - trans->tuples_deleted;
528  /* update and delete each create a dead tuple */
529  tabstat->t_counts.t_delta_dead_tuples +=
530  trans->tuples_updated + trans->tuples_deleted;
531  /* insert, update, delete each count as one change event */
532  tabstat->t_counts.t_changed_tuples +=
533  trans->tuples_inserted + trans->tuples_updated +
534  trans->tuples_deleted;
535  }
536  else
537  {
538  /* inserted tuples are dead, deleted tuples are unaffected */
539  tabstat->t_counts.t_delta_dead_tuples +=
540  trans->tuples_inserted + trans->tuples_updated;
541  /* an aborted xact generates no changed_tuple events */
542  }
543  tabstat->trans = NULL;
544  }
545 }
546 
547 /*
548  * Perform relation stats specific end-of-sub-transaction work. Helper for
549  * AtEOSubXact_PgStat.
550  *
551  * Transfer transactional insert/update counts into the next higher
552  * subtransaction state.
553  */
554 void
555 AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
556 {
558  PgStat_TableXactStatus *next_trans;
559 
560  for (trans = xact_state->first; trans != NULL; trans = next_trans)
561  {
562  PgStat_TableStatus *tabstat;
563 
564  next_trans = trans->next;
565  Assert(trans->nest_level == nestDepth);
566  tabstat = trans->parent;
567  Assert(tabstat->trans == trans);
568 
569  if (isCommit)
570  {
571  if (trans->upper && trans->upper->nest_level == nestDepth - 1)
572  {
573  if (trans->truncdropped)
574  {
575  /* propagate the truncate/drop status one level up */
576  save_truncdrop_counters(trans->upper, false);
577  /* replace upper xact stats with ours */
578  trans->upper->tuples_inserted = trans->tuples_inserted;
579  trans->upper->tuples_updated = trans->tuples_updated;
580  trans->upper->tuples_deleted = trans->tuples_deleted;
581  }
582  else
583  {
584  trans->upper->tuples_inserted += trans->tuples_inserted;
585  trans->upper->tuples_updated += trans->tuples_updated;
586  trans->upper->tuples_deleted += trans->tuples_deleted;
587  }
588  tabstat->trans = trans->upper;
589  pfree(trans);
590  }
591  else
592  {
593  /*
594  * When there isn't an immediate parent state, we can just
595  * reuse the record instead of going through a palloc/pfree
596  * pushup (this works since it's all in TopTransactionContext
597  * anyway). We have to re-link it into the parent level,
598  * though, and that might mean pushing a new entry into the
599  * pgStatXactStack.
600  */
601  PgStat_SubXactStatus *upper_xact_state;
602 
603  upper_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
604  trans->next = upper_xact_state->first;
605  upper_xact_state->first = trans;
606  trans->nest_level = nestDepth - 1;
607  }
608  }
609  else
610  {
611  /*
612  * On abort, update top-level tabstat counts, then forget the
613  * subtransaction
614  */
615 
616  /* first restore values obliterated by truncate/drop */
618  /* count attempted actions regardless of commit/abort */
619  tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
620  tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
621  tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
622  /* inserted tuples are dead, deleted tuples are unaffected */
623  tabstat->t_counts.t_delta_dead_tuples +=
624  trans->tuples_inserted + trans->tuples_updated;
625  tabstat->trans = trans->upper;
626  pfree(trans);
627  }
628  }
629 }
630 
631 /*
632  * Generate 2PC records for all the pending transaction-dependent relation
633  * stats.
634  */
635 void
637 {
639 
640  for (trans = xact_state->first; trans != NULL; trans = trans->next)
641  {
643  TwoPhasePgStatRecord record;
644 
645  Assert(trans->nest_level == 1);
646  Assert(trans->upper == NULL);
647  tabstat = trans->parent;
648  Assert(tabstat->trans == trans);
649 
650  record.tuples_inserted = trans->tuples_inserted;
651  record.tuples_updated = trans->tuples_updated;
652  record.tuples_deleted = trans->tuples_deleted;
653  record.inserted_pre_truncdrop = trans->inserted_pre_truncdrop;
654  record.updated_pre_truncdrop = trans->updated_pre_truncdrop;
655  record.deleted_pre_truncdrop = trans->deleted_pre_truncdrop;
656  record.t_id = tabstat->t_id;
657  record.t_shared = tabstat->t_shared;
658  record.t_truncdropped = trans->truncdropped;
659 
661  &record, sizeof(TwoPhasePgStatRecord));
662  }
663 }
664 
665 /*
666  * All we need do here is unlink the transaction stats state from the
667  * nontransactional state. The nontransactional action counts will be
668  * reported to the stats system immediately, while the effects on live and
669  * dead tuple counts are preserved in the 2PC state file.
670  *
671  * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
672  */
673 void
675 {
677 
678  for (trans = xact_state->first; trans != NULL; trans = trans->next)
679  {
680  PgStat_TableStatus *tabstat;
681 
682  tabstat = trans->parent;
683  tabstat->trans = NULL;
684  }
685 }
686 
687 /*
688  * 2PC processing routine for COMMIT PREPARED case.
689  *
690  * Load the saved counts into our local pgstats state.
691  */
692 void
694  void *recdata, uint32 len)
695 {
696  TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
697  PgStat_TableStatus *pgstat_info;
698 
699  /* Find or create a tabstat entry for the rel */
700  pgstat_info = pgstat_prep_relation_pending(rec->t_id, rec->t_shared);
701 
702  /* Same math as in AtEOXact_PgStat, commit case */
703  pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
704  pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
705  pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
706  pgstat_info->t_counts.t_truncdropped = rec->t_truncdropped;
707  if (rec->t_truncdropped)
708  {
709  /* forget live/dead stats seen by backend thus far */
710  pgstat_info->t_counts.t_delta_live_tuples = 0;
711  pgstat_info->t_counts.t_delta_dead_tuples = 0;
712  }
713  pgstat_info->t_counts.t_delta_live_tuples +=
714  rec->tuples_inserted - rec->tuples_deleted;
715  pgstat_info->t_counts.t_delta_dead_tuples +=
716  rec->tuples_updated + rec->tuples_deleted;
717  pgstat_info->t_counts.t_changed_tuples +=
718  rec->tuples_inserted + rec->tuples_updated +
719  rec->tuples_deleted;
720 }
721 
722 /*
723  * 2PC processing routine for ROLLBACK PREPARED case.
724  *
725  * Load the saved counts into our local pgstats state, but treat them
726  * as aborted.
727  */
728 void
730  void *recdata, uint32 len)
731 {
732  TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
733  PgStat_TableStatus *pgstat_info;
734 
735  /* Find or create a tabstat entry for the rel */
736  pgstat_info = pgstat_prep_relation_pending(rec->t_id, rec->t_shared);
737 
738  /* Same math as in AtEOXact_PgStat, abort case */
739  if (rec->t_truncdropped)
740  {
744  }
745  pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
746  pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
747  pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
748  pgstat_info->t_counts.t_delta_dead_tuples +=
749  rec->tuples_inserted + rec->tuples_updated;
750 }
751 
752 /*
753  * Flush out pending stats for the entry
754  *
755  * If nowait is true, this function returns false if lock could not
756  * immediately acquired, otherwise true is returned.
757  *
758  * Some of the stats are copied to the corresponding pending database stats
759  * entry when successfully flushing.
760  */
761 bool
763 {
764  static const PgStat_TableCounts all_zeroes;
765  Oid dboid;
766  PgStat_TableStatus *lstats; /* pending stats entry */
767  PgStatShared_Relation *shtabstats;
768  PgStat_StatTabEntry *tabentry; /* table entry of shared stats */
769  PgStat_StatDBEntry *dbentry; /* pending database entry */
770 
771  dboid = entry_ref->shared_entry->key.dboid;
772  lstats = (PgStat_TableStatus *) entry_ref->pending;
773  shtabstats = (PgStatShared_Relation *) entry_ref->shared_stats;
774 
775  /*
776  * Ignore entries that didn't accumulate any actual counts, such as
777  * indexes that were opened by the planner but not used.
778  */
779  if (memcmp(&lstats->t_counts, &all_zeroes,
780  sizeof(PgStat_TableCounts)) == 0)
781  {
782  return true;
783  }
784 
785  if (!pgstat_lock_entry(entry_ref, nowait))
786  return false;
787 
788  /* add the values to the shared entry. */
789  tabentry = &shtabstats->stats;
790 
791  tabentry->numscans += lstats->t_counts.t_numscans;
792  tabentry->tuples_returned += lstats->t_counts.t_tuples_returned;
793  tabentry->tuples_fetched += lstats->t_counts.t_tuples_fetched;
794  tabentry->tuples_inserted += lstats->t_counts.t_tuples_inserted;
795  tabentry->tuples_updated += lstats->t_counts.t_tuples_updated;
796  tabentry->tuples_deleted += lstats->t_counts.t_tuples_deleted;
797  tabentry->tuples_hot_updated += lstats->t_counts.t_tuples_hot_updated;
798 
799  /*
800  * If table was truncated/dropped, first reset the live/dead counters.
801  */
802  if (lstats->t_counts.t_truncdropped)
803  {
804  tabentry->n_live_tuples = 0;
805  tabentry->n_dead_tuples = 0;
806  tabentry->inserts_since_vacuum = 0;
807  }
808 
809  tabentry->n_live_tuples += lstats->t_counts.t_delta_live_tuples;
810  tabentry->n_dead_tuples += lstats->t_counts.t_delta_dead_tuples;
811  tabentry->changes_since_analyze += lstats->t_counts.t_changed_tuples;
812  tabentry->inserts_since_vacuum += lstats->t_counts.t_tuples_inserted;
813  tabentry->blocks_fetched += lstats->t_counts.t_blocks_fetched;
814  tabentry->blocks_hit += lstats->t_counts.t_blocks_hit;
815 
816  /* Clamp n_live_tuples in case of negative delta_live_tuples */
817  tabentry->n_live_tuples = Max(tabentry->n_live_tuples, 0);
818  /* Likewise for n_dead_tuples */
819  tabentry->n_dead_tuples = Max(tabentry->n_dead_tuples, 0);
820 
821  pgstat_unlock_entry(entry_ref);
822 
823  /* The entry was successfully flushed, add the same to database stats */
824  dbentry = pgstat_prep_database_pending(dboid);
825  dbentry->n_tuples_returned += lstats->t_counts.t_tuples_returned;
826  dbentry->n_tuples_fetched += lstats->t_counts.t_tuples_fetched;
827  dbentry->n_tuples_inserted += lstats->t_counts.t_tuples_inserted;
828  dbentry->n_tuples_updated += lstats->t_counts.t_tuples_updated;
829  dbentry->n_tuples_deleted += lstats->t_counts.t_tuples_deleted;
830  dbentry->n_blocks_fetched += lstats->t_counts.t_blocks_fetched;
831  dbentry->n_blocks_hit += lstats->t_counts.t_blocks_hit;
832 
833  return true;
834 }
835 
836 void
838 {
839  PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending;
840 
841  if (pending->relation)
843 }
844 
845 /*
846  * Find or create a PgStat_TableStatus entry for rel. New entry is created and
847  * initialized if not exists.
848  */
849 static PgStat_TableStatus *
850 pgstat_prep_relation_pending(Oid rel_id, bool isshared)
851 {
852  PgStat_EntryRef *entry_ref;
853  PgStat_TableStatus *pending;
854 
856  isshared ? InvalidOid : MyDatabaseId,
857  rel_id, NULL);
858  pending = entry_ref->pending;
859  pending->t_id = rel_id;
860  pending->t_shared = isshared;
861 
862  return pending;
863 }
864 
865 /*
866  * add a new (sub)transaction state record
867  */
868 static void
869 add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level)
870 {
871  PgStat_SubXactStatus *xact_state;
873 
874  /*
875  * If this is the first rel to be modified at the current nest level, we
876  * first have to push a transaction stack entry.
877  */
878  xact_state = pgstat_get_xact_stack_level(nest_level);
879 
880  /* Now make a per-table stack entry */
883  sizeof(PgStat_TableXactStatus));
884  trans->nest_level = nest_level;
885  trans->upper = pgstat_info->trans;
886  trans->parent = pgstat_info;
887  trans->next = xact_state->first;
888  xact_state->first = trans;
889  pgstat_info->trans = trans;
890 }
891 
892 /*
893  * Add a new (sub)transaction record if needed.
894  */
895 static void
897 {
898  int nest_level = GetCurrentTransactionNestLevel();
899 
900  if (pgstat_info->trans == NULL ||
901  pgstat_info->trans->nest_level != nest_level)
902  add_tabstat_xact_level(pgstat_info, nest_level);
903 }
904 
905 /*
906  * Whenever a table is truncated/dropped, we save its i/u/d counters so that
907  * they can be cleared, and if the (sub)xact that executed the truncate/drop
908  * later aborts, the counters can be restored to the saved (pre-truncate/drop)
909  * values.
910  *
911  * Note that for truncate we do this on the first truncate in any particular
912  * subxact level only.
913  */
914 static void
916 {
917  if (!trans->truncdropped || is_drop)
918  {
919  trans->inserted_pre_truncdrop = trans->tuples_inserted;
920  trans->updated_pre_truncdrop = trans->tuples_updated;
921  trans->deleted_pre_truncdrop = trans->tuples_deleted;
922  trans->truncdropped = true;
923  }
924 }
925 
926 /*
927  * restore counters when a truncate aborts
928  */
929 static void
931 {
932  if (trans->truncdropped)
933  {
934  trans->tuples_inserted = trans->inserted_pre_truncdrop;
935  trans->tuples_updated = trans->updated_pre_truncdrop;
936  trans->tuples_deleted = trans->deleted_pre_truncdrop;
937  }
938 }
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3309
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1574
unsigned short uint16
Definition: c.h:440
unsigned int uint32
Definition: c.h:441
#define Max(x, y)
Definition: c.h:980
uint32 TransactionId
Definition: c.h:587
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:155
int64 TimestampTz
Definition: timestamp.h:39
Oid MyDatabaseId
Definition: globals.c:89
Assert(fmt[strlen(fmt) - 1] !='\n')
MemoryContext TopTransactionContext
Definition: mcxt.c:53
void pfree(void *pointer)
Definition: mcxt.c:1175
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:906
const void size_t len
bool pgstat_track_counts
Definition: pgstat.c:186
void * pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
Definition: pgstat.c:779
PgStat_EntryRef * pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid, bool *created_entry)
Definition: pgstat.c:1053
PgStat_EntryRef * pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
Definition: pgstat.c:1091
@ PGSTAT_KIND_RELATION
Definition: pgstat.h:42
#define pgstat_should_count_relation(rel)
Definition: pgstat.h:519
int64 PgStat_Counter
Definition: pgstat.h:88
PgStat_StatDBEntry * pgstat_prep_database_pending(Oid dboid)
struct TwoPhasePgStatRecord TwoPhasePgStatRecord
static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info)
void pgstat_copy_relation_stats(Relation dst, Relation src)
void pgstat_unlink_relation(Relation rel)
void pgstat_count_heap_update(Relation rel, bool hot)
void pgstat_twophase_postcommit(TransactionId xid, uint16 info, void *recdata, uint32 len)
static PgStat_TableStatus * pgstat_prep_relation_pending(Oid rel_id, bool isshared)
PgStat_TableStatus * find_tabstat_entry(Oid rel_id)
void pgstat_assoc_relation(Relation rel)
void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter)
static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool is_drop)
void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
void pgstat_twophase_postabort(TransactionId xid, uint16 info, void *recdata, uint32 len)
static void add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level)
bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
PgStat_StatTabEntry * pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid)
void pgstat_create_relation(Relation rel)
PgStat_StatTabEntry * pgstat_fetch_stat_tabentry(Oid relid)
void pgstat_update_heap_dead_tuples(Relation rel, int delta)
void pgstat_count_heap_delete(Relation rel)
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
void pgstat_report_vacuum(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples)
void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
void pgstat_count_truncate(Relation rel)
static void restore_truncdrop_counters(PgStat_TableXactStatus *trans)
void pgstat_drop_relation(Relation rel)
void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
void pgstat_init_relation(Relation rel)
void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
void pgstat_unlock_entry(PgStat_EntryRef *entry_ref)
Definition: pgstat_shmem.c:583
bool pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
Definition: pgstat_shmem.c:571
PgStat_EntryRef * pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, Oid objoid, bool nowait)
Definition: pgstat_shmem.c:592
void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
Definition: pgstat_xact.c:366
void pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
Definition: pgstat_xact.c:388
PgStat_SubXactStatus * pgstat_get_xact_stack_level(int nest_level)
Definition: pgstat_xact.c:243
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:488
PgStat_StatTabEntry stats
PgStatShared_Common * shared_stats
PgStatShared_HashEntry * shared_entry
PgStat_Counter n_tuples_deleted
Definition: pgstat.h:289
PgStat_Counter n_tuples_updated
Definition: pgstat.h:288
PgStat_Counter n_blocks_fetched
Definition: pgstat.h:283
PgStat_Counter n_tuples_fetched
Definition: pgstat.h:286
PgStat_Counter n_tuples_inserted
Definition: pgstat.h:287
PgStat_Counter n_tuples_returned
Definition: pgstat.h:285
PgStat_Counter n_blocks_hit
Definition: pgstat.h:284
PgStat_Counter vacuum_count
Definition: pgstat.h:376
PgStat_Counter tuples_fetched
Definition: pgstat.h:360
TimestampTz vacuum_timestamp
Definition: pgstat.h:375
PgStat_Counter blocks_hit
Definition: pgstat.h:373
PgStat_Counter analyze_count
Definition: pgstat.h:380
TimestampTz autovac_analyze_timestamp
Definition: pgstat.h:381
PgStat_Counter tuples_deleted
Definition: pgstat.h:364
PgStat_Counter autovac_analyze_count
Definition: pgstat.h:382
PgStat_Counter tuples_hot_updated
Definition: pgstat.h:365
PgStat_Counter tuples_updated
Definition: pgstat.h:363
PgStat_Counter changes_since_analyze
Definition: pgstat.h:369
PgStat_Counter inserts_since_vacuum
Definition: pgstat.h:370
PgStat_Counter autovac_vacuum_count
Definition: pgstat.h:378
PgStat_Counter numscans
Definition: pgstat.h:357
PgStat_Counter blocks_fetched
Definition: pgstat.h:372
PgStat_Counter tuples_returned
Definition: pgstat.h:359
TimestampTz analyze_timestamp
Definition: pgstat.h:379
PgStat_Counter n_live_tuples
Definition: pgstat.h:367
TimestampTz autovac_vacuum_timestamp
Definition: pgstat.h:377
PgStat_Counter n_dead_tuples
Definition: pgstat.h:368
PgStat_Counter tuples_inserted
Definition: pgstat.h:362
PgStat_TableXactStatus * first
PgStat_Counter t_delta_dead_tuples
Definition: pgstat.h:182
PgStat_Counter t_delta_live_tuples
Definition: pgstat.h:181
PgStat_Counter t_tuples_hot_updated
Definition: pgstat.h:178
PgStat_Counter t_tuples_inserted
Definition: pgstat.h:175
PgStat_Counter t_tuples_updated
Definition: pgstat.h:176
PgStat_Counter t_tuples_fetched
Definition: pgstat.h:173
PgStat_Counter t_tuples_deleted
Definition: pgstat.h:177
PgStat_Counter t_numscans
Definition: pgstat.h:170
PgStat_Counter t_blocks_hit
Definition: pgstat.h:186
bool t_truncdropped
Definition: pgstat.h:179
PgStat_Counter t_changed_tuples
Definition: pgstat.h:183
PgStat_Counter t_blocks_fetched
Definition: pgstat.h:185
PgStat_Counter t_tuples_returned
Definition: pgstat.h:172
PgStat_TableCounts t_counts
Definition: pgstat.h:209
struct PgStat_TableXactStatus * trans
Definition: pgstat.h:208
Relation relation
Definition: pgstat.h:210
PgStat_Counter tuples_inserted
Definition: pgstat.h:219
PgStat_Counter tuples_updated
Definition: pgstat.h:220
PgStat_Counter tuples_deleted
Definition: pgstat.h:221
bool pgstat_enabled
Definition: rel.h:248
Form_pg_class rd_rel
Definition: rel.h:109
struct PgStat_TableStatus * pgstat_info
Definition: rel.h:250
PgStat_Counter deleted_pre_truncdrop
PgStat_Counter inserted_pre_truncdrop
PgStat_Counter updated_pre_truncdrop
PgStat_Counter tuples_deleted
PgStat_Counter tuples_inserted
PgStat_Counter tuples_updated
void RegisterTwoPhaseRecord(TwoPhaseRmgrId rmid, uint16 info, const void *data, uint32 len)
Definition: twophase.c:1259
#define TWOPHASE_RM_PGSTAT_ID
Definition: twophase_rmgr.h:26
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:910
static zic_t trans[TZ_MAX_LEAPS]
Definition: zic.c:400