From 45e25445846e98fe4aac23d1073566c08cd62f0b Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 15 Aug 2002 02:51:27 +0000 Subject: [PATCH] As discussed on several occasions previously, the new anonymous composite type capability makes it possible to create a system view based on a table function in a way that is hopefully palatable to everyone. The attached patch takes advantage of this, moving show_all_settings() from contrib/tablefunc into the backend (renamed all_settings(). It is defined as a builtin returning type RECORD. During initdb a system view is created to expose the same information presently available through SHOW ALL. For example: test=# select * from pg_settings where name like '%debug%'; name | setting -----------------------+--------- debug_assertions | on debug_pretty_print | off debug_print_parse | off debug_print_plan | off debug_print_query | off debug_print_rewritten | off wal_debug | 0 (7 rows) Additionally during initdb two rules are created which make it possible to change settings by updating the system view -- a "virtual table" as Tom put it. Here's an example: Joe Conway --- contrib/tablefunc/README.tablefunc | 139 +++++++++---- contrib/tablefunc/tablefunc-test.sql | 4 +- contrib/tablefunc/tablefunc.c | 291 ++++++++++++--------------- contrib/tablefunc/tablefunc.h | 1 - contrib/tablefunc/tablefunc.sql.in | 12 +- src/backend/utils/misc/guc.c | 114 ++++++++++- src/bin/initdb/initdb.sh | 17 +- src/include/catalog/pg_proc.h | 4 +- src/include/utils/builtins.h | 3 +- src/test/regress/expected/rules.out | 7 +- 10 files changed, 372 insertions(+), 220 deletions(-) diff --git a/contrib/tablefunc/README.tablefunc b/contrib/tablefunc/README.tablefunc index b3b663aafa..310778ca36 100644 --- a/contrib/tablefunc/README.tablefunc +++ b/contrib/tablefunc/README.tablefunc @@ -46,9 +46,6 @@ Installation: installs following functions into database template1: - show_all_settings() - - returns the same information as SHOW ALL, but as a query result - normal_rand(int numvals, float8 mean, float8 stddev, int seed) - returns a set of normally distributed float8 values @@ -58,48 +55,15 @@ Installation: but you can create additional crosstab functions per the instructions in the documentation below. + crosstab(text sql, N int) + - returns a set of row_name plus N category value columns + - requires anonymous composite type syntax in the FROM clause. See + the instructions in the documentation below. + Documentation ================================================================== Name -show_all_settings() - returns the same information as SHOW ALL, - but as a query result. - -Synopsis - -show_all_settings() - -Inputs - - none - -Outputs - - Returns setof tablefunc_config_settings which is defined by: - CREATE VIEW tablefunc_config_settings AS - SELECT - ''::TEXT AS name, - ''::TEXT AS setting; - -Example usage - - test=# select * from show_all_settings(); - name | setting --------------------------------+--------------------------------------- - australian_timezones | off - authentication_timeout | 60 - checkpoint_segments | 3 - . - . - . - wal_debug | 0 - wal_files | 0 - wal_sync_method | fdatasync -(94 rows) - -================================================================== -Name - normal_rand(int, float8, float8, int) - returns a set of normally distributed float8 values @@ -267,6 +231,99 @@ select * from crosstab3( test2 | val6 | val7 | (2 rows) +================================================================== +Name + +crosstab(text, int) - returns a set of row_name + plus N category value columns + +Synopsis + +crosstab(text sql, int N) + +Inputs + + sql + + A SQL statement which produces the source set of data. The SQL statement + must return one row_name column, one category column, and one value + column. + + e.g. provided sql must produce a set something like: + + row_name cat value + ----------+-------+------- + row1 cat1 val1 + row1 cat2 val2 + row1 cat3 val3 + row1 cat4 val4 + row2 cat1 val5 + row2 cat2 val6 + row2 cat3 val7 + row2 cat4 val8 + + N + + number of category value columns + +Outputs + + Returns setof record, which must defined with a column definition + in the FROM clause of the SELECT statement, e.g.: + + SELECT * + FROM crosstab(sql, 2) AS ct(row_name text, category_1 text, category_2 text); + + the example crosstab function produces a set something like: + <== values columns ==> + row_name category_1 category_2 + ---------+------------+------------ + row1 val1 val2 + row2 val5 val6 + +Notes + + 1. The sql result must be ordered by 1,2. + + 2. The number of values columns is determined at run-time. The + column definition provided in the FROM clause must provide for + N + 1 columns of the proper data types. + + 3. Missing values (i.e. not enough adjacent rows of same row_name to + fill the number of result values columns) are filled in with nulls. + + 4. Extra values (i.e. too many adjacent rows of same row_name to fill + the number of result values columns) are skipped. + + 5. Rows with all nulls in the values columns are skipped. + + +Example usage + +create table ct(id serial, rowclass text, rowid text, attribute text, value text); +insert into ct(rowclass, rowid, attribute, value) values('group1','test1','att1','val1'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test1','att2','val2'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test1','att3','val3'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test1','att4','val4'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test2','att1','val5'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test2','att2','val6'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test2','att3','val7'); +insert into ct(rowclass, rowid, attribute, value) values('group1','test2','att4','val8'); + +SELECT * +FROM crosstab( + 'select rowid, attribute, value + from ct + where rowclass = ''group1'' + and (attribute = ''att2'' or attribute = ''att3'') order by 1,2;', 3) +AS ct(row_name text, category_1 text, category_2 text, category_3 text); + + row_name | category_1 | category_2 | category_3 +----------+------------+------------+------------ + test1 | val2 | val3 | + test2 | val6 | val7 | +(2 rows) + ================================================================== -- Joe Conway diff --git a/contrib/tablefunc/tablefunc-test.sql b/contrib/tablefunc/tablefunc-test.sql index 141894b0f4..e1e0a7c89e 100644 --- a/contrib/tablefunc/tablefunc-test.sql +++ b/contrib/tablefunc/tablefunc-test.sql @@ -44,4 +44,6 @@ select * from crosstab2('select rowid, attribute, value from ct where rowclass = select * from crosstab3('select rowid, attribute, value from ct where rowclass = ''group2'' order by 1,2;'); select * from crosstab4('select rowid, attribute, value from ct where rowclass = ''group2'' order by 1,2;'); - +select * from crosstab('select rowid, attribute, value from ct where rowclass = ''group1'' order by 1,2;', 2) as c(rowid text, att1 text, att2 text); +select * from crosstab('select rowid, attribute, value from ct where rowclass = ''group1'' order by 1,2;', 3) as c(rowid text, att1 text, att2 text, att3 text); +select * from crosstab('select rowid, attribute, value from ct where rowclass = ''group1'' order by 1,2;', 4) as c(rowid text, att1 text, att2 text, att3 text, att4 text); diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c index 236d833e46..d05fc1a76b 100644 --- a/contrib/tablefunc/tablefunc.c +++ b/contrib/tablefunc/tablefunc.c @@ -35,11 +35,13 @@ #include "executor/spi.h" #include "utils/builtins.h" #include "utils/guc.h" +#include "utils/lsyscache.h" #include "tablefunc.h" static bool compatTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2); static void get_normal_pair(float8 *x1, float8 *x2); +static TupleDesc make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_catagories); typedef struct { @@ -66,118 +68,6 @@ typedef struct } \ } while (0) -/* - * show_all_settings - equiv to SHOW ALL command but implemented as - * a Table Function. - */ -PG_FUNCTION_INFO_V1(show_all_settings); -Datum -show_all_settings(PG_FUNCTION_ARGS) -{ - FuncCallContext *funcctx; - TupleDesc tupdesc; - int call_cntr; - int max_calls; - TupleTableSlot *slot; - AttInMetadata *attinmeta; - - /* stuff done only on the first call of the function */ - if(SRF_IS_FIRSTCALL()) - { - Oid foid = fcinfo->flinfo->fn_oid; - Oid functypeid; - - /* create a function context for cross-call persistence */ - funcctx = SRF_FIRSTCALL_INIT(); - - /* get the typeid that represents our return type */ - functypeid = foidGetTypeId(foid); - - /* Build a tuple description for a funcrelid tuple */ - tupdesc = TypeGetTupleDesc(functypeid, NIL); - - /* allocate a slot for a tuple with this tupdesc */ - slot = TupleDescGetSlot(tupdesc); - - /* assign slot to function context */ - funcctx->slot = slot; - - /* - * 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(); - } - - /* stuff done on every call of the function */ - funcctx = SRF_PERCALL_SETUP(); - - call_cntr = funcctx->call_cntr; - max_calls = funcctx->max_calls; - slot = funcctx->slot; - attinmeta = funcctx->attinmeta; - - if (call_cntr < max_calls) /* do when there is more left to send */ - { - char **values; - char *varname; - char *varval; - bool noshow; - HeapTuple tuple; - Datum result; - - /* - * Get the next visible GUC variable name and value - */ - do - { - varval = GetConfigOptionByNum(call_cntr, (const char **) &varname, &noshow); - if (noshow) - { - /* varval is a palloc'd copy, so free it */ - xpfree(varval); - - /* 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); - - /* - * Prepare a values array for storage in our slot. - * This should be an array of C strings which will - * be processed later by the appropriate "in" functions. - */ - values = (char **) palloc(2 * sizeof(char *)); - values[0] = pstrdup(varname); - values[1] = varval; /* varval is already a palloc'd copy */ - - /* build a tuple */ - tuple = BuildTupleFromCStrings(attinmeta, values); - - /* make the tuple into a datum */ - result = TupleGetDatum(slot, tuple); - - /* Clean up */ - xpfree(values[0]); - xpfree(values[1]); - xpfree(values); - - SRF_RETURN_NEXT(funcctx, result); - } - else /* do when there is no more left */ - { - SRF_RETURN_DONE(funcctx); - } -} - /* * normal_rand - return requested number of random values * with a Gaussian (Normal) distribution. @@ -368,7 +258,7 @@ crosstab(PG_FUNCTION_ARGS) int max_calls; TupleTableSlot *slot; AttInMetadata *attinmeta; - SPITupleTable *spi_tuptable; + SPITupleTable *spi_tuptable = NULL; TupleDesc spi_tupdesc; char *lastrowid; crosstab_fctx *fctx; @@ -378,34 +268,20 @@ crosstab(PG_FUNCTION_ARGS) /* stuff done only on the first call of the function */ if(SRF_IS_FIRSTCALL()) { - char *sql = GET_STR(PG_GETARG_TEXT_P(0)); - Oid foid = fcinfo->flinfo->fn_oid; - Oid functypeid; - TupleDesc tupdesc; - int ret; - int proc; + char *sql = GET_STR(PG_GETARG_TEXT_P(0)); + Oid funcid = fcinfo->flinfo->fn_oid; + Oid functypeid; + char functyptype; + TupleDesc tupdesc = NULL; + int ret; + int proc; + MemoryContext oldcontext; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); - /* get the typeid that represents our return type */ - functypeid = foidGetTypeId(foid); - - /* Build a tuple description for a funcrelid tuple */ - tupdesc = TypeGetTupleDesc(functypeid, NIL); - - /* allocate a slot for a tuple with this tupdesc */ - slot = TupleDescGetSlot(tupdesc); - - /* assign slot to function context */ - funcctx->slot = slot; - - /* - * Generate attribute metadata needed later to produce tuples from raw - * C strings - */ - attinmeta = TupleDescGetAttInMetadata(tupdesc); - funcctx->attinmeta = attinmeta; + /* SPI switches context on us, so save it first */ + oldcontext = CurrentMemoryContext; /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) @@ -424,7 +300,7 @@ crosstab(PG_FUNCTION_ARGS) /* * The provided SQL query must always return three columns. * - * 1. rowid the label or identifier for each row in the final + * 1. rowname the label or identifier for each row in the final * result * 2. category the label or identifier for each column in the * final result @@ -433,35 +309,78 @@ crosstab(PG_FUNCTION_ARGS) if (spi_tupdesc->natts != 3) elog(ERROR, "crosstab: provided SQL must return 3 columns;" " a rowid, a category, and a values column"); - - /* - * Check that return tupdesc is compatible with the one we got - * from ret_relname, at least based on number and type of - * attributes - */ - if (!compatTupleDescs(tupdesc, spi_tupdesc)) - elog(ERROR, "crosstab: return and sql tuple descriptions are" - " incompatible"); - - /* allocate memory for user context */ - fctx = (crosstab_fctx *) palloc(sizeof(crosstab_fctx)); - - /* - * OK, we have data, and it seems to be valid, so save it - * for use across calls - */ - fctx->spi_tuptable = spi_tuptable; - fctx->lastrowid = NULL; - funcctx->user_fctx = fctx; - - /* total number of tuples to be returned */ - funcctx->max_calls = proc; } else { /* no qualifying tuples */ - funcctx->max_calls = 0; + SPI_finish(); + SRF_RETURN_DONE(funcctx); } + + /* back to the original memory context */ + MemoryContextSwitchTo(oldcontext); + + /* get the typeid that represents our return type */ + functypeid = get_func_rettype(funcid); + + /* check typtype to see if we have a predetermined return type */ + functyptype = get_typtype(functypeid); + + if (functyptype == 'c') + { + /* Build a tuple description for a functypeid tuple */ + tupdesc = TypeGetTupleDesc(functypeid, NIL); + } + else if (functyptype == 'p' && functypeid == RECORDOID) + { + if (fcinfo->nargs != 2) + elog(ERROR, "Wrong number of arguments specified for function"); + else + { + int num_catagories = PG_GETARG_INT32(1); + + tupdesc = make_crosstab_tupledesc(spi_tupdesc, num_catagories); + } + } + else if (functyptype == 'b') + elog(ERROR, "Invalid kind of return type specified for function"); + else + elog(ERROR, "Unknown kind of return type specified for function"); + + /* + * Check that return tupdesc is compatible with the one we got + * from ret_relname, at least based on number and type of + * attributes + */ + if (!compatTupleDescs(tupdesc, spi_tupdesc)) + elog(ERROR, "crosstab: return and sql tuple descriptions are" + " incompatible"); + + /* allocate a slot for a tuple with this tupdesc */ + slot = TupleDescGetSlot(tupdesc); + + /* assign slot to function context */ + funcctx->slot = slot; + + /* + * Generate attribute metadata needed later to produce tuples from raw + * C strings + */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + /* allocate memory for user context */ + fctx = (crosstab_fctx *) palloc(sizeof(crosstab_fctx)); + + /* + * Save spi data for use across calls + */ + fctx->spi_tuptable = spi_tuptable; + fctx->lastrowid = NULL; + funcctx->user_fctx = fctx; + + /* total number of tuples to be returned */ + funcctx->max_calls = proc; } /* stuff done on every call of the function */ @@ -662,3 +581,51 @@ compatTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc) /* OK, the two tupdescs are compatible for our purposes */ return true; } + +static TupleDesc +make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_catagories) +{ + Form_pg_attribute sql_attr; + Oid sql_atttypid; + TupleDesc tupdesc; + int natts; + AttrNumber attnum; + char attname[NAMEDATALEN]; + int i; + + /* + * We need to build a tuple description with one column + * for the rowname, and num_catagories columns for the values. + * Each must be of the same type as the corresponding + * spi result input column. + */ + natts = num_catagories + 1; + tupdesc = CreateTemplateTupleDesc(natts, WITHOUTOID); + + /* first the rowname column */ + attnum = 1; + + sql_attr = spi_tupdesc->attrs[0]; + sql_atttypid = sql_attr->atttypid; + + strcpy(attname, "rowname"); + + TupleDescInitEntry(tupdesc, attnum, attname, sql_atttypid, + -1, 0, false); + + /* now the catagory values columns */ + sql_attr = spi_tupdesc->attrs[2]; + sql_atttypid = sql_attr->atttypid; + + for (i = 0; i < num_catagories; i++) + { + attnum++; + + sprintf(attname, "category_%d", i + 1); + TupleDescInitEntry(tupdesc, attnum, attname, sql_atttypid, + -1, 0, false); + } + + return tupdesc; +} + diff --git a/contrib/tablefunc/tablefunc.h b/contrib/tablefunc/tablefunc.h index 309894ac66..44cfd11fcc 100644 --- a/contrib/tablefunc/tablefunc.h +++ b/contrib/tablefunc/tablefunc.h @@ -32,7 +32,6 @@ /* * External declarations */ -extern Datum show_all_settings(PG_FUNCTION_ARGS); extern Datum normal_rand(PG_FUNCTION_ARGS); extern Datum crosstab(PG_FUNCTION_ARGS); diff --git a/contrib/tablefunc/tablefunc.sql.in b/contrib/tablefunc/tablefunc.sql.in index 746e8f9cff..7d599d4f08 100644 --- a/contrib/tablefunc/tablefunc.sql.in +++ b/contrib/tablefunc/tablefunc.sql.in @@ -1,12 +1,3 @@ -CREATE VIEW tablefunc_config_settings AS - SELECT - ''::TEXT AS name, - ''::TEXT AS setting; - -CREATE OR REPLACE FUNCTION show_all_settings() - RETURNS setof tablefunc_config_settings - AS 'MODULE_PATHNAME','show_all_settings' LANGUAGE 'c' STABLE STRICT; - CREATE OR REPLACE FUNCTION normal_rand(int4, float8, float8, int4) RETURNS setof float8 AS 'MODULE_PATHNAME','normal_rand' LANGUAGE 'c' VOLATILE STRICT; @@ -44,3 +35,6 @@ CREATE OR REPLACE FUNCTION crosstab4(text) RETURNS setof tablefunc_crosstab_4 AS 'MODULE_PATHNAME','crosstab' LANGUAGE 'c' STABLE STRICT; +CREATE OR REPLACE FUNCTION crosstab(text,int) + RETURNS setof record + AS 'MODULE_PATHNAME','crosstab' LANGUAGE 'c' STABLE STRICT; \ No newline at end of file diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 4e2764fc43..3cfa84191c 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.81 2002/08/14 23:02:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.82 2002/08/15 02:51:26 momjian Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut . @@ -29,6 +29,7 @@ #include "commands/vacuum.h" #include "executor/executor.h" #include "fmgr.h" +#include "funcapi.h" #include "libpq/auth.h" #include "libpq/pqcomm.h" #include "mb/pg_wchar.h" @@ -2403,6 +2404,117 @@ show_config_by_name(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(result_text); } +/* + * show_all_settings - equiv to SHOW ALL command but implemented as + * a Table Function. + */ +Datum +show_all_settings(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + TupleDesc tupdesc; + int call_cntr; + int max_calls; + TupleTableSlot *slot; + AttInMetadata *attinmeta; + + /* 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(); + + /* need a tuple descriptor representing two TEXT columns */ + tupdesc = CreateTemplateTupleDesc(2, WITHOUTOID); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", + TEXTOID, -1, 0, false); + + /* allocate a slot for a tuple with this tupdesc */ + slot = TupleDescGetSlot(tupdesc); + + /* assign slot to function context */ + funcctx->slot = slot; + + /* + * 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(); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + slot = funcctx->slot; + attinmeta = funcctx->attinmeta; + + if (call_cntr < max_calls) /* do when there is more left to send */ + { + char **values; + char *varname; + char *varval; + bool noshow; + HeapTuple tuple; + Datum result; + + /* + * Get the next visible GUC variable name and value + */ + do + { + varval = GetConfigOptionByNum(call_cntr, (const char **) &varname, &noshow); + if (noshow) + { + /* varval is a palloc'd copy, so free it */ + if (varval != NULL) + pfree(varval); + + /* 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); + + /* + * Prepare a values array for storage in our slot. + * This should be an array of C strings which will + * be processed later by the appropriate "in" functions. + */ + values = (char **) palloc(2 * sizeof(char *)); + values[0] = pstrdup(varname); + values[1] = varval; /* varval is already a palloc'd copy */ + + /* build a tuple */ + tuple = BuildTupleFromCStrings(attinmeta, values); + + /* make the tuple into a datum */ + result = TupleGetDatum(slot, tuple); + + /* Clean up */ + pfree(values[0]); + if (varval != NULL) + pfree(values[1]); + pfree(values); + + SRF_RETURN_NEXT(funcctx, result); + } + else /* do when there is no more left */ + { + SRF_RETURN_DONE(funcctx); + } +} + static char * _ShowOption(struct config_generic *record) { diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 0a09d6252f..d876a888a2 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.165 2002/08/08 19:39:05 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.166 2002/08/15 02:51:26 momjian Exp $ # #------------------------------------------------------------------------- @@ -1015,6 +1015,21 @@ CREATE VIEW pg_stat_database AS \ pg_stat_get_db_blocks_hit(D.oid) AS blks_hit \ FROM pg_database D; +CREATE VIEW pg_settings AS \ + SELECT \ + A.name, \ + A.setting \ + FROM pg_show_all_settings() AS A(name text, setting text); + +CREATE RULE pg_settings_u AS \ + ON UPDATE TO pg_settings \ + WHERE new.name = old.name DO \ + SELECT set_config(old.name, new.setting, 'f'); + +CREATE RULE pg_settings_n AS \ + ON UPDATE TO pg_settings \ + DO INSTEAD NOTHING; + EOF if [ "$?" -ne 0 ]; then exit_nicely diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c4717e7b51..0c0a1a7bde 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.253 2002/08/09 16:45:15 tgl Exp $ + * $Id: pg_proc.h,v 1.254 2002/08/15 02:51:27 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2885,6 +2885,8 @@ DATA(insert OID = 2077 ( current_setting PGNSP PGUID 12 f f t f s 1 25 "25" sho DESCR("SHOW X as a function"); DATA(insert OID = 2078 ( set_config PGNSP PGUID 12 f f f f v 3 25 "25 25 16" set_config_by_name - _null_ )); DESCR("SET X as a function"); +DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 f f t t s 0 2249 "" show_all_settings - _null_ )); +DESCR("SHOW ALL as a function"); DATA(insert OID = 2079 ( pg_table_is_visible PGNSP PGUID 12 f f t f s 1 16 "26" pg_table_is_visible - _null_ )); DESCR("is table visible in search path?"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 01d5a4e730..621ab80315 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.190 2002/08/09 16:45:16 tgl Exp $ + * $Id: builtins.h,v 1.191 2002/08/15 02:51:27 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -662,6 +662,7 @@ 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); +extern Datum show_all_settings(PG_FUNCTION_ARGS); /* catalog/pg_conversion.c */ extern Datum pg_convert3(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 3a7916dd81..b8a76bb94d 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1269,6 +1269,7 @@ SELECT viewname, definition FROM pg_views ORDER BY viewname; iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, pg_get_indexdef(i.oid) AS indexdef FROM (((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); + pg_settings | SELECT a.name, a.setting FROM pg_show_all_settings() a; pg_stat_activity | SELECT d.oid AS datid, d.datname, pg_stat_get_backend_pid(s.backendid) AS procpid, pg_stat_get_backend_userid(s.backendid) AS usesysid, u.usename, pg_stat_get_backend_activity(s.backendid) AS current_query FROM pg_database d, (SELECT pg_stat_get_backend_idset() AS backendid) s, pg_shadow u WHERE ((pg_stat_get_backend_dbid(s.backendid) = d.oid) AND (pg_stat_get_backend_userid(s.backendid) = u.usesysid)); pg_stat_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, pg_stat_get_numscans(i.oid) AS idx_scan, pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch FROM (((pg_class c JOIN pg_index x ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'r'::"char"); pg_stat_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_numscans(c.oid) AS seq_scan, pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, sum(pg_stat_get_numscans(i.indexrelid)) AS idx_scan, sum(pg_stat_get_tuples_fetched(i.indexrelid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'r'::"char") GROUP BY c.oid, n.nspname, c.relname; @@ -1304,12 +1305,14 @@ SELECT viewname, definition FROM pg_views ORDER BY viewname; shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp; -(38 rows) +(39 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; tablename | rulename | definition ---------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + pg_settings | pg_settings_n | CREATE RULE pg_settings_n AS ON UPDATE TO pg_settings DO INSTEAD NOTHING; + pg_settings | pg_settings_u | CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, 'f'::boolean) AS set_config; rtest_emp | rtest_emp_del | CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (old.ename, "current_user"(), 'fired '::bpchar, '$0.00'::money, old.salary); rtest_emp | rtest_emp_ins | CREATE RULE rtest_emp_ins AS ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'hired '::bpchar, new.salary, '$0.00'::money); rtest_emp | rtest_emp_upd | CREATE RULE rtest_emp_upd AS ON UPDATE TO rtest_emp WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'honored '::bpchar, new.salary, old.salary); @@ -1337,5 +1340,5 @@ SELECT tablename, rulename, definition FROM pg_rules shoelace | shoelace_upd | CREATE RULE shoelace_upd AS ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = new.sl_name, sl_avail = new.sl_avail, sl_color = new.sl_color, sl_len = new.sl_len, sl_unit = new.sl_unit WHERE (shoelace_data.sl_name = old.sl_name); shoelace_data | log_shoelace | CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data WHERE (new.sl_avail <> old.sl_avail) DO INSERT INTO shoelace_log (sl_name, sl_avail, log_who, log_when) VALUES (new.sl_name, new.sl_avail, 'Al Bundy'::name, 'Thu Jan 01 00:00:00 1970'::timestamp without time zone); shoelace_ok | shoelace_ok_ins | CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = (shoelace.sl_avail + new.ok_quant) WHERE (shoelace.sl_name = new.ok_name); -(27 rows) +(29 rows)