1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00
Files
postgres/contrib/pg_stash_advice/stashfuncs.c
Michael Paquier b3a95566fc Fix set of typos and grammar mistakes
This is similar to d3bba04154, batching all the reports of this type
received since the last batch.  This covers typos and inconsistencies
for the most part.

The user-visible documentation change impacts only HEAD.
2026-06-24 16:00:28 +09:00

348 lines
8.8 KiB
C

/*-------------------------------------------------------------------------
*
* stashfuncs.c
* SQL interface to pg_stash_advice
*
* Copyright (c) 2016-2026, PostgreSQL Global Development Group
*
* contrib/pg_stash_advice/stashfuncs.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "common/hashfn.h"
#include "fmgr.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "pg_stash_advice.h"
#include "utils/builtins.h"
#include "utils/tuplestore.h"
PG_FUNCTION_INFO_V1(pg_create_advice_stash);
PG_FUNCTION_INFO_V1(pg_drop_advice_stash);
PG_FUNCTION_INFO_V1(pg_get_advice_stash_contents);
PG_FUNCTION_INFO_V1(pg_get_advice_stashes);
PG_FUNCTION_INFO_V1(pg_set_stashed_advice);
PG_FUNCTION_INFO_V1(pg_start_stash_advice_worker);
typedef struct pgsa_stash_count
{
uint32 status;
uint64 pgsa_stash_id;
int64 num_entries;
} pgsa_stash_count;
#define SH_PREFIX pgsa_stash_count_table
#define SH_ELEMENT_TYPE pgsa_stash_count
#define SH_KEY_TYPE uint64
#define SH_KEY pgsa_stash_id
#define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(uint64))
#define SH_EQUAL(tb, a, b) (a == b)
#define SH_SCOPE static inline
#define SH_DEFINE
#define SH_DECLARE
#include "lib/simplehash.h"
/*
* SQL-callable function to create an advice stash
*/
Datum
pg_create_advice_stash(PG_FUNCTION_ARGS)
{
char *stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
pgsa_check_stash_name(stash_name);
if (unlikely(pgsa_entry_dshash == NULL))
pgsa_attach();
pgsa_check_lockout();
LWLockAcquire(&pgsa_state->lock, LW_EXCLUSIVE);
pgsa_create_stash(stash_name);
LWLockRelease(&pgsa_state->lock);
PG_RETURN_VOID();
}
/*
* SQL-callable function to drop an advice stash
*/
Datum
pg_drop_advice_stash(PG_FUNCTION_ARGS)
{
char *stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
pgsa_check_stash_name(stash_name);
if (unlikely(pgsa_entry_dshash == NULL))
pgsa_attach();
pgsa_check_lockout();
LWLockAcquire(&pgsa_state->lock, LW_EXCLUSIVE);
pgsa_drop_stash(stash_name);
LWLockRelease(&pgsa_state->lock);
PG_RETURN_VOID();
}
/*
* SQL-callable function to provide a list of advice stashes
*/
Datum
pg_get_advice_stashes(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
dshash_seq_status iterator;
pgsa_entry *entry;
pgsa_stash *stash;
pgsa_stash_count_table_hash *chash;
InitMaterializedSRF(fcinfo, 0);
/* Attach to dynamic shared memory if not already done. */
if (unlikely(pgsa_entry_dshash == NULL))
pgsa_attach();
/* If stash data is still being restored from disk, ignore. */
if (pg_atomic_unlocked_test_flag(&pgsa_state->stashes_ready))
return (Datum) 0;
/* Tally up the number of entries per stash. */
chash = pgsa_stash_count_table_create(CurrentMemoryContext, 64, NULL);
dshash_seq_init(&iterator, pgsa_entry_dshash, true);
while ((entry = dshash_seq_next(&iterator)) != NULL)
{
pgsa_stash_count *c;
bool found;
c = pgsa_stash_count_table_insert(chash,
entry->key.pgsa_stash_id,
&found);
if (!found)
c->num_entries = 1;
else
c->num_entries++;
}
dshash_seq_term(&iterator);
/* Emit results. */
dshash_seq_init(&iterator, pgsa_stash_dshash, true);
while ((stash = dshash_seq_next(&iterator)) != NULL)
{
Datum values[2];
bool nulls[2];
pgsa_stash_count *c;
values[0] = CStringGetTextDatum(stash->name);
nulls[0] = false;
c = pgsa_stash_count_table_lookup(chash, stash->pgsa_stash_id);
values[1] = Int64GetDatum(c == NULL ? 0 : c->num_entries);
nulls[1] = false;
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values,
nulls);
}
dshash_seq_term(&iterator);
return (Datum) 0;
}
/*
* SQL-callable function to provide advice stash contents
*/
Datum
pg_get_advice_stash_contents(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
dshash_seq_status iterator;
char *stash_name = NULL;
pgsa_stash_name_table_hash *nhash = NULL;
uint64 stash_id = 0;
pgsa_entry *entry;
InitMaterializedSRF(fcinfo, 0);
/* Attach to dynamic shared memory if not already done. */
if (unlikely(pgsa_entry_dshash == NULL))
pgsa_attach();
/* If stash data is still being restored from disk, ignore. */
if (pg_atomic_unlocked_test_flag(&pgsa_state->stashes_ready))
return (Datum) 0;
/* User can pass NULL for all stashes, or the name of a specific stash. */
if (!PG_ARGISNULL(0))
{
stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
pgsa_check_stash_name(stash_name);
stash_id = pgsa_lookup_stash_id(stash_name);
/* If the user specified a stash name, it should exist. */
if (stash_id == 0)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("advice stash \"%s\" does not exist", stash_name));
}
else
{
pgsa_stash *stash;
/*
* If we're dumping data about all stashes, we need an ID->name lookup
* table.
*/
nhash = pgsa_stash_name_table_create(CurrentMemoryContext, 64, NULL);
dshash_seq_init(&iterator, pgsa_stash_dshash, true);
while ((stash = dshash_seq_next(&iterator)) != NULL)
{
pgsa_stash_name *n;
bool found;
n = pgsa_stash_name_table_insert(nhash,
stash->pgsa_stash_id,
&found);
Assert(!found);
n->name = pstrdup(stash->name);
}
dshash_seq_term(&iterator);
}
/* Now iterate over all the entries. */
dshash_seq_init(&iterator, pgsa_entry_dshash, false);
while ((entry = dshash_seq_next(&iterator)) != NULL)
{
Datum values[3];
bool nulls[3];
char *this_stash_name;
char *advice_string;
/* Skip incomplete entries where the advice string was never set. */
if (entry->advice_string == InvalidDsaPointer)
continue;
if (stash_id != 0)
{
/*
* We're only dumping data for one particular stash, so skip
* entries for any other stash and use the stash name specified by
* the user.
*/
if (stash_id != entry->key.pgsa_stash_id)
continue;
this_stash_name = stash_name;
}
else
{
pgsa_stash_name *n;
/*
* We're dumping data for all stashes, so look up the correct name
* to use in the hash table. If nothing is found, which is
* possible due to race conditions, make up a string to use.
*/
n = pgsa_stash_name_table_lookup(nhash, entry->key.pgsa_stash_id);
if (n != NULL)
this_stash_name = n->name;
else
this_stash_name = psprintf("<stash %" PRIu64 ">",
entry->key.pgsa_stash_id);
}
/* Work out tuple values. */
values[0] = CStringGetTextDatum(this_stash_name);
nulls[0] = false;
values[1] = Int64GetDatum(entry->key.queryId);
nulls[1] = false;
advice_string = dsa_get_address(pgsa_dsa_area, entry->advice_string);
values[2] = CStringGetTextDatum(advice_string);
nulls[2] = false;
/* Emit the tuple. */
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values,
nulls);
}
dshash_seq_term(&iterator);
return (Datum) 0;
}
/*
* SQL-callable function to update an advice stash entry for a particular
* query ID
*
* If the advice string (the third argument) is NULL, we delete any existing
* advice stash entry; otherwise, we either create an entry or update it with
* the new advice string.
*/
Datum
pg_set_stashed_advice(PG_FUNCTION_ARGS)
{
char *stash_name;
int64 queryId;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL();
/* Get and check advice stash name. */
stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
pgsa_check_stash_name(stash_name);
/*
* Get and check query ID.
*
* Query ID 0 means no query ID was computed, so reject that.
*/
queryId = PG_GETARG_INT64(1);
if (queryId == 0)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot set advice string for query ID 0"));
/* Attach to dynamic shared memory if not already done. */
if (unlikely(pgsa_entry_dshash == NULL))
pgsa_attach();
/* Don't allow writes if stash data is still being restored from disk. */
pgsa_check_lockout();
/* Now call the appropriate function to do the real work. */
if (PG_ARGISNULL(2))
{
LWLockAcquire(&pgsa_state->lock, LW_SHARED);
pgsa_clear_advice_string(stash_name, queryId);
LWLockRelease(&pgsa_state->lock);
}
else
{
char *advice_string = text_to_cstring(PG_GETARG_TEXT_PP(2));
LWLockAcquire(&pgsa_state->lock, LW_SHARED);
pgsa_set_advice_string(stash_name, queryId, advice_string);
LWLockRelease(&pgsa_state->lock);
}
PG_RETURN_VOID();
}
/*
* SQL-callable function to start the persistence background worker.
*/
Datum
pg_start_stash_advice_worker(PG_FUNCTION_ARGS)
{
pid_t pid;
if (unlikely(pgsa_entry_dshash == NULL))
pgsa_attach();
LWLockAcquire(&pgsa_state->lock, LW_SHARED);
pid = pgsa_state->bgworker_pid;
LWLockRelease(&pgsa_state->lock);
if (pid != InvalidPid)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stash_advice worker is already running under PID %d",
(int) pid)));
pgsa_start_worker();
PG_RETURN_VOID();
}