As discussed on several occasions previously, the new anonymous
authorBruce Momjian
Thu, 15 Aug 2002 02:51:27 +0000 (02:51 +0000)
committerBruce Momjian
Thu, 15 Aug 2002 02:51:27 +0000 (02:51 +0000)
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
contrib/tablefunc/tablefunc-test.sql
contrib/tablefunc/tablefunc.c
contrib/tablefunc/tablefunc.h
contrib/tablefunc/tablefunc.sql.in
src/backend/utils/misc/guc.c
src/bin/initdb/initdb.sh
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/rules.out

index b3b663aafa227d8575f4494cf32e7c9e406b5496..310778ca3668615e217d20ad23c9b0ad2c04a82d 100644 (file)
@@ -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,45 +55,12 @@ Installation:
         but you can create additional crosstab functions per 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)
+    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
 
@@ -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
 
index 141894b0f418d472a52c297b1d44200eb54ca533..e1e0a7c89e443788e0d8440e7a8535ed8233497d 100644 (file)
@@ -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);
index 236d833e469e2bdf259dcd77453dcdd1d8e34df4..d05fc1a76b66c04444218e3fb8689b6b9ccb038c 100644 (file)
 #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");
+       }
+       else
+       {
+           /* no qualifying tuples */
+           SPI_finish();
+           SRF_RETURN_DONE(funcctx);
+       }
 
-           /*
-            * 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));
+       /* back to the original memory context */
+       MemoryContextSwitchTo(oldcontext);
 
-           /*
-            * 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;
+       /* get the typeid that represents our return type */
+       functypeid = get_func_rettype(funcid);
 
-           /* total number of tuples to be returned */
-           funcctx->max_calls = proc;
+       /* 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
+       else if (functyptype == 'p' && functypeid == RECORDOID)
        {
-           /* no qualifying tuples */
-           funcctx->max_calls = 0;
+           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;
+}
+
index 309894ac66ca6717369e2a972c01bf1e0681f9a3..44cfd11fcc06370f9b9d34ab3f93260d28fa90bd 100644 (file)
@@ -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);
 
index 746e8f9cff16bc747d9eef32beacc2d52d93a0d4..7d599d4f08c2aa764eb036d7b85baa1b012eac88 100644 (file)
@@ -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
index 4e2764fc43064a2a65dc4a276374c77d54e33fdd..3cfa84191c77d6ea0a9f545d3ed61cd9670aca29 100644 (file)
@@ -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)
 {
index 0a09d6252f253469471833f23ac853b9257e5518..d876a888a20aa39f624e329faf140ec59792df02 100644 (file)
@@ -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
index c4717e7b511cf447038f551439d26a780f32b261..0c0a1a7bde5cad1b1508cdce0cf53f045173f163 100644 (file)
@@ -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?");
index 01d5a4e7306bc7850e53a7d41be47bfdf423894c..621ab80315b57c63dd02f8903c7b06eddf75f451 100644 (file)
@@ -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);
index 3a7916dd81c60d6cc7b4eabbcdcf0f33e7ce7f89..b8a76bb94d6e55c47cc71271a6ebcdb69a336d3b 100644 (file)
@@ -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)