#define PG_GET_ADVICE_COLUMNS 7
+/*
+ * Advice extracted from one query plan, together with the query string
+ * and various other identifying details.
+ */
typedef struct pgpa_collected_advice
{
Oid userid; /* user OID */
char textual_data[FLEXIBLE_ARRAY_MEMBER];
} pgpa_collected_advice;
+/*
+ * A bunch of pointers to pgpa_collected_advice objects, stored in
+ * backend-local memory.
+ */
typedef struct pgpa_local_advice_chunk
{
pgpa_collected_advice *entries[ADVICE_CHUNK_SIZE];
} pgpa_local_advice_chunk;
+/*
+ * Information about all of the pgpa_collected_advice objects that we're
+ * storing in local memory.
+ *
+ * We assign consecutive IDs, starting from 0, to each pgpa_collected_advice
+ * object that we store. The actual storage is an array of chunks, which
+ * helps keep memcpy() overhead low when we start discarding older data.
+ */
typedef struct pgpa_local_advice
{
uint64 next_id;
pgpa_local_advice_chunk **chunks;
} pgpa_local_advice;
-static pgpa_local_advice *local_advice;
+/*
+ * Just like pgpa_local_advice_chunk, but stored in a dynamic shared area,
+ * so we must use dsa_pointer instead of native pointers.
+ */
+typedef struct pgpa_shared_advice_chunk
+{
+ dsa_pointer entries[ADVICE_CHUNK_SIZE];
+} pgpa_shared_advice_chunk;
+
+/*
+ * Just like pgpa_local_advice, but stored in a dynamic shared area, so
+ * we must use dsa_pointer instead of native pointers.
+ */
+typedef struct pgpa_shared_advice
+{
+ uint64 next_id;
+ uint64 oldest_id;
+ uint64 base_id;
+ int chunk_array_allocated_size;
+ dsa_pointer chunks;
+} pgpa_shared_advice;
+
+/* Pointers to local and shared collectors */
+static pgpa_local_advice *local_collector = NULL;
+static pgpa_shared_advice *shared_collector = NULL;
+/* Static functions */
static pgpa_collected_advice *pgpa_make_collected_advice(Oid userid,
Oid dbid,
uint64 queryId,
dsa_pointer *result);
static void pgpa_store_local_advice(pgpa_collected_advice *ca);
static void pgpa_trim_local_advice(void);
+static void pgpa_store_shared_advice(dsa_pointer ca_pointer);
/* Helper function to extract the query string from pgpa_collected_advice */
static inline const char *
pgpa_make_collected_advice(userid, dbid, queryId, now,
query_string, advice_string, area,
&ca_pointer);
- /* XXX do something */
+ pgpa_store_shared_advice(ca_pointer);
/* XXX over limit? */
}
}
{
uint64 chunk_number;
uint64 chunk_offset;
- pgpa_local_advice *la = local_advice;
+ pgpa_local_advice *la = local_collector;
/* If the local advice collector isn't initialized yet, do that now. */
if (la == NULL)
la->chunk_array_allocated_size = ADVICE_CHUNK_ARRAY_SIZE;
la->chunks = palloc0_array(pgpa_local_advice_chunk *,
la->chunk_array_allocated_size);
- local_advice = la;
+ local_collector = la;
}
/* Compute chunk and offset at which to store this advice. */
static void
pgpa_trim_local_advice(void)
{
- pgpa_local_advice *la = local_advice;
+ pgpa_local_advice *la = local_collector;
uint64 current_count;
uint64 trim_count;
uint64 total_chunk_count;
la->base_id += trim_chunk_count * ADVICE_CHUNK_SIZE;
}
+/*
+ * Add a pg_collected_advice object to the shared advice collection.
+ *
+ * 'ca_pointer' should have been allocated from the pg_plan_advice DSA area
+ * and should point to an object of type pgpa_collected_advice.
+ */
+static void
+pgpa_store_shared_advice(dsa_pointer ca_pointer)
+{
+ uint64 chunk_number;
+ uint64 chunk_offset;
+ pgpa_shared_state *state = pg_plan_advice_attach();
+ dsa_area *area = pg_plan_advice_dsa_area();
+ pgpa_shared_advice *sa = shared_collector;
+ dsa_pointer *chunk_array;
+ pgpa_shared_advice_chunk *chunk;
+
+ /* Lock the shared state. */
+ LWLockAcquire(&state->lock, LW_EXCLUSIVE);
+
+ /*
+ * If we're not attached to the shared advice collector yet, fix that now.
+ * If we're the first ones to attach, we may need to create the object.
+ */
+ if (sa == NULL)
+ {
+ if (state->shared_collector == InvalidDsaPointer)
+ state->shared_collector =
+ dsa_allocate0(area, sizeof(pgpa_shared_advice));
+ shared_collector = sa = dsa_get_address(area, state->shared_collector);
+ }
+
+ /*
+ * It's possible that some other backend may have succeeded in creating
+ * the main collector object but failed to allocate an initial chunk array,
+ * so we must be prepared to allocate the chunk array here whether or not
+ * we created the collector object.
+ */
+ if (shared_collector->chunk_array_allocated_size == 0)
+ {
+ sa->chunks =
+ dsa_allocate0(area,
+ sizeof(dsa_pointer) * ADVICE_CHUNK_ARRAY_SIZE);
+ sa->chunk_array_allocated_size = ADVICE_CHUNK_ARRAY_SIZE;
+ }
+
+ /* Compute chunk and offset at which to store this advice. */
+ chunk_number = (sa->next_id - sa->base_id) / ADVICE_CHUNK_SIZE;
+ chunk_offset = (sa->next_id - sa->base_id) % ADVICE_CHUNK_SIZE;
+
+ /* Get the address of the chunk array and, if needed, extend it. */
+ if (chunk_number > sa->chunk_array_allocated_size)
+ {
+ int new_size;
+ dsa_pointer new_chunks;
+
+ /*
+ * DSA can't enlarge an existing allocation, so we must make a new
+ * allocation and copy data over.
+ */
+ new_size = sa->chunk_array_allocated_size + ADVICE_CHUNK_ARRAY_SIZE;
+ new_chunks = dsa_allocate0(area, sizeof(dsa_pointer) * new_size);
+ chunk_array = dsa_get_address(area, new_chunks);
+ memcpy(chunk_array, dsa_get_address(area, sa->chunks),
+ sizeof(dsa_pointer) * sa->chunk_array_allocated_size);
+ dsa_free(area, sa->chunks);
+ sa->chunks = new_chunks;
+ sa->chunk_array_allocated_size = new_size;
+ }
+ else
+ chunk_array = dsa_get_address(area, sa->chunks);
+
+ /* Get the address of the desired chunk, allocating it if needed. */
+ if (chunk_array[chunk_number] == InvalidDsaPointer)
+ chunk_array[chunk_number] =
+ dsa_allocate0(area, sizeof(pgpa_shared_advice_chunk));
+ chunk = dsa_get_address(area, chunk_array[chunk_number]);
+
+ /* Save pointer and bump next-id counter. */
+ Assert(chunk->entries[chunk_offset] == InvalidDsaPointer);
+ chunk->entries[chunk_offset] = ca_pointer;
+ ++sa->next_id;
+
+ /* Release lock on shared state. */
+ LWLockRelease(&state->lock);
+
+}
+
/*
* SQL-callable SRF to return locally collected advice
*/
pg_get_collected_local_advice(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- pgpa_local_advice *la = local_advice;
+ pgpa_local_advice *la = local_collector;
/*
* XXX. Is this the correct thing from a security point of view?