diff options
Diffstat (limited to 'src/backend/utils/misc/guc_funcs.c')
-rw-r--r-- | src/backend/utils/misc/guc_funcs.c | 1047 |
1 files changed, 1047 insertions, 0 deletions
diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c new file mode 100644 index 00000000000..3d2df18659b --- /dev/null +++ b/src/backend/utils/misc/guc_funcs.c @@ -0,0 +1,1047 @@ +/*-------------------------------------------------------------------- + * + * guc_funcs.c + * + * SQL commands and SQL-accessible functions related to GUC variables. + * + * + * Copyright (c) 2000-2022, PostgreSQL Global Development Group + * Written by Peter Eisentraut <peter_e@gmx.net>. + * + * IDENTIFICATION + * src/backend/utils/misc/guc_funcs.c + * + *-------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <sys/stat.h> +#include <unistd.h> + +#include "access/xact.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_parameter_acl.h" +#include "funcapi.h" +#include "guc_internal.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/backend_status.h" +#include "utils/builtins.h" +#include "utils/guc_tables.h" +#include "utils/snapmgr.h" + +static char *flatten_set_variable_args(const char *name, List *args); +static void ShowGUCConfigOption(const char *name, DestReceiver *dest); +static void ShowAllGUCConfig(DestReceiver *dest); + + +/* + * SET command + */ +void +ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel) +{ + GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET; + + /* + * Workers synchronize these parameters at the start of the parallel + * operation; then, we block SET during the operation. + */ + if (IsInParallelMode()) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TRANSACTION_STATE), + errmsg("cannot set parameters during a parallel operation"))); + + switch (stmt->kind) + { + case VAR_SET_VALUE: + case VAR_SET_CURRENT: + if (stmt->is_local) + WarnNoTransactionBlock(isTopLevel, "SET LOCAL"); + (void) set_config_option(stmt->name, + ExtractSetVariableArgs(stmt), + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + action, true, 0, false); + break; + case VAR_SET_MULTI: + + /* + * Special-case SQL syntaxes. The TRANSACTION and SESSION + * CHARACTERISTICS cases effectively set more than one variable + * per statement. TRANSACTION SNAPSHOT only takes one argument, + * but we put it here anyway since it's a special case and not + * related to any GUC variable. + */ + if (strcmp(stmt->name, "TRANSACTION") == 0) + { + ListCell *head; + + WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION"); + + foreach(head, stmt->args) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation") == 0) + SetPGVariable("transaction_isolation", + list_make1(item->arg), stmt->is_local); + else if (strcmp(item->defname, "transaction_read_only") == 0) + SetPGVariable("transaction_read_only", + list_make1(item->arg), stmt->is_local); + else if (strcmp(item->defname, "transaction_deferrable") == 0) + SetPGVariable("transaction_deferrable", + list_make1(item->arg), stmt->is_local); + else + elog(ERROR, "unexpected SET TRANSACTION element: %s", + item->defname); + } + } + else if (strcmp(stmt->name, "SESSION CHARACTERISTICS") == 0) + { + ListCell *head; + + foreach(head, stmt->args) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation") == 0) + SetPGVariable("default_transaction_isolation", + list_make1(item->arg), stmt->is_local); + else if (strcmp(item->defname, "transaction_read_only") == 0) + SetPGVariable("default_transaction_read_only", + list_make1(item->arg), stmt->is_local); + else if (strcmp(item->defname, "transaction_deferrable") == 0) + SetPGVariable("default_transaction_deferrable", + list_make1(item->arg), stmt->is_local); + else + elog(ERROR, "unexpected SET SESSION element: %s", + item->defname); + } + } + else if (strcmp(stmt->name, "TRANSACTION SNAPSHOT") == 0) + { + A_Const *con = linitial_node(A_Const, stmt->args); + + if (stmt->is_local) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented"))); + + WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION"); + ImportSnapshot(strVal(&con->val)); + } + else + elog(ERROR, "unexpected SET MULTI element: %s", + stmt->name); + break; + case VAR_SET_DEFAULT: + if (stmt->is_local) + WarnNoTransactionBlock(isTopLevel, "SET LOCAL"); + /* fall through */ + case VAR_RESET: + if (strcmp(stmt->name, "transaction_isolation") == 0) + WarnNoTransactionBlock(isTopLevel, "RESET TRANSACTION"); + + (void) set_config_option(stmt->name, + NULL, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + action, true, 0, false); + break; + case VAR_RESET_ALL: + ResetAllOptions(); + break; + } + + /* Invoke the post-alter hook for setting this GUC variable, by name. */ + InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name, + ACL_SET, stmt->kind, false); +} + +/* + * Get the value to assign for a VariableSetStmt, or NULL if it's RESET. + * The result is palloc'd. + * + * This is exported for use by actions such as ALTER ROLE SET. + */ +char * +ExtractSetVariableArgs(VariableSetStmt *stmt) +{ + switch (stmt->kind) + { + case VAR_SET_VALUE: + return flatten_set_variable_args(stmt->name, stmt->args); + case VAR_SET_CURRENT: + return GetConfigOptionByName(stmt->name, NULL, false); + default: + return NULL; + } +} + +/* + * flatten_set_variable_args + * Given a parsenode List as emitted by the grammar for SET, + * convert to the flat string representation used by GUC. + * + * We need to be told the name of the variable the args are for, because + * the flattening rules vary (ugh). + * + * The result is NULL if args is NIL (i.e., SET ... TO DEFAULT), otherwise + * a palloc'd string. + */ +static char * +flatten_set_variable_args(const char *name, List *args) +{ + struct config_generic *record; + int flags; + StringInfoData buf; + ListCell *l; + + /* Fast path if just DEFAULT */ + if (args == NIL) + return NULL; + + /* + * Get flags for the variable; if it's not known, use default flags. + * (Caller might throw error later, but not our business to do so here.) + */ + record = find_option(name, false, true, WARNING); + if (record) + flags = record->flags; + else + flags = 0; + + /* Complain if list input and non-list variable */ + if ((flags & GUC_LIST_INPUT) == 0 && + list_length(args) != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("SET %s takes only one argument", name))); + + initStringInfo(&buf); + + /* + * Each list member may be a plain A_Const node, or an A_Const within a + * TypeCast; the latter case is supported only for ConstInterval arguments + * (for SET TIME ZONE). + */ + foreach(l, args) + { + Node *arg = (Node *) lfirst(l); + char *val; + TypeName *typeName = NULL; + A_Const *con; + + if (l != list_head(args)) + appendStringInfoString(&buf, ", "); + + if (IsA(arg, TypeCast)) + { + TypeCast *tc = (TypeCast *) arg; + + arg = tc->arg; + typeName = tc->typeName; + } + + if (!IsA(arg, A_Const)) + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg)); + con = (A_Const *) arg; + + switch (nodeTag(&con->val)) + { + case T_Integer: + appendStringInfo(&buf, "%d", intVal(&con->val)); + break; + case T_Float: + /* represented as a string, so just copy it */ + appendStringInfoString(&buf, castNode(Float, &con->val)->fval); + break; + case T_String: + val = strVal(&con->val); + if (typeName != NULL) + { + /* + * Must be a ConstInterval argument for TIME ZONE. Coerce + * to interval and back to normalize the value and account + * for any typmod. + */ + Oid typoid; + int32 typmod; + Datum interval; + char *intervalout; + + typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod); + Assert(typoid == INTERVALOID); + + interval = + DirectFunctionCall3(interval_in, + CStringGetDatum(val), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(typmod)); + + intervalout = + DatumGetCString(DirectFunctionCall1(interval_out, + interval)); + appendStringInfo(&buf, "INTERVAL '%s'", intervalout); + } + else + { + /* + * Plain string literal or identifier. For quote mode, + * quote it if it's not a vanilla identifier. + */ + if (flags & GUC_LIST_QUOTE) + appendStringInfoString(&buf, quote_identifier(val)); + else + appendStringInfoString(&buf, val); + } + break; + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(&con->val)); + break; + } + } + + return buf.data; +} + +/* + * SetPGVariable - SET command exported as an easily-C-callable function. + * + * This provides access to SET TO value, as well as SET TO DEFAULT (expressed + * by passing args == NIL), but not SET FROM CURRENT functionality. + */ +void +SetPGVariable(const char *name, List *args, bool is_local) +{ + char *argstring = flatten_set_variable_args(name, args); + + /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */ + (void) set_config_option(name, + argstring, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET, + true, 0, false); +} + +/* + * SET command wrapped as a SQL callable function. + */ +Datum +set_config_by_name(PG_FUNCTION_ARGS) +{ + char *name; + char *value; + char *new_value; + bool is_local; + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("SET requires parameter name"))); + + /* Get the GUC variable name */ + name = TextDatumGetCString(PG_GETARG_DATUM(0)); + + /* Get the desired value or set to NULL for a reset request */ + if (PG_ARGISNULL(1)) + value = NULL; + else + value = TextDatumGetCString(PG_GETARG_DATUM(1)); + + /* + * Get the desired state of is_local. Default to false if provided value + * is NULL + */ + if (PG_ARGISNULL(2)) + is_local = false; + else + is_local = PG_GETARG_BOOL(2); + + /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */ + (void) set_config_option(name, + value, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET, + true, 0, false); + + /* get the new current value */ + new_value = GetConfigOptionByName(name, NULL, false); + + /* Convert return string to text */ + PG_RETURN_TEXT_P(cstring_to_text(new_value)); +} + + +/* + * SHOW command + */ +void +GetPGVariable(const char *name, DestReceiver *dest) +{ + if (guc_name_compare(name, "all") == 0) + ShowAllGUCConfig(dest); + else + ShowGUCConfigOption(name, dest); +} + +/* + * Get a tuple descriptor for SHOW's result + */ +TupleDesc +GetPGVariableResultDesc(const char *name) +{ + TupleDesc tupdesc; + + if (guc_name_compare(name, "all") == 0) + { + /* need a tuple descriptor representing three TEXT columns */ + tupdesc = CreateTemplateTupleDesc(3); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", + TEXTOID, -1, 0); + } + else + { + const char *varname; + + /* Get the canonical spelling of name */ + (void) GetConfigOptionByName(name, &varname, false); + + /* need a tuple descriptor representing a single TEXT column */ + tupdesc = CreateTemplateTupleDesc(1); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, + TEXTOID, -1, 0); + } + return tupdesc; +} + +/* + * SHOW one variable + */ +static void +ShowGUCConfigOption(const char *name, DestReceiver *dest) +{ + TupOutputState *tstate; + TupleDesc tupdesc; + const char *varname; + char *value; + + /* Get the value and canonical spelling of name */ + value = GetConfigOptionByName(name, &varname, false); + + /* need a tuple descriptor representing a single TEXT column */ + tupdesc = CreateTemplateTupleDesc(1); + TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, varname, + TEXTOID, -1, 0); + + /* prepare for projection of tuples */ + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); + + /* Send it */ + do_text_output_oneline(tstate, value); + + end_tup_output(tstate); +} + +/* + * SHOW ALL command + */ +static void +ShowAllGUCConfig(DestReceiver *dest) +{ + int i; + TupOutputState *tstate; + TupleDesc tupdesc; + Datum values[3]; + bool isnull[3] = {false, false, false}; + struct config_generic **guc_variables = get_guc_variables(); + int num_guc_variables = GetNumConfigOptions(); + + /* need a tuple descriptor representing three TEXT columns */ + tupdesc = CreateTemplateTupleDesc(3); + TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 2, "setting", + TEXTOID, -1, 0); + TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 3, "description", + TEXTOID, -1, 0); + + /* prepare for projection of tuples */ + tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual); + + for (i = 0; i < num_guc_variables; i++) + { + struct config_generic *conf = guc_variables[i]; + char *setting; + + if ((conf->flags & GUC_NO_SHOW_ALL) || + ((conf->flags & GUC_SUPERUSER_ONLY) && + !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))) + continue; + + /* assign to the values array */ + values[0] = PointerGetDatum(cstring_to_text(conf->name)); + + setting = ShowGUCOption(conf, true); + if (setting) + { + values[1] = PointerGetDatum(cstring_to_text(setting)); + isnull[1] = false; + } + else + { + values[1] = PointerGetDatum(NULL); + isnull[1] = true; + } + + if (conf->short_desc) + { + values[2] = PointerGetDatum(cstring_to_text(conf->short_desc)); + isnull[2] = false; + } + else + { + values[2] = PointerGetDatum(NULL); + isnull[2] = true; + } + + /* send it to dest */ + do_tup_output(tstate, values, isnull); + + /* clean up */ + pfree(DatumGetPointer(values[0])); + if (setting) + { + pfree(setting); + pfree(DatumGetPointer(values[1])); + } + if (conf->short_desc) + pfree(DatumGetPointer(values[2])); + } + + end_tup_output(tstate); +} + +/* + * Return some of the flags associated to the specified GUC in the shape of + * a text array, and NULL if it does not exist. An empty array is returned + * if the GUC exists without any meaningful flags to show. + */ +Datum +pg_settings_get_flags(PG_FUNCTION_ARGS) +{ +#define MAX_GUC_FLAGS 5 + char *varname = TextDatumGetCString(PG_GETARG_DATUM(0)); + struct config_generic *record; + int cnt = 0; + Datum flags[MAX_GUC_FLAGS]; + ArrayType *a; + + record = find_option(varname, false, true, ERROR); + + /* return NULL if no such variable */ + if (record == NULL) + PG_RETURN_NULL(); + + if (record->flags & GUC_EXPLAIN) + flags[cnt++] = CStringGetTextDatum("EXPLAIN"); + if (record->flags & GUC_NO_RESET_ALL) + flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL"); + if (record->flags & GUC_NO_SHOW_ALL) + flags[cnt++] = CStringGetTextDatum("NO_SHOW_ALL"); + if (record->flags & GUC_NOT_IN_SAMPLE) + flags[cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE"); + if (record->flags & GUC_RUNTIME_COMPUTED) + flags[cnt++] = CStringGetTextDatum("RUNTIME_COMPUTED"); + + Assert(cnt <= MAX_GUC_FLAGS); + + /* Returns the record as Datum */ + a = construct_array_builtin(flags, cnt, TEXTOID); + PG_RETURN_ARRAYTYPE_P(a); +} + +/* + * Return GUC variable value by variable number; optionally return canonical + * form of name. Return value is palloc'd. + */ +static void +GetConfigOptionByNum(int varnum, const char **values, bool *noshow) +{ + char buffer[256]; + struct config_generic *conf; + struct config_generic **guc_variables = get_guc_variables(); + + /* check requested variable number valid */ + Assert((varnum >= 0) && (varnum < GetNumConfigOptions())); + + conf = guc_variables[varnum]; + + if (noshow) + { + if ((conf->flags & GUC_NO_SHOW_ALL) || + ((conf->flags & GUC_SUPERUSER_ONLY) && + !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))) + *noshow = true; + else + *noshow = false; + } + + /* first get the generic attributes */ + + /* name */ + values[0] = conf->name; + + /* setting: use ShowGUCOption in order to avoid duplicating the logic */ + values[1] = ShowGUCOption(conf, false); + + /* unit, if any (NULL is fine) */ + values[2] = get_config_unit_name(conf->flags); + + /* group */ + values[3] = _(config_group_names[conf->group]); + + /* short_desc */ + values[4] = conf->short_desc != NULL ? _(conf->short_desc) : NULL; + + /* extra_desc */ + values[5] = conf->long_desc != NULL ? _(conf->long_desc) : NULL; + + /* context */ + values[6] = GucContext_Names[conf->context]; + + /* vartype */ + values[7] = config_type_names[conf->vartype]; + + /* source */ + values[8] = GucSource_Names[conf->source]; + + /* now get the type specific attributes */ + switch (conf->vartype) + { + case PGC_BOOL: + { + struct config_bool *lconf = (struct config_bool *) conf; + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + values[12] = pstrdup(lconf->boot_val ? "on" : "off"); + + /* reset_val */ + values[13] = pstrdup(lconf->reset_val ? "on" : "off"); + } + break; + + case PGC_INT: + { + struct config_int *lconf = (struct config_int *) conf; + + /* min_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->min); + values[9] = pstrdup(buffer); + + /* max_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->max); + values[10] = pstrdup(buffer); + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val); + values[12] = pstrdup(buffer); + + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val); + values[13] = pstrdup(buffer); + } + break; + + case PGC_REAL: + { + struct config_real *lconf = (struct config_real *) conf; + + /* min_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->min); + values[9] = pstrdup(buffer); + + /* max_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->max); + values[10] = pstrdup(buffer); + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val); + values[12] = pstrdup(buffer); + + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val); + values[13] = pstrdup(buffer); + } + break; + + case PGC_STRING: + { + struct config_string *lconf = (struct config_string *) conf; + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + if (lconf->boot_val == NULL) + values[12] = NULL; + else + values[12] = pstrdup(lconf->boot_val); + + /* reset_val */ + if (lconf->reset_val == NULL) + values[13] = NULL; + else + values[13] = pstrdup(lconf->reset_val); + } + break; + + case PGC_ENUM: + { + struct config_enum *lconf = (struct config_enum *) conf; + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + + /* + * NOTE! enumvals with double quotes in them are not + * supported! + */ + values[11] = config_enum_get_options((struct config_enum *) conf, + "{\"", "\"}", "\",\""); + + /* boot_val */ + values[12] = pstrdup(config_enum_lookup_by_value(lconf, + lconf->boot_val)); + + /* reset_val */ + values[13] = pstrdup(config_enum_lookup_by_value(lconf, + lconf->reset_val)); + } + break; + + default: + { + /* + * should never get here, but in case we do, set 'em to NULL + */ + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + values[12] = NULL; + + /* reset_val */ + values[13] = NULL; + } + break; + } + + /* + * If the setting came from a config file, set the source location. For + * security reasons, we don't show source file/line number for + * insufficiently-privileged users. + */ + if (conf->source == PGC_S_FILE && + has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS)) + { + values[14] = conf->sourcefile; + snprintf(buffer, sizeof(buffer), "%d", conf->sourceline); + values[15] = pstrdup(buffer); + } + else + { + values[14] = NULL; + values[15] = NULL; + } + + values[16] = (conf->status & GUC_PENDING_RESTART) ? "t" : "f"; +} + +/* + * show_config_by_name - equiv to SHOW X command but implemented as + * a function. + */ +Datum +show_config_by_name(PG_FUNCTION_ARGS) +{ + char *varname = TextDatumGetCString(PG_GETARG_DATUM(0)); + char *varval; + + /* Get the value */ + varval = GetConfigOptionByName(varname, NULL, false); + + /* Convert to text */ + PG_RETURN_TEXT_P(cstring_to_text(varval)); +} + +/* + * show_config_by_name_missing_ok - equiv to SHOW X command but implemented as + * a function. If X does not exist, suppress the error and just return NULL + * if missing_ok is true. + */ +Datum +show_config_by_name_missing_ok(PG_FUNCTION_ARGS) +{ + char *varname = TextDatumGetCString(PG_GETARG_DATUM(0)); + bool missing_ok = PG_GETARG_BOOL(1); + char *varval; + + /* Get the value */ + varval = GetConfigOptionByName(varname, NULL, missing_ok); + + /* return NULL if no such variable */ + if (varval == NULL) + PG_RETURN_NULL(); + + /* Convert to text */ + PG_RETURN_TEXT_P(cstring_to_text(varval)); +} + +/* + * show_all_settings - equiv to SHOW ALL command but implemented as + * a Table Function. + */ +#define NUM_PG_SETTINGS_ATTS 17 + +Datum +show_all_settings(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + TupleDesc tupdesc; + int call_cntr; + int max_calls; + AttInMetadata *attinmeta; + MemoryContext oldcontext; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* + * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns + * of the appropriate types + */ + tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "unit", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "category", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "short_desc", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "extra_desc", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "context", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "vartype", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 9, "source", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 10, "min_val", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals", + TEXTARRAYOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 15, "sourcefile", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 17, "pending_restart", + BOOLOID, -1, 0); + + /* + * Generate attribute metadata needed later to produce tuples from raw + * C strings + */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + /* total number of tuples to be returned */ + funcctx->max_calls = GetNumConfigOptions(); + + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + attinmeta = funcctx->attinmeta; + + if (call_cntr < max_calls) /* do when there is more left to send */ + { + char *values[NUM_PG_SETTINGS_ATTS]; + bool noshow; + HeapTuple tuple; + Datum result; + + /* + * Get the next visible GUC variable name and value + */ + do + { + GetConfigOptionByNum(call_cntr, (const char **) values, &noshow); + if (noshow) + { + /* bump the counter and get the next config setting */ + call_cntr = ++funcctx->call_cntr; + + /* make sure we haven't gone too far now */ + if (call_cntr >= max_calls) + SRF_RETURN_DONE(funcctx); + } + } while (noshow); + + /* build a tuple */ + tuple = BuildTupleFromCStrings(attinmeta, values); + + /* make the tuple into a datum */ + result = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, result); + } + else + { + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); + } +} + +/* + * show_all_file_settings + * + * Returns a table of all parameter settings in all configuration files + * which includes the config file pathname, the line number, a sequence number + * indicating the order in which the settings were encountered, the parameter + * name and value, a bool showing if the value could be applied, and possibly + * an associated error message. (For problems such as syntax errors, the + * parameter name/value might be NULL.) + * + * Note: no filtering is done here, instead we depend on the GRANT system + * to prevent unprivileged users from accessing this function or the view + * built on top of it. + */ +Datum +show_all_file_settings(PG_FUNCTION_ARGS) +{ +#define NUM_PG_FILE_SETTINGS_ATTS 7 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + ConfigVariable *conf; + int seqno; + + /* Scan the config files using current context as workspace */ + conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3); + + /* Build a tuplestore to return our results in */ + SetSingleFuncCall(fcinfo, 0); + + /* Process the results and create a tuplestore */ + for (seqno = 1; conf != NULL; conf = conf->next, seqno++) + { + Datum values[NUM_PG_FILE_SETTINGS_ATTS]; + bool nulls[NUM_PG_FILE_SETTINGS_ATTS]; + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + /* sourcefile */ + if (conf->filename) + values[0] = PointerGetDatum(cstring_to_text(conf->filename)); + else + nulls[0] = true; + + /* sourceline (not meaningful if no sourcefile) */ + if (conf->filename) + values[1] = Int32GetDatum(conf->sourceline); + else + nulls[1] = true; + + /* seqno */ + values[2] = Int32GetDatum(seqno); + + /* name */ + if (conf->name) + values[3] = PointerGetDatum(cstring_to_text(conf->name)); + else + nulls[3] = true; + + /* setting */ + if (conf->value) + values[4] = PointerGetDatum(cstring_to_text(conf->value)); + else + nulls[4] = true; + + /* applied */ + values[5] = BoolGetDatum(conf->applied); + + /* error */ + if (conf->errmsg) + values[6] = PointerGetDatum(cstring_to_text(conf->errmsg)); + else + nulls[6] = true; + + /* shove row into tuplestore */ + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + return (Datum) 0; +} |