333 lignes
7,8 Kio
C
333 lignes
7,8 Kio
C
/*--------------------------------------------------------------------------
|
|
*
|
|
* pacs.c
|
|
* Code for easier management of "measures".
|
|
*
|
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "fmgr.h"
|
|
|
|
#include "common/hashfn.h"
|
|
#include "pgstat.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/pgstat_internal.h"
|
|
|
|
#include "pacs.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
/* structure for measure */
|
|
typedef struct MeasureStatEntry
|
|
{
|
|
PgStat_Counter measure;
|
|
} MeasureStatEntry;
|
|
|
|
/* boilerplace structure for all measures */
|
|
typedef struct MeasuresStatShared
|
|
{
|
|
PgStatShared_Common header;
|
|
MeasureStatEntry stats;
|
|
} MeasuresStatShared;
|
|
|
|
/* boilerplate declaration */
|
|
static bool measure_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
|
|
|
|
/* declare our measures stats */
|
|
static const PgStat_KindInfo measures = {
|
|
.name = "measures",
|
|
.fixed_amount = false,
|
|
|
|
/* Measures are system-wide */
|
|
.accessed_across_databases = true,
|
|
|
|
.shared_size = sizeof(MeasuresStatShared),
|
|
.shared_data_off = offsetof(MeasuresStatShared, stats),
|
|
.shared_data_len = sizeof(((MeasuresStatShared *) 0)->stats),
|
|
.pending_size = sizeof(MeasureStatEntry),
|
|
.flush_pending_cb = measure_flush_cb,
|
|
};
|
|
|
|
/*
|
|
* Compute stats entry idx from measure name with an 8-byte hash.
|
|
*/
|
|
#define PGSTAT_MEASURE_IDX(name) hash_bytes_extended((const unsigned char *) name, strlen(name), 0)
|
|
|
|
/*
|
|
* Kind ID reserved for statistics of measures.
|
|
*/
|
|
#define PGSTAT_KIND_MEASURE (PGSTAT_KIND_CUSTOM_MIN + 1)
|
|
|
|
/* Track if stats are loaded */
|
|
static bool measures_loaded = false;
|
|
|
|
void
|
|
_PG_init(void)
|
|
{
|
|
if (!process_shared_preload_libraries_in_progress)
|
|
return;
|
|
|
|
ereport(LOG,
|
|
(errmsg("Module pacs is starting"),
|
|
errdetail("providing advanced stats structures.")));
|
|
pgstat_register_kind(PGSTAT_KIND_MEASURE, &measures);
|
|
|
|
/* mark stats as loaded */
|
|
measures_loaded = true;
|
|
}
|
|
|
|
/*
|
|
* Callback for flushing a measure
|
|
*/
|
|
static bool
|
|
measure_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
|
|
{
|
|
MeasureStatEntry *localent;
|
|
MeasuresStatShared *shfuncent;
|
|
|
|
localent = (MeasureStatEntry *) entry_ref->pending;
|
|
shfuncent = (MeasuresStatShared *) entry_ref->shared_stats;
|
|
|
|
if (!pgstat_lock_entry(entry_ref, nowait))
|
|
return false;
|
|
|
|
shfuncent->stats.measure += localent->measure;
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
check_loaded()
|
|
{
|
|
if (!measures_loaded)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("measures module not loaded"),
|
|
errhint("check shared_preload_libraries")));
|
|
}
|
|
|
|
/*
|
|
* The following code contains:
|
|
* - the exported C functions
|
|
* - the SQL API
|
|
*/
|
|
/*
|
|
* Create a measure
|
|
*/
|
|
PGDLLEXPORT void
|
|
pacsCreateMeasure(const char *name)
|
|
{
|
|
PgStat_EntryRef *entry_ref;
|
|
MeasuresStatShared *shstatent;
|
|
|
|
/* error if disabled */
|
|
check_loaded();
|
|
|
|
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_MEASURE, InvalidOid,
|
|
PGSTAT_MEASURE_IDX(name), false);
|
|
shstatent = (MeasuresStatShared *) entry_ref->shared_stats;
|
|
|
|
/* initialize shared memory data */
|
|
memset(&shstatent->stats, 0, sizeof(shstatent->stats));
|
|
pgstat_unlock_entry(entry_ref);
|
|
}
|
|
|
|
/*
|
|
* SQL API
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pacs_create_measure);
|
|
Datum
|
|
pacs_create_measure(PG_FUNCTION_ARGS)
|
|
{
|
|
Name name;
|
|
|
|
name = PG_GETARG_NAME(0);
|
|
pacsCreateMeasure(NameStr(*name));
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
/*
|
|
* Drop a measure
|
|
*
|
|
* XXX register to create, drop to unregister...
|
|
*/
|
|
PGDLLEXPORT void
|
|
pacsDropMeasure(const char *name)
|
|
{
|
|
/* error if disabled */
|
|
check_loaded();
|
|
|
|
/* drop and on faillure inform for later garbage collection */
|
|
if (!pgstat_drop_entry(PGSTAT_KIND_MEASURE, InvalidOid,
|
|
PGSTAT_MEASURE_IDX(name)))
|
|
pgstat_request_entry_refs_gc();
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pacs_drop_measure);
|
|
Datum
|
|
pacs_drop_measure(PG_FUNCTION_ARGS)
|
|
{
|
|
Name name;
|
|
|
|
name = PG_GETARG_NAME(0);
|
|
pacsDropMeasure(NameStr(*name));
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
/*
|
|
* Increment a measure
|
|
*
|
|
* By design, PostgreSQL does create the measure if absent!
|
|
*
|
|
* we do not manage ereport() here to not add overhead.
|
|
* XXX is it accurate ?
|
|
*/
|
|
PGDLLEXPORT void
|
|
pacsIncrementMeasure(const char *name)
|
|
{
|
|
PgStat_EntryRef *entry_ref;
|
|
MeasuresStatShared *shstatent;
|
|
MeasureStatEntry *statent;
|
|
|
|
/* quick leave if disabled */
|
|
if (!measures_loaded)
|
|
return;
|
|
|
|
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_MEASURE, InvalidOid,
|
|
PGSTAT_MEASURE_IDX(name), false);
|
|
|
|
shstatent = (MeasuresStatShared *) entry_ref->shared_stats;
|
|
statent = &shstatent->stats;
|
|
|
|
/* Update the measure */
|
|
statent->measure++;
|
|
|
|
pgstat_unlock_entry(entry_ref);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pacs_increment_measure);
|
|
Datum
|
|
pacs_increment_measure(PG_FUNCTION_ARGS)
|
|
{
|
|
Name name;
|
|
|
|
name = PG_GETARG_NAME(0);
|
|
pacsIncrementMeasure(NameStr(*name));
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
/*
|
|
* Support function for the SQL-callable functions. Returns
|
|
* a pointer to the measure struct.
|
|
*/
|
|
static MeasureStatEntry *
|
|
pacsGetMeasure(const char *name)
|
|
{
|
|
MeasureStatEntry *entry = NULL;
|
|
|
|
/* error if disabled */
|
|
check_loaded();
|
|
|
|
/* Compile the lookup key as a hash of the measure name */
|
|
entry = (MeasureStatEntry *) pgstat_fetch_entry(PGSTAT_KIND_MEASURE,
|
|
InvalidOid,
|
|
PGSTAT_MEASURE_IDX(name));
|
|
return entry;
|
|
}
|
|
|
|
/*
|
|
* SQL function returning the measure current value.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pacs_get_measure);
|
|
Datum
|
|
pacs_get_measure(PG_FUNCTION_ARGS)
|
|
{
|
|
MeasureStatEntry *entry;
|
|
Name name;
|
|
|
|
name = PG_GETARG_NAME(0);
|
|
|
|
entry = pacsGetMeasure(NameStr(*name));
|
|
if (entry == NULL)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("measure \"%s\" does not exist",
|
|
NameStr(*name)),
|
|
errhint("ensure the measure name is correct or "
|
|
"$$ select create_measure('%s')$$ to create it",
|
|
NameStr(*name))));
|
|
|
|
PG_RETURN_INT64(entry->measure);
|
|
}
|
|
|
|
/*
|
|
* Reset a measure
|
|
*/
|
|
PGDLLEXPORT void
|
|
pacsResetMeasure(const char *name)
|
|
{
|
|
/* error if disabled */
|
|
check_loaded();
|
|
pgstat_reset_entry(PGSTAT_KIND_MEASURE, InvalidOid,
|
|
PGSTAT_MEASURE_IDX(name), 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* SQL function reseting the measure(s)
|
|
*/
|
|
PG_FUNCTION_INFO_V1(pacs_reset_measure);
|
|
Datum
|
|
pacs_reset_measure(PG_FUNCTION_ARGS)
|
|
{
|
|
Name name;
|
|
name = PG_GETARG_NAME(0);
|
|
pacsResetMeasure(NameStr(*name));
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
/*
|
|
* Reset a measure
|
|
*/
|
|
PGDLLEXPORT void
|
|
pacsResetAllMeasures(void)
|
|
{
|
|
/* error if disabled */
|
|
check_loaded();
|
|
pgstat_reset_of_kind(PGSTAT_KIND_MEASURE);
|
|
return;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(pacs_reset_all_measures);
|
|
Datum
|
|
pacs_reset_all_measures(PG_FUNCTION_ARGS)
|
|
{
|
|
pacsResetAllMeasures();
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
// PG_FUNCTION_INFO_V1(pacs_list_all_measures);
|
|
// PGDLLEXPORT void
|
|
// pacs_list_all_measures(PgStat_Kind kind)
|
|
// {
|
|
// HASH_SEQ_STATUS status;
|
|
// PgStat_EntryRef *entry_ref;
|
|
//
|
|
// const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
|
|
// if (!kind_info || !kind_info->hash_table)
|
|
// {
|
|
// elog(ERROR, "Invalid or unsupported statistics kind.");
|
|
// return;
|
|
// }
|
|
//
|
|
// hash_seq_init(&status, kind_info->hash_table);
|
|
//
|
|
// while ((entry_ref = hash_seq_search(&status)) != NULL)
|
|
// {
|
|
// elog(INFO, "Key for kind %d: %u/%llu", kind,
|
|
// entry_ref->key.dboid, (unsigned long long)entry_ref->key.objid);
|
|
// }
|
|
// }
|