diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 2c90df205a..72acab0f1e 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994-5, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.81 2002/07/20 05:16:57 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.82 2002/07/20 05:49:27 momjian Exp $ * */ @@ -15,6 +15,7 @@ #include "access/heapam.h" #include "catalog/pg_type.h" #include "commands/explain.h" +#include "executor/executor.h" #include "executor/instrument.h" #include "lib/stringinfo.h" #include "nodes/print.h" @@ -38,15 +39,9 @@ typedef struct ExplainState List *rtable; /* range table */ } ExplainState; -typedef struct TextOutputState -{ - TupleDesc tupdesc; - DestReceiver *destfunc; -} TextOutputState; - static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es); static void ExplainOneQuery(Query *query, ExplainStmt *stmt, - TextOutputState *tstate); + TupOutputState *tstate); static void explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan, int indent, ExplainState *es); static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel, @@ -59,11 +54,6 @@ static void show_upper_qual(List *qual, const char *qlabel, static void show_sort_keys(List *tlist, int nkeys, const char *qlabel, StringInfo str, int indent, ExplainState *es); static Node *make_ors_ands_explicit(List *orclauses); -static TextOutputState *begin_text_output(CommandDest dest, char *title); -static void do_text_output(TextOutputState *tstate, char *aline); -static void do_text_output_multiline(TextOutputState *tstate, char *text); -static void end_text_output(TextOutputState *tstate); - /* * ExplainQuery - @@ -73,16 +63,23 @@ void ExplainQuery(ExplainStmt *stmt, CommandDest dest) { Query *query = stmt->query; - TextOutputState *tstate; + TupOutputState *tstate; + TupleDesc tupdesc; List *rewritten; List *l; - tstate = begin_text_output(dest, "QUERY PLAN"); + /* need a tuple descriptor representing a single TEXT column */ + tupdesc = CreateTemplateTupleDesc(1); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN", + TEXTOID, -1, 0, false); + + /* prepare for projection of tuples */ + tstate = begin_tup_output_tupdesc(dest, tupdesc); if (query->commandType == CMD_UTILITY) { /* rewriter will not cope with utility statements */ - do_text_output(tstate, "Utility statements have no plan structure"); + PROJECT_LINE_OF_TEXT("Utility statements have no plan structure"); } else { @@ -92,7 +89,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) if (rewritten == NIL) { /* In the case of an INSTEAD NOTHING, tell at least that */ - do_text_output(tstate, "Query rewrites to nothing"); + PROJECT_LINE_OF_TEXT("Query rewrites to nothing"); } else { @@ -102,12 +99,12 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) ExplainOneQuery(lfirst(l), stmt, tstate); /* put a blank line between plans */ if (lnext(l) != NIL) - do_text_output(tstate, ""); + PROJECT_LINE_OF_TEXT(""); } } } - end_text_output(tstate); + end_tup_output(tstate); } /* @@ -115,7 +112,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest) * print out the execution plan for one query */ static void -ExplainOneQuery(Query *query, ExplainStmt *stmt, TextOutputState *tstate) +ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) { Plan *plan; ExplainState *es; @@ -125,9 +122,9 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TextOutputState *tstate) if (query->commandType == CMD_UTILITY) { if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) - do_text_output(tstate, "NOTIFY"); + PROJECT_LINE_OF_TEXT("NOTIFY"); else - do_text_output(tstate, "UTILITY"); + PROJECT_LINE_OF_TEXT("UTILITY"); return; } @@ -192,7 +189,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TextOutputState *tstate) do_text_output_multiline(tstate, f); pfree(f); if (es->printCost) - do_text_output(tstate, ""); /* separator line */ + PROJECT_LINE_OF_TEXT(""); /* separator line */ } } @@ -837,78 +834,3 @@ make_ors_ands_explicit(List *orclauses) return (Node *) make_orclause(args); } } - - -/* - * Functions for sending text to the frontend (or other specified destination) - * as though it is a SELECT result. - * - * We tell the frontend that the table structure is a single TEXT column. - */ - -static TextOutputState * -begin_text_output(CommandDest dest, char *title) -{ - TextOutputState *tstate; - TupleDesc tupdesc; - - tstate = (TextOutputState *) palloc(sizeof(TextOutputState)); - - /* need a tuple descriptor representing a single TEXT column */ - tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, title, - TEXTOID, -1, 0, false); - - tstate->tupdesc = tupdesc; - tstate->destfunc = DestToFunction(dest); - - (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT, - NULL, tupdesc); - - return tstate; -} - -/* write a single line of text */ -static void -do_text_output(TextOutputState *tstate, char *aline) -{ - HeapTuple tuple; - Datum values[1]; - char nulls[1]; - - /* form a tuple and send it to the receiver */ - values[0] = DirectFunctionCall1(textin, CStringGetDatum(aline)); - nulls[0] = ' '; - tuple = heap_formtuple(tstate->tupdesc, values, nulls); - (*tstate->destfunc->receiveTuple) (tuple, - tstate->tupdesc, - tstate->destfunc); - pfree(DatumGetPointer(values[0])); - heap_freetuple(tuple); -} - -/* write a chunk of text, breaking at newline characters */ -/* NB: scribbles on its input! */ -static void -do_text_output_multiline(TextOutputState *tstate, char *text) -{ - while (*text) - { - char *eol; - - eol = strchr(text, '\n'); - if (eol) - *eol++ = '\0'; - else - eol = text + strlen(text); - do_text_output(tstate, text); - text = eol; - } -} - -static void -end_text_output(TextOutputState *tstate) -{ - (*tstate->destfunc->cleanup) (tstate->destfunc); - pfree(tstate); -} diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 5124bd023f..06a784ce26 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.55 2002/07/20 05:16:57 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.56 2002/07/20 05:49:27 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -791,3 +791,73 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) return tuple; } +/* + * Functions for sending tuples to the frontend (or other specified destination) + * as though it is a SELECT result. These are used by utility commands that + * need to project directly to the destination and don't need or want full + * Table Function capability. Currently used by EXPLAIN and SHOW ALL + */ +TupOutputState * +begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc) +{ + TupOutputState *tstate; + + tstate = (TupOutputState *) palloc(sizeof(TupOutputState)); + + tstate->tupdesc = tupdesc; + tstate->destfunc = DestToFunction(dest); + + (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT, + NULL, tupdesc); + + return tstate; +} + +/* + * write a single tuple + * + * values is a list of the external C string representations of the values + * to be projected. + */ +void +do_tup_output(TupOutputState *tstate, char **values) +{ + /* build a tuple from the input strings using the tupdesc */ + AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tstate->tupdesc); + HeapTuple tuple = BuildTupleFromCStrings(attinmeta, values); + + /* send the tuple to the receiver */ + (*tstate->destfunc->receiveTuple) (tuple, + tstate->tupdesc, + tstate->destfunc); + /* clean up */ + heap_freetuple(tuple); +} + +/* write a chunk of text, breaking at newline characters + * NB: scribbles on its input! + * Should only be used for a single TEXT attribute tupdesc. + */ +void +do_text_output_multiline(TupOutputState *tstate, char *text) +{ + while (*text) + { + char *eol; + + eol = strchr(text, '\n'); + if (eol) + *eol++ = '\0'; + else + eol = text + strlen(text); + do_tup_output(tstate, &text); + text = eol; + } +} + +void +end_tup_output(TupOutputState *tstate) +{ + (*tstate->destfunc->cleanup) (tstate->destfunc); + pfree(tstate); +} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 482423f805..d63d603b1d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5,7 +5,7 @@ * command, configuration file, and command line options. * See src/backend/utils/misc/README for more information. * - * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.72 2002/07/18 02:02:30 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.73 2002/07/20 05:49:27 momjian Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut . @@ -23,8 +23,10 @@ #include "access/xlog.h" #include "catalog/namespace.h" +#include "catalog/pg_type.h" #include "commands/async.h" #include "commands/variable.h" +#include "executor/executor.h" #include "fmgr.h" #include "libpq/auth.h" #include "libpq/pqcomm.h" @@ -826,7 +828,7 @@ static char *guc_string_workspace; /* for avoiding memory leaks */ static int guc_var_compare(const void *a, const void *b); -static void _ShowOption(struct config_generic *record); +static char *_ShowOption(struct config_generic *record); /* @@ -2167,6 +2169,57 @@ SetPGVariable(const char *name, List *args, bool is_local) true); } +/* + * 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; + text *result_text; + + if (PG_ARGISNULL(0)) + elog(ERROR, "SET variable name is required"); + + /* Get the GUC variable name */ + name = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0)))); + + /* Get the desired value or set to NULL for a reset request */ + if (PG_ARGISNULL(1)) + value = NULL; + else + value = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(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 */ + set_config_option(name, + value, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + is_local, + true); + + /* get the new current value */ + new_value = GetConfigOptionByName(name); + + /* Convert return string to text */ + result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(new_value))); + + /* return it */ + PG_RETURN_TEXT_P(result_text); +} + /* * SHOW command */ @@ -2203,13 +2256,26 @@ ResetPGVariable(const char *name) void ShowGUCConfigOption(const char *name) { - struct config_generic *record; + TupOutputState *tstate; + TupleDesc tupdesc; + CommandDest dest = whereToSendOutput; + char *value; - record = find_option(name); - if (record == NULL) - elog(ERROR, "Option '%s' is not recognized", name); + /* need a tuple descriptor representing a single TEXT column */ + tupdesc = CreateTemplateTupleDesc(1); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, (char *) name, + TEXTOID, -1, 0, false); - _ShowOption(record); + /* prepare for projection of tuples */ + tstate = begin_tup_output_tupdesc(dest, tupdesc); + + /* Get the value */ + value = GetConfigOptionByName(name); + + /* Send it */ + PROJECT_LINE_OF_TEXT(value); + + end_tup_output(tstate); } /* @@ -2219,17 +2285,115 @@ void ShowAllGUCConfig(void) { int i; + TupOutputState *tstate; + TupleDesc tupdesc; + CommandDest dest = whereToSendOutput; + char *name; + char *value; + char *values[2]; + + /* need a tuple descriptor representing two TEXT columns */ + tupdesc = CreateTemplateTupleDesc(2); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", + TEXTOID, -1, 0, false); + + /* prepare for projection of tuples */ + tstate = begin_tup_output_tupdesc(dest, tupdesc); for (i = 0; i < num_guc_variables; i++) { - struct config_generic *conf = guc_variables[i]; + /* Get the next GUC variable name and value */ + value = GetConfigOptionByNum(i, &name); - if ((conf->flags & GUC_NO_SHOW_ALL) == 0) - _ShowOption(conf); + /* assign to the values array */ + values[0] = name; + values[1] = value; + + /* send it to dest */ + do_tup_output(tstate, values); + + /* + * clean up + */ + /* we always should have a name */ + pfree(name); + + /* but value can be returned to us as a NULL */ + if (value != NULL) + pfree(value); } + + end_tup_output(tstate); } -static void +/* + * Return GUC variable value by name + */ +char * +GetConfigOptionByName(const char *name) +{ + struct config_generic *record; + + record = find_option(name); + if (record == NULL) + elog(ERROR, "Option '%s' is not recognized", name); + + return _ShowOption(record); +} + +/* + * Return GUC variable value and set varname for a specific + * variable by number. + */ +char * +GetConfigOptionByNum(int varnum, char **varname) +{ + struct config_generic *conf = guc_variables[varnum]; + + *varname = pstrdup(conf->name); + + if ((conf->flags & GUC_NO_SHOW_ALL) == 0) + return _ShowOption(conf); + else + return NULL; +} + +/* + * Return the total number of GUC variables + */ +int +GetNumConfigOptions(void) +{ + return num_guc_variables; +} + +/* + * show_config_by_name - equiv to SHOW X command but implemented as + * a function. + */ +Datum +show_config_by_name(PG_FUNCTION_ARGS) +{ + char *varname; + char *varval; + text *result_text; + + /* Get the GUC variable name */ + varname = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0)))); + + /* Get the value */ + varval = GetConfigOptionByName(varname); + + /* Convert to text */ + result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(varval))); + + /* return it */ + PG_RETURN_TEXT_P(result_text); +} + +static char * _ShowOption(struct config_generic *record) { char buffer[256]; @@ -2297,7 +2461,7 @@ _ShowOption(struct config_generic *record) break; } - elog(INFO, "%s is %s", record->name, val); + return pstrdup(val); } diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c84ac13a95..4966781945 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.244 2002/07/18 23:11:30 petere Exp $ + * $Id: pg_proc.h,v 1.245 2002/07/20 05:49:27 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2881,6 +2881,11 @@ DESCR("substitutes regular expression"); DATA(insert OID = 2074 ( substring PGNSP PGUID 14 f f f t f i 3 25 "25 25 25" 100 0 0 100 "select substring($1, like_escape($2, $3))" - _null_ )); DESCR("substitutes regular expression with escape argument"); +DATA(insert OID = 2090 ( current_setting PGNSP PGUID 12 f f f t f s 1 25 "25" 100 0 0 100 show_config_by_name - _null_ )); +DESCR("SHOW X as a function"); +DATA(insert OID = 2091 ( set_config PGNSP PGUID 12 f f f f f v 3 25 "25 25 16" 100 0 0 100 set_config_by_name - _null_ )); +DESCR("SET X as a function"); + /* Aggregates (moved here from pg_aggregate for 7.3) */ DATA(insert OID = 2100 ( avg PGNSP PGUID 12 t f f f f i 1 1700 "20" 100 0 0 100 aggregate_dummy - _null_ )); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 427c146a57..5b121546d9 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.70 2002/07/20 05:16:59 momjian Exp $ + * $Id: executor.h,v 1.71 2002/07/20 05:49:28 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -121,6 +121,25 @@ extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, extern TupleDesc ExecTypeFromTL(List *targetList, hasoid_t withoid); extern void SetChangedParamList(Plan *node, List *newchg); +typedef struct TupOutputState +{ + TupleDesc tupdesc; + DestReceiver *destfunc; +} TupOutputState; + +extern TupOutputState *begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc); +extern void do_tup_output(TupOutputState *tstate, char **values); +extern void do_text_output_multiline(TupOutputState *tstate, char *text); +extern void end_tup_output(TupOutputState *tstate); + +#define PROJECT_LINE_OF_TEXT(text_to_project) \ + do { \ + char *values[1]; \ + values[0] = text_to_project; \ + do_tup_output(tstate, values); \ + } while (0) + + /* * prototypes from functions in execUtils.c */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index d79b1c6dcb..02a537ca4e 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.186 2002/06/20 20:29:52 momjian Exp $ + * $Id: builtins.h,v 1.187 2002/07/20 05:49:28 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -633,4 +633,8 @@ extern int32 type_maximum_size(Oid type_oid, int32 typemod); extern Datum quote_ident(PG_FUNCTION_ARGS); extern Datum quote_literal(PG_FUNCTION_ARGS); +/* guc.c */ +extern Datum show_config_by_name(PG_FUNCTION_ARGS); +extern Datum set_config_by_name(PG_FUNCTION_ARGS); + #endif /* BUILTINS_H */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index ce1b10be83..35c56eba76 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -4,7 +4,7 @@ * External declarations pertaining to backend/utils/misc/guc.c and * backend/utils/misc/guc-file.l * - * $Id: guc.h,v 1.17 2002/05/17 01:19:19 tgl Exp $ + * $Id: guc.h,v 1.18 2002/07/20 05:49:28 momjian Exp $ */ #ifndef GUC_H #define GUC_H @@ -86,6 +86,9 @@ extern bool set_config_option(const char *name, const char *value, bool isLocal, bool DoIt); extern void ShowGUCConfigOption(const char *name); extern void ShowAllGUCConfig(void); +extern char *GetConfigOptionByName(const char *name); +extern char *GetConfigOptionByNum(int varnum, char **varname); +extern int GetNumConfigOptions(void); extern void SetPGVariable(const char *name, List *args, bool is_local); extern void GetPGVariable(const char *name);