From: Robert Haas Date: Tue, 1 Jul 2025 20:10:36 +0000 (-0400) Subject: start some advice storage ideas in motion X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=a853ffec52f4c0a005b8a813e027130cdea69da0;p=users%2Frhaas%2Fpostgres.git start some advice storage ideas in motion --- diff --git a/contrib/pg_plan_advice/Makefile b/contrib/pg_plan_advice/Makefile index e0f19dc022..afeea2e5dd 100644 --- a/contrib/pg_plan_advice/Makefile +++ b/contrib/pg_plan_advice/Makefile @@ -4,7 +4,7 @@ MODULE_big = pg_plan_advice OBJS = \ $(WIN32RES) \ pg_plan_advice.o \ - pgpa_buffer.o \ + pgpa_collector.o \ pgpa_identifier.o \ pgpa_join.o \ pgpa_output.o \ diff --git a/contrib/pg_plan_advice/meson.build b/contrib/pg_plan_advice/meson.build index 81d313a044..db510082d6 100644 --- a/contrib/pg_plan_advice/meson.build +++ b/contrib/pg_plan_advice/meson.build @@ -2,7 +2,7 @@ pg_plan_advice_sources = files( 'pg_plan_advice.c', - 'pgpa_buffer.c', + 'pgpa_collector.c', 'pgpa_identifier.c', 'pgpa_join.c', 'pgpa_output.c', diff --git a/contrib/pg_plan_advice/pg_plan_advice.c b/contrib/pg_plan_advice/pg_plan_advice.c index 1344bcc169..497b69457e 100644 --- a/contrib/pg_plan_advice/pg_plan_advice.c +++ b/contrib/pg_plan_advice/pg_plan_advice.c @@ -11,35 +11,112 @@ */ #include "postgres.h" -#include "funcapi.h" #include "pgpa_identifier.h" +#include "pg_plan_advice.h" #include "pgpa_output.h" #include "pgpa_walker.h" +#include "funcapi.h" +#include "storage/dsm_registry.h" +#include "utils/guc.h" + PG_MODULE_MAGIC; +static pgpa_shared_state *pgpa_state = NULL; + +/* GUC variables */ +static int pg_plan_advice_local_collection_limit = 0; +static int pg_plan_advice_shared_collection_limit = 0; + /* Saved hook values */ static ExecutorStart_hook_type prev_ExecutorStart = NULL; static bool pg_plan_advice_ExecutorStart(QueryDesc *queryDesc, int eflags); -static void pgpa_check_plan(PlannedStmt *pstmt); +static void pgpa_collect_advice(PlannedStmt *pstmt); /* - * Initialize this module + * Initialize this module. */ void _PG_init(void) { + DefineCustomIntVariable("pg_plan_advice.local_collection_limit", + "# of advice entries to retain in per-backend memory", + NULL, + &pg_plan_advice_local_collection_limit, + 0, + 0, INT_MAX, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + + DefineCustomIntVariable("pg_plan_advice.shared_collection_limit", + "# of advice entries to retain in shared memory", + NULL, + &pg_plan_advice_shared_collection_limit, + 0, + 0, INT_MAX, + PGC_SUSET, + 0, + NULL, + NULL, + NULL); + + /* Install hooks */ prev_ExecutorStart = ExecutorStart_hook; ExecutorStart_hook = pg_plan_advice_ExecutorStart; } +/* + * Initialize shared state when first created. + */ +static void +pgpa_init_shared_state(void *ptr) +{ + pgpa_shared_state *state = (pgpa_shared_state *) ptr; + + state->area = DSA_HANDLE_INVALID; + LWLockInitialize(&state->lock, LWLockNewTrancheId()); + state->advice_count = 0; + state->advice_array = InvalidDsaPointer; +} + +/* + * Get a pointer to our shared state. + * + * If no shared state exists, create and initialize it. If it does exist but + * this backend has not yet accessed it, attach to it. Otherwise, just return + * our cached pointer. + * + * Along the way, make sure the relevant LWLock tranche is registered. + */ +pgpa_shared_state * +pg_plan_advice_attach(void) +{ + pgpa_shared_state *state; + bool found; + + if (pgpa_state == NULL) + { + state = GetNamedDSMSegment("pg_plan_advice", sizeof(pgpa_shared_state), + pgpa_init_shared_state, &found); + LWLockRegisterTranche(pgpa_state->lock.tranche, "pg_plan_advice"); + pgpa_state = state; + } + + return pgpa_state; +} + static bool pg_plan_advice_ExecutorStart(QueryDesc *queryDesc, int eflags) { PlannedStmt *pstmt = queryDesc->plannedstmt; - pgpa_check_plan(pstmt); + if (pg_plan_advice_local_collection_limit > 0 + || pg_plan_advice_shared_collection_limit > 0) + pgpa_collect_advice(pstmt); if (prev_ExecutorStart) return prev_ExecutorStart(queryDesc, eflags); @@ -48,10 +125,10 @@ pg_plan_advice_ExecutorStart(QueryDesc *queryDesc, int eflags) } static void -pgpa_check_plan(PlannedStmt *pstmt) +pgpa_collect_advice(PlannedStmt *pstmt) { pgpa_plan_walker_context context; - StringInfoData buf; + StringInfoData buf; ListCell *lc; const char **rt_identifiers; @@ -63,7 +140,7 @@ pgpa_check_plan(PlannedStmt *pstmt) foreach(lc, pstmt->subplans) { - Plan *plan = lfirst(lc); + Plan *plan = lfirst(lc); if (plan != NULL) pgpa_plan_walker(&context, plan, 0, NULL, NULL); diff --git a/contrib/pg_plan_advice/pg_plan_advice.h b/contrib/pg_plan_advice/pg_plan_advice.h index 5fd5493b0c..81ac95e9f9 100644 --- a/contrib/pg_plan_advice/pg_plan_advice.h +++ b/contrib/pg_plan_advice/pg_plan_advice.h @@ -12,9 +12,17 @@ #ifndef PG_PLAN_ADVICE_H #define PG_PLAN_ADVICE_H -#include "nodes/plannodes.h" +#include "storage/lwlock.h" +#include "utils/dsa.h" -/* create_advice.c */ -extern bool append_advice_from_plan(StringInfo buf, PlannedStmt *stmt); +typedef struct pgpa_shared_state +{ + dsa_handle area; + LWLock lock; + uint64 advice_count; + dsa_pointer advice_array; +} pgpa_shared_state; + +extern pgpa_shared_state *pg_plan_advice_attach(void); #endif diff --git a/contrib/pg_plan_advice/pgpa_buffer.c b/contrib/pg_plan_advice/pgpa_buffer.c deleted file mode 100644 index d8cbd1aa8e..0000000000 --- a/contrib/pg_plan_advice/pgpa_buffer.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "postgres.h" - -typedef struct pgpa_collected_advice -{ - Oid userid; /* user OID */ - Oid dbid; /* database OID */ - uint64 queryid; /* query identifier */ - int nesting_level; /* query nesting level */ - char advice_offset; /* start of advice in textual data */ - char textual_data[FLEXIBLE_ARRAY_MEMBER]; -} pgpa_collected_advice; - -#if 0 -static inline const char * -query_string(pgpa_collected_advice *ca) -{ - return ca->textual_data; -} - -static inline const char * -advice_string(pgpa_collected_advice *ca) -{ - return ca->textual_data + ca->advice_offset; -} -#endif diff --git a/contrib/pg_plan_advice/pgpa_collector.c b/contrib/pg_plan_advice/pgpa_collector.c new file mode 100644 index 0000000000..927ea28b01 --- /dev/null +++ b/contrib/pg_plan_advice/pgpa_collector.c @@ -0,0 +1,63 @@ +#include "postgres.h" + +#include "datatype/timestamp.h" +#include "nodes/pg_list.h" + +typedef struct pgpa_collected_advice +{ + Oid userid; /* user OID */ + Oid dbid; /* database OID */ + uint64 queryid; /* query identifier */ + int nesting_level; /* query nesting level */ + TimestampTz timestamp; /* query timestamp */ + char advice_offset; /* start of advice in textual data */ + char textual_data[FLEXIBLE_ARRAY_MEMBER]; +} pgpa_collected_advice; + +static List *locally_collected_advice = NIL; + +extern void +pgpa_save_collected_advice(Oid userid, Oid dbid, uint64 queryId, + int nesting_level, TimestampTz timestamp, + const char *query_string, const char *advice_string); + +#if 0 +static inline const char * +query_string(pgpa_collected_advice *ca) +{ + return ca->textual_data; +} + +static inline const char * +advice_string(pgpa_collected_advice *ca) +{ + return ca->textual_data + ca->advice_offset; +} +#endif + +void +pgpa_save_collected_advice(Oid userid, Oid dbid, uint64 queryId, + int nesting_level, TimestampTz timestamp, + const char *query_string, const char *advice_string) +{ + size_t query_string_length = strlen(query_string) + 1; + size_t advice_string_length = strlen(advice_string) + 1; + size_t total_length; + pgpa_collected_advice *ca; + + total_length = offsetof(pgpa_collected_advice, textual_data) + + query_string_length + advice_string_length; + + ca = palloc(total_length); + ca->userid = userid; + ca->dbid = dbid; + ca->queryid = queryId; + ca->nesting_level = nesting_level; + ca->timestamp = timestamp; + + memcpy(ca->textual_data, query_string, query_string_length); + memcpy(&ca->textual_data[query_string_length], + advice_string, advice_string_length); + + locally_collected_advice = lappend(locally_collected_advice, ca); +}