--- /dev/null
+/*--------------------------------------------------------------------------
+ *
+ * injection_stats_fixed.c
+ * Code for fixed-numbered statistics of injection points.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/test/modules/injection_points/injection_stats_fixed.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+
+#include "common/hashfn.h"
+#include "funcapi.h"
+#include "injection_stats.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/pgstat_internal.h"
+
+/* Structures for statistics of injection points, fixed-size */
+typedef struct PgStat_StatInjFixedEntry
+{
+ PgStat_Counter numattach; /* number of points attached */
+ PgStat_Counter numdetach; /* number of points detached */
+ PgStat_Counter numrun; /* number of points run */
+ TimestampTz stat_reset_timestamp;
+} PgStat_StatInjFixedEntry;
+
+typedef struct PgStatShared_InjectionPointFixed
+{
+ LWLock lock; /* protects all the counters */
+ uint32 changecount;
+ PgStat_StatInjFixedEntry stats;
+ PgStat_StatInjFixedEntry reset_offset;
+} PgStatShared_InjectionPointFixed;
+
+/* Callbacks for fixed-numbered stats */
+static void injection_stats_fixed_init_shmem_cb(void *stats);
+static void injection_stats_fixed_reset_all_cb(TimestampTz ts);
+static void injection_stats_fixed_snapshot_cb(void);
+
+static const PgStat_KindInfo injection_stats_fixed = {
+ .name = "injection_points_fixed",
+ .fixed_amount = true,
+
+ .shared_size = sizeof(PgStat_StatInjFixedEntry),
+ .shared_data_off = offsetof(PgStatShared_InjectionPointFixed, stats),
+ .shared_data_len = sizeof(((PgStatShared_InjectionPointFixed *) 0)->stats),
+
+ .init_shmem_cb = injection_stats_fixed_init_shmem_cb,
+ .reset_all_cb = injection_stats_fixed_reset_all_cb,
+ .snapshot_cb = injection_stats_fixed_snapshot_cb,
+};
+
+/*
+ * Kind ID reserved for statistics of injection points.
+ */
+#define PGSTAT_KIND_INJECTION_FIXED 130
+
+/* Track if fixed-numbered stats are loaded */
+static bool inj_fixed_loaded = false;
+
+static void
+injection_stats_fixed_init_shmem_cb(void *stats)
+{
+ PgStatShared_InjectionPointFixed *stats_shmem =
+ (PgStatShared_InjectionPointFixed *) stats;
+
+ LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
+}
+
+static void
+injection_stats_fixed_reset_all_cb(TimestampTz ts)
+{
+ PgStatShared_InjectionPointFixed *stats_shmem =
+ pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
+
+ LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
+ pgstat_copy_changecounted_stats(&stats_shmem->reset_offset,
+ &stats_shmem->stats,
+ sizeof(stats_shmem->stats),
+ &stats_shmem->changecount);
+ stats_shmem->stats.stat_reset_timestamp = ts;
+ LWLockRelease(&stats_shmem->lock);
+}
+
+static void
+injection_stats_fixed_snapshot_cb(void)
+{
+ PgStatShared_InjectionPointFixed *stats_shmem =
+ pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
+ PgStat_StatInjFixedEntry *stat_snap =
+ pgstat_get_custom_snapshot_data(PGSTAT_KIND_INJECTION_FIXED);
+ PgStat_StatInjFixedEntry *reset_offset = &stats_shmem->reset_offset;
+ PgStat_StatInjFixedEntry reset;
+
+ pgstat_copy_changecounted_stats(stat_snap,
+ &stats_shmem->stats,
+ sizeof(stats_shmem->stats),
+ &stats_shmem->changecount);
+
+ LWLockAcquire(&stats_shmem->lock, LW_SHARED);
+ memcpy(&reset, reset_offset, sizeof(stats_shmem->stats));
+ LWLockRelease(&stats_shmem->lock);
+
+ /* compensate by reset offsets */
+#define FIXED_COMP(fld) stat_snap->fld -= reset.fld;
+ FIXED_COMP(numattach);
+ FIXED_COMP(numdetach);
+ FIXED_COMP(numrun);
+#undef FIXED_COMP
+}
+
+/*
+ * Workhorse to do the registration work, called in _PG_init().
+ */
+void
+pgstat_register_inj_fixed(void)
+{
+ pgstat_register_kind(PGSTAT_KIND_INJECTION_FIXED, &injection_stats_fixed);
+
+ /* mark stats as loaded */
+ inj_fixed_loaded = true;
+}
+
+/*
+ * Report fixed number of statistics for an injection point.
+ */
+void
+pgstat_report_inj_fixed(uint32 numattach,
+ uint32 numdetach,
+ uint32 numrun)
+{
+ PgStatShared_InjectionPointFixed *stats_shmem;
+
+ /* leave if disabled */
+ if (!inj_fixed_loaded)
+ return;
+
+ stats_shmem = pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
+
+ pgstat_begin_changecount_write(&stats_shmem->changecount);
+ stats_shmem->stats.numattach += numattach;
+ stats_shmem->stats.numdetach += numdetach;
+ stats_shmem->stats.numrun += numrun;
+ pgstat_end_changecount_write(&stats_shmem->changecount);
+}
+
+/*
+ * SQL function returning fixed-numbered statistics for injection points.
+ */
+PG_FUNCTION_INFO_V1(injection_points_stats_fixed);
+Datum
+injection_points_stats_fixed(PG_FUNCTION_ARGS)
+{
+ TupleDesc tupdesc;
+ Datum values[3] = {0};
+ bool nulls[3] = {0};
+ PgStat_StatInjFixedEntry *stats;
+
+ if (!inj_fixed_loaded)
+ PG_RETURN_NULL();
+
+ pgstat_snapshot_fixed(PGSTAT_KIND_INJECTION_FIXED);
+ stats = pgstat_get_custom_snapshot_data(PGSTAT_KIND_INJECTION_FIXED);
+
+ /* Initialise attributes information in the tuple descriptor */
+ tupdesc = CreateTemplateTupleDesc(3);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "numattach",
+ INT8OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "numdetach",
+ INT8OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "numrun",
+ INT8OID, -1, 0);
+ BlessTupleDesc(tupdesc);
+
+ values[0] = Int64GetDatum(stats->numattach);
+ values[1] = Int64GetDatum(stats->numdetach);
+ values[2] = Int64GetDatum(stats->numrun);
+ nulls[0] = false;
+ nulls[1] = false;
+ nulls[2] = false;
+
+ /* Returns the record as Datum */
+ PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+}
my $numcalls = $node->safe_psql('postgres',
"SELECT injection_points_stats_numcalls('stats-notice');");
is($numcalls, '2', 'number of stats calls');
+my $fixedstats = $node->safe_psql('postgres',
+ "SELECT * FROM injection_points_stats_fixed();");
+is($fixedstats, '1|0|2', 'number of fixed stats');
# Restart the node cleanly, stats should still be around.
$node->restart;
$numcalls = $node->safe_psql('postgres',
"SELECT injection_points_stats_numcalls('stats-notice');");
is($numcalls, '2', 'number of stats after clean restart');
+$fixedstats = $node->safe_psql('postgres',
+ "SELECT * FROM injection_points_stats_fixed();");
+is($fixedstats, '1|0|2', 'number of fixed stats after clean restart');
# On crash the stats are gone.
$node->stop('immediate');
$node->start;
$numcalls = $node->safe_psql('postgres',
"SELECT injection_points_stats_numcalls('stats-notice');");
-is($numcalls, '', 'number of stats after clean restart');
+is($numcalls, '', 'number of stats after crash');
+$fixedstats = $node->safe_psql('postgres',
+ "SELECT * FROM injection_points_stats_fixed();");
+is($fixedstats, '0|0|0', 'number of fixed stats after crash');
done_testing();