*/
#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);
}
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;
foreach(lc, pstmt->subplans)
{
- Plan *plan = lfirst(lc);
+ Plan *plan = lfirst(lc);
if (plan != NULL)
pgpa_plan_walker(&context, plan, 0, NULL, NULL);
+++ /dev/null
-#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
--- /dev/null
+#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);
+}