OSDN Git Service

Revise rule-printing routines to use expandable StringInfo buffers, so that
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Oct 1999 01:08:05 +0000 (01:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Oct 1999 01:08:05 +0000 (01:08 +0000)
they have no hardwired limit on the length of a rule's text.  Fix a couple
of minor bugs in passing --- deparsed UPDATE queries didn't have quotes
around relation name, and quotes and backslashes in constant values weren't
backslash-quoted.

src/backend/utils/adt/ruleutils.c
src/test/regress/expected/rules.out

index e89d229..59f3586 100644 (file)
@@ -3,7 +3,7 @@
  *                       out of it's tuple
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.25 1999/09/02 03:04:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.26 1999/10/02 01:07:51 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -40,6 +40,7 @@
 
 #include "postgres.h"
 #include "executor/spi.h"
+#include "lib/stringinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/tlist.h"
 #include "utils/lsyscache.h"
@@ -47,7 +48,6 @@
 #include "catalog/pg_operator.h"
 #include "catalog/pg_shadow.h"
 
-#define BUFSIZE 8192
 
 /* ----------
  * Local data types
@@ -92,21 +92,29 @@ NameData   *pg_get_userbyid(int4 uid);
 
 /* ----------
  * Local functions
+ *
+ * Most of these functions used to use fixed-size buffers to build their
+ * results.  Now, they take an (already initialized) StringInfo object
+ * as a parameter, and append their text output to its contents.
  * ----------
  */
-static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc);
-static char *make_viewdef(HeapTuple ruletup, TupleDesc rulettc);
-static char *get_query_def(Query *query, QryHier *parentqh);
-static char *get_select_query_def(Query *query, QryHier *qh);
-static char *get_insert_query_def(Query *query, QryHier *qh);
-static char *get_update_query_def(Query *query, QryHier *qh);
-static char *get_delete_query_def(Query *query, QryHier *qh);
+static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
+static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
+static void get_query_def(StringInfo buf, Query *query, QryHier *parentqh);
+static void get_select_query_def(StringInfo buf, Query *query, QryHier *qh);
+static void get_insert_query_def(StringInfo buf, Query *query, QryHier *qh);
+static void get_update_query_def(StringInfo buf, Query *query, QryHier *qh);
+static void get_delete_query_def(StringInfo buf, Query *query, QryHier *qh);
 static RangeTblEntry *get_rte_for_var(Var *var, QryHier *qh);
-static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
-static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix);
-static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix);
-static char *get_const_expr(Const *constval);
-static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
+static void get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
+                                                 Node *node, bool varprefix);
+static void get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
+                                                 Expr *expr, bool varprefix);
+static void get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
+                                                TargetEntry *tle, bool varprefix);
+static void get_const_expr(StringInfo buf, Const *constval);
+static void get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
+                                                        Node *node, bool varprefix);
 static char *get_relation_name(Oid relid);
 static char *get_attribute_name(Oid relid, int2 attnum);
 static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
@@ -129,7 +137,7 @@ pg_get_ruledef(NameData *rname)
        int                     spirc;
        HeapTuple       ruletup;
        TupleDesc       rulettc;
-       char       *tmp;
+       StringInfoData  buf;
        int                     len;
 
        /* ----------
@@ -190,11 +198,13 @@ pg_get_ruledef(NameData *rname)
         * Get the rules definition and put it into executors memory
         * ----------
         */
-       tmp = make_ruledef(ruletup, rulettc);
-       len = strlen(tmp) + VARHDRSZ;
+       initStringInfo(&buf);
+       make_ruledef(&buf, ruletup, rulettc);
+       len = buf.len + VARHDRSZ;
        ruledef = SPI_palloc(len);
        VARSIZE(ruledef) = len;
-       memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
+       memcpy(VARDATA(ruledef), buf.data, buf.len);
+       pfree(buf.data);
 
        /* ----------
         * Disconnect from SPI manager
@@ -225,7 +235,7 @@ pg_get_viewdef(NameData *rname)
        int                     spirc;
        HeapTuple       ruletup;
        TupleDesc       rulettc;
-       char       *tmp;
+       StringInfoData  buf;
        int                     len;
        char            name1[NAMEDATALEN + 5];
        char            name2[NAMEDATALEN + 5];
@@ -276,8 +286,9 @@ pg_get_viewdef(NameData *rname)
        spirc = SPI_execp(plan_getview, args, nulls, 1);
        if (spirc != SPI_OK_SELECT)
                elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
+       initStringInfo(&buf);
        if (SPI_processed != 1)
-               tmp = "Not a view";
+               appendStringInfo(&buf, "Not a view");
        else
        {
                /* ----------
@@ -286,12 +297,13 @@ pg_get_viewdef(NameData *rname)
                 */
                ruletup = SPI_tuptable->vals[0];
                rulettc = SPI_tuptable->tupdesc;
-               tmp = make_viewdef(ruletup, rulettc);
+               make_viewdef(&buf, ruletup, rulettc);
        }
-       len = strlen(tmp) + VARHDRSZ;
+       len = buf.len + VARHDRSZ;
        ruledef = SPI_palloc(len);
        VARSIZE(ruledef) = len;
-       memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
+       memcpy(VARDATA(ruledef), buf.data, buf.len);
+       pfree(buf.data);
 
        /* ----------
         * Disconnect from SPI manager
@@ -309,8 +321,7 @@ pg_get_viewdef(NameData *rname)
 
 
 /* ----------
- * get_viewdef                 - Mainly the same thing, but we
- *                               only return the SELECT part of a view
+ * get_indexdef                        - Get the definition of an index
  * ----------
  */
 text *
@@ -331,8 +342,8 @@ pg_get_indexdef(Oid indexrelid)
        int                     spirc;
        int                     len;
        int                     keyno;
-       char            buf[BUFSIZE];
-       char            keybuf[BUFSIZE];
+       StringInfoData  buf;
+       StringInfoData  keybuf;
        char       *sep;
 
        /* ----------
@@ -415,37 +426,37 @@ pg_get_indexdef(Oid indexrelid)
         * Start the index definition
         * ----------
         */
-       sprintf(buf, "CREATE %sINDEX \"%s\" ON \"%s\" USING %s (",
-                       idxrec->indisunique ? "UNIQUE " : "",
-                       nameout(&(idxrelrec->relname)),
-                       nameout(&(indrelrec->relname)),
-                       SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+       initStringInfo(&buf);
+       appendStringInfo(&buf, "CREATE %sINDEX \"%s\" ON \"%s\" USING %s (",
+                                        idxrec->indisunique ? "UNIQUE " : "",
+                                        nameout(&(idxrelrec->relname)),
+                                        nameout(&(indrelrec->relname)),
+                                        SPI_getvalue(spi_tup, spi_ttc, spi_fno));
 
        /* ----------
         * Collect the indexed attributes
         * ----------
         */
+       initStringInfo(&keybuf);
        sep = "";
-       keybuf[0] = '\0';
        for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
        {
                if (idxrec->indkey[keyno] == InvalidAttrNumber)
                        break;
 
-               strcat(keybuf, sep);
+               appendStringInfo(&keybuf, sep);
                sep = ", ";
 
                /* ----------
                 * Add the indexed field name
                 * ----------
                 */
-               strcat(keybuf, "\"");
                if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
-                       strcat(keybuf, "oid");
+                       appendStringInfo(&keybuf, "\"oid\"");
                else
-                       strcat(keybuf, get_attribute_name(idxrec->indrelid,
-                                                                                         idxrec->indkey[keyno]));
-               strcat(keybuf, "\"");
+                       appendStringInfo(&keybuf, "\"%s\"",
+                                                        get_attribute_name(idxrec->indrelid,
+                                                                                               idxrec->indkey[keyno]));
 
                /* ----------
                 * If not a functional index, add the operator class name
@@ -464,9 +475,8 @@ pg_get_indexdef(Oid indexrelid)
                        spi_tup = SPI_tuptable->vals[0];
                        spi_ttc = SPI_tuptable->tupdesc;
                        spi_fno = SPI_fnumber(spi_ttc, "opcname");
-                       strcat(keybuf, " \"");
-                       strcat(keybuf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
-                       strcat(keybuf, "\"");
+                       appendStringInfo(&keybuf, " \"%s\"",
+                                                        SPI_getvalue(spi_tup, spi_ttc, spi_fno));
                }
        }
 
@@ -485,11 +495,9 @@ pg_get_indexdef(Oid indexrelid)
                        elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
 
                procStruct = (Form_pg_proc) GETSTRUCT(proctup);
-               strcat(buf, "\"");
-               strcat(buf, nameout(&(procStruct->proname)));
-               strcat(buf, "\" (");
-               strcat(buf, keybuf);
-               strcat(buf, ") ");
+               appendStringInfo(&buf, "\"%s\" (%s) ",
+                                                nameout(&(procStruct->proname)),
+                                                keybuf.data);
 
                spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
                spi_nulls[0] = ' ';
@@ -502,31 +510,32 @@ pg_get_indexdef(Oid indexrelid)
                spi_tup = SPI_tuptable->vals[0];
                spi_ttc = SPI_tuptable->tupdesc;
                spi_fno = SPI_fnumber(spi_ttc, "opcname");
-               strcat(buf, "\"");
-               strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
-               strcat(buf, "\"");
+               appendStringInfo(&buf, "\"%s\"",
+                                                SPI_getvalue(spi_tup, spi_ttc, spi_fno));
        }
        else
                /* ----------
                 * For the others say 'attr opclass [, ...]'
                 * ----------
                 */
-               strcat(buf, keybuf);
+               appendStringInfo(&buf, "%s", keybuf.data);
 
        /* ----------
         * Finish
         * ----------
         */
-       strcat(buf, ")");
+       appendStringInfo(&buf, ")");
 
        /* ----------
         * Create the result in upper executor memory
         * ----------
         */
-       len = strlen(buf) + VARHDRSZ;
+       len = buf.len + VARHDRSZ;
        indexdef = SPI_palloc(len);
        VARSIZE(indexdef) = len;
-       memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
+       memcpy(VARDATA(indexdef), buf.data, buf.len);
+       pfree(buf.data);
+       pfree(keybuf.data);
 
        /* ----------
         * Disconnect from SPI manager
@@ -581,10 +590,9 @@ pg_get_userbyid(int4 uid)
  *                               for a given pg_rewrite tuple
  * ----------
  */
-static char *
-make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
+static void
+make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 {
-       char       *buf;
        char            ev_type;
        Oid                     ev_class;
        int2            ev_attr;
@@ -596,12 +604,6 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
        bool            isnull;
 
        /* ----------
-        * Allocate space for the returned rule definition text
-        * ----------
-        */
-       buf = palloc(BUFSIZE);
-
-       /* ----------
         * Get the attribute values from the rules tuple
         * ----------
         */
@@ -630,29 +632,25 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
         * Build the rules definition text
         * ----------
         */
-       strcpy(buf, "CREATE RULE \"");
-
-       /* The rule name */
-       strcat(buf, rulename);
-       strcat(buf, "\" AS ON ");
+       appendStringInfo(buf, "CREATE RULE \"%s\" AS ON ", rulename);
 
        /* The event the rule is fired for */
        switch (ev_type)
        {
                case '1':
-                       strcat(buf, "SELECT TO \"");
+                       appendStringInfo(buf, "SELECT");
                        break;
 
                case '2':
-                       strcat(buf, "UPDATE TO \"");
+                       appendStringInfo(buf, "UPDATE");
                        break;
 
                case '3':
-                       strcat(buf, "INSERT TO \"");
+                       appendStringInfo(buf, "INSERT");
                        break;
 
                case '4':
-                       strcat(buf, "DELETE TO \"");
+                       appendStringInfo(buf, "DELETE");
                        break;
 
                default:
@@ -662,14 +660,10 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
        }
 
        /* The relation the rule is fired on */
-       strcat(buf, get_relation_name(ev_class));
-       strcat(buf, "\"");
+       appendStringInfo(buf, " TO \"%s\"", get_relation_name(ev_class));
        if (ev_attr > 0)
-       {
-               strcat(buf, ".\"");
-               strcat(buf, get_attribute_name(ev_class, ev_attr));
-               strcat(buf, "\"");
-       }
+               appendStringInfo(buf, ".\"%s\"",
+                                                get_attribute_name(ev_class, ev_attr));
 
        /* If the rule has an event qualification, add it */
        if (ev_qual == NULL)
@@ -685,15 +679,15 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
                qh.parent = NULL;
                qh.query = query;
 
-               strcat(buf, " WHERE ");
-               strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
+               appendStringInfo(buf, " WHERE ");
+               get_rule_expr(buf, &qh, 0, qual, TRUE);
        }
 
-       strcat(buf, " DO ");
+       appendStringInfo(buf, " DO ");
 
        /* The INSTEAD keyword (if so) */
        if (is_instead)
-               strcat(buf, "INSTEAD ");
+               appendStringInfo(buf, "INSTEAD ");
 
        /* Finally the rules actions */
        if (length(actions) > 1)
@@ -701,36 +695,30 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
                List       *action;
                Query      *query;
 
-               strcat(buf, "(");
+               appendStringInfo(buf, "(");
                foreach(action, actions)
                {
                        query = (Query *) lfirst(action);
-                       strcat(buf, get_query_def(query, NULL));
-                       strcat(buf, "; ");
+                       get_query_def(buf, query, NULL);
+                       appendStringInfo(buf, "; ");
                }
-               strcat(buf, ");");
+               appendStringInfo(buf, ");");
        }
        else
        {
                if (length(actions) == 0)
                {
-                       strcat(buf, "NOTHING;");
+                       appendStringInfo(buf, "NOTHING;");
                }
                else
                {
                        Query      *query;
 
                        query = (Query *) lfirst(actions);
-                       strcat(buf, get_query_def(query, NULL));
-                       strcat(buf, ";");
+                       get_query_def(buf, query, NULL);
+                       appendStringInfo(buf, ";");
                }
        }
-
-       /* ----------
-        * That's it
-        * ----------
-        */
-       return buf;
 }
 
 
@@ -739,10 +727,9 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
  *                               view rewrite rule
  * ----------
  */
-static char *
-make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
+static void
+make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 {
-       char            buf[BUFSIZE];
        Query      *query;
        char            ev_type;
        Oid                     ev_class;
@@ -779,21 +766,21 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
                actions = (List *) stringToNode(ev_action);
 
        if (length(actions) != 1)
-               return "Not a view";
+       {
+               appendStringInfo(buf, "Not a view");
+               return;
+       }
 
        query = (Query *) lfirst(actions);
 
        if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>"))
-               return "Not a view";
-
-       strcpy(buf, get_query_def(query, NULL));
-       strcat(buf, ";");
+       {
+               appendStringInfo(buf, "Not a view");
+               return;
+       }
 
-       /* ----------
-        * That's it
-        * ----------
-        */
-       return pstrdup(buf);
+       get_query_def(buf, query, NULL);
+       appendStringInfo(buf, ";");
 }
 
 
@@ -803,8 +790,8 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
  *                                       list
  * ----------
  */
-static char *
-get_query_def(Query *query, QryHier *parentqh)
+static void
+get_query_def(StringInfo buf, Query *query, QryHier *parentqh)
 {
        QryHier         qh;
 
@@ -814,23 +801,23 @@ get_query_def(Query *query, QryHier *parentqh)
        switch (query->commandType)
        {
                case CMD_SELECT:
-                       return get_select_query_def(query, &qh);
+                       get_select_query_def(buf, query, &qh);
                        break;
 
                case CMD_UPDATE:
-                       return get_update_query_def(query, &qh);
+                       get_update_query_def(buf, query, &qh);
                        break;
 
                case CMD_INSERT:
-                       return get_insert_query_def(query, &qh);
+                       get_insert_query_def(buf, query, &qh);
                        break;
 
                case CMD_DELETE:
-                       return get_delete_query_def(query, &qh);
+                       get_delete_query_def(buf, query, &qh);
                        break;
 
                case CMD_NOTHING:
-                       return "NOTHING";
+                       appendStringInfo(buf, "NOTHING");
                        break;
 
                default:
@@ -838,8 +825,6 @@ get_query_def(Query *query, QryHier *parentqh)
                                 rulename, query->commandType);
                        break;
        }
-
-       return NULL;
 }
 
 
@@ -847,10 +832,9 @@ get_query_def(Query *query, QryHier *parentqh)
  * get_select_query_def                        - Parse back a SELECT parsetree
  * ----------
  */
-static char *
-get_select_query_def(Query *query, QryHier *qh)
+static void
+get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
 {
-       char            buf[BUFSIZE];
        char       *sep;
        TargetEntry *tle;
        RangeTblEntry *rte;
@@ -908,7 +892,7 @@ get_select_query_def(Query *query, QryHier *qh)
         * Build up the query string - first we say SELECT
         * ----------
         */
-       strcpy(buf, "SELECT");
+       appendStringInfo(buf, "SELECT");
 
        /* Then we tell what to select (the targetlist) */
        sep = " ";
@@ -917,10 +901,10 @@ get_select_query_def(Query *query, QryHier *qh)
                bool            tell_as = FALSE;
 
                tle = (TargetEntry *) lfirst(l);
-               strcat(buf, sep);
+               appendStringInfo(buf, sep);
                sep = ", ";
 
-               strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
+               get_tle_expr(buf, qh, 0, tle, (rt_numused > 1));
 
                /* Check if we must say AS ... */
                if (! IsA(tle->expr, Var))
@@ -938,17 +922,13 @@ get_select_query_def(Query *query, QryHier *qh)
 
                /* and do if so */
                if (tell_as)
-               {
-                       strcat(buf, " AS \"");
-                       strcat(buf, tle->resdom->resname);
-                       strcat(buf, "\"");
-               }
+                       appendStringInfo(buf, " AS \"%s\"", tle->resdom->resname);
        }
 
        /* If we need other tables that *NEW* or *CURRENT* add the FROM clause */
        if (!rt_constonly && rt_numused > 0)
        {
-               strcat(buf, " FROM");
+               appendStringInfo(buf, " FROM");
 
                i = 0;
                sep = " ";
@@ -964,17 +944,11 @@ get_select_query_def(Query *query, QryHier *qh)
                                if (!strcmp(rte->refname, "*CURRENT*"))
                                        continue;
 
-                               strcat(buf, sep);
+                               appendStringInfo(buf, sep);
                                sep = ", ";
-                               strcat(buf, "\"");
-                               strcat(buf, rte->relname);
-                               strcat(buf, "\"");
+                               appendStringInfo(buf, "\"%s\"", rte->relname);
                                if (strcmp(rte->relname, rte->refname) != 0)
-                               {
-                                       strcat(buf, " \"");
-                                       strcat(buf, rte->refname);
-                                       strcat(buf, "\"");
-                               }
+                                       appendStringInfo(buf, " \"%s\"", rte->refname);
                        }
                }
        }
@@ -982,14 +956,14 @@ get_select_query_def(Query *query, QryHier *qh)
        /* Add the WHERE clause if given */
        if (query->qual != NULL)
        {
-               strcat(buf, " WHERE ");
-               strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
+               appendStringInfo(buf, " WHERE ");
+               get_rule_expr(buf, qh, 0, query->qual, (rt_numused > 1));
        }
 
        /* Add the GROUP BY CLAUSE */
        if (query->groupClause != NULL)
        {
-               strcat(buf, " GROUP BY ");
+               appendStringInfo(buf, " GROUP BY ");
                sep = "";
                foreach(l, query->groupClause)
                {
@@ -998,17 +972,11 @@ get_select_query_def(Query *query, QryHier *qh)
 
                        groupexpr = get_sortgroupclause_expr(grp,
                                                                                                 query->targetList);
-                       strcat(buf, sep);
-                       strcat(buf, get_rule_expr(qh, 0, groupexpr, (rt_numused > 1)));
+                       appendStringInfo(buf, sep);
+                       get_rule_expr(buf, qh, 0, groupexpr, (rt_numused > 1));
                        sep = ", ";
                }
        }
-
-       /* ----------
-        * Copy the query string into allocated space and return it
-        * ----------
-        */
-       return pstrdup(buf);
 }
 
 
@@ -1016,10 +984,9 @@ get_select_query_def(Query *query, QryHier *qh)
  * get_insert_query_def                        - Parse back an INSERT parsetree
  * ----------
  */
-static char *
-get_insert_query_def(Query *query, QryHier *qh)
+static void
+get_insert_query_def(StringInfo buf, Query *query, QryHier *qh)
 {
-       char            buf[BUFSIZE];
        char       *sep;
        TargetEntry *tle;
        RangeTblEntry *rte;
@@ -1072,9 +1039,7 @@ get_insert_query_def(Query *query, QryHier *qh)
         * ----------
         */
        rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
-       strcpy(buf, "INSERT INTO \"");
-       strcat(buf, rte->relname);
-       strcat(buf, "\"");
+       appendStringInfo(buf, "INSERT INTO \"%s\"", rte->relname);
 
        /* Add the target list */
        sep = " (";
@@ -1082,37 +1047,29 @@ get_insert_query_def(Query *query, QryHier *qh)
        {
                tle = (TargetEntry *) lfirst(l);
 
-               strcat(buf, sep);
+               appendStringInfo(buf, sep);
                sep = ", ";
-               strcat(buf, "\"");
-               strcat(buf, tle->resdom->resname);
-               strcat(buf, "\"");
+               appendStringInfo(buf, "\"%s\"", tle->resdom->resname);
        }
-       strcat(buf, ") ");
+       appendStringInfo(buf, ") ");
 
        /* Add the VALUES or the SELECT */
        if (rt_constonly && query->qual == NULL)
        {
-               strcat(buf, "VALUES (");
+               appendStringInfo(buf, "VALUES (");
                sep = "";
                foreach(l, query->targetList)
                {
                        tle = (TargetEntry *) lfirst(l);
 
-                       strcat(buf, sep);
+                       appendStringInfo(buf, sep);
                        sep = ", ";
-                       strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
+                       get_tle_expr(buf, qh, 0, tle, (rt_numused > 1));
                }
-               strcat(buf, ")");
+               appendStringInfo(buf, ")");
        }
        else
-               strcat(buf, get_select_query_def(query, qh));
-
-       /* ----------
-        * Copy the query string into allocated space and return it
-        * ----------
-        */
-       return pstrdup(buf);
+               get_select_query_def(buf, query, qh);
 }
 
 
@@ -1120,10 +1077,9 @@ get_insert_query_def(Query *query, QryHier *qh)
  * get_update_query_def                        - Parse back an UPDATE parsetree
  * ----------
  */
-static char *
-get_update_query_def(Query *query, QryHier *qh)
+static void
+get_update_query_def(StringInfo buf, Query *query, QryHier *qh)
 {
-       char            buf[BUFSIZE];
        char       *sep;
        TargetEntry *tle;
        RangeTblEntry *rte;
@@ -1134,9 +1090,7 @@ get_update_query_def(Query *query, QryHier *qh)
         * ----------
         */
        rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
-       strcpy(buf, "UPDATE ");
-       strcat(buf, rte->relname);
-       strcat(buf, " SET ");
+       appendStringInfo(buf, "UPDATE \"%s\" SET ", rte->relname);
 
        /* Add the comma separated list of 'attname = value' */
        sep = "";
@@ -1144,28 +1098,18 @@ get_update_query_def(Query *query, QryHier *qh)
        {
                tle = (TargetEntry *) lfirst(l);
 
-               strcat(buf, sep);
+               appendStringInfo(buf, sep);
                sep = ", ";
-               strcat(buf, "\"");
-               strcat(buf, tle->resdom->resname);
-               strcat(buf, "\" = ");
-               strcat(buf, get_tle_expr(qh, query->resultRelation,
-                                                                tle, TRUE));
+               appendStringInfo(buf, "\"%s\" = ", tle->resdom->resname);
+               get_tle_expr(buf, qh, query->resultRelation, tle, TRUE);
        }
 
        /* Finally add a WHERE clause if given */
        if (query->qual != NULL)
        {
-               strcat(buf, " WHERE ");
-               strcat(buf, get_rule_expr(qh, query->resultRelation,
-                                                                 query->qual, TRUE));
+               appendStringInfo(buf, " WHERE ");
+               get_rule_expr(buf, qh, query->resultRelation, query->qual, TRUE);
        }
-
-       /* ----------
-        * Copy the query string into allocated space and return it
-        * ----------
-        */
-       return pstrdup(buf);
 }
 
 
@@ -1173,10 +1117,9 @@ get_update_query_def(Query *query, QryHier *qh)
  * get_delete_query_def                        - Parse back a DELETE parsetree
  * ----------
  */
-static char *
-get_delete_query_def(Query *query, QryHier *qh)
+static void
+get_delete_query_def(StringInfo buf, Query *query, QryHier *qh)
 {
-       char            buf[BUFSIZE];
        RangeTblEntry *rte;
 
        /* ----------
@@ -1184,22 +1127,14 @@ get_delete_query_def(Query *query, QryHier *qh)
         * ----------
         */
        rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
-       strcpy(buf, "DELETE FROM \"");
-       strcat(buf, rte->relname);
-       strcat(buf, "\"");
+       appendStringInfo(buf, "DELETE FROM \"%s\"", rte->relname);
 
        /* Add a WHERE clause if given */
        if (query->qual != NULL)
        {
-               strcat(buf, " WHERE ");
-               strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
+               appendStringInfo(buf, " WHERE ");
+               get_rule_expr(buf, qh, 0, query->qual, FALSE);
        }
-
-       /* ----------
-        * Copy the query string into allocated space and return it
-        * ----------
-        */
-       return pstrdup(buf);
 }
 
 /*
@@ -1221,28 +1156,26 @@ get_rte_for_var(Var *var, QryHier *qh)
  * get_rule_expr                       - Parse back an expression
  * ----------
  */
-static char *
-get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
+static void
+get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
+                         Node *node, bool varprefix)
 {
-       char            buf[BUFSIZE];
-
        if (node == NULL)
-               return pstrdup("");
-
-       buf[0] = '\0';
+               return;
 
        /* ----------
-        * Each level of get_rule_expr must return an indivisible term
+        * Each level of get_rule_expr must emit an indivisible term
         * (parenthesized if necessary) to ensure result is reparsed into
         * the same expression tree.
         *
-        * There might be some work left here to support additional node types...
+        * There might be some work left here to support additional node types.
+        * Can we ever see Param nodes here?
         * ----------
         */
        switch (nodeTag(node))
        {
                case T_Const:
-                       return get_const_expr((Const *) node);
+                       get_const_expr(buf, (Const *) node);
                        break;
 
                case T_Var:
@@ -1251,20 +1184,14 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
                                RangeTblEntry *rte = get_rte_for_var(var, qh);
 
                                if (!strcmp(rte->refname, "*NEW*"))
-                                       strcat(buf, "new.");
+                                       appendStringInfo(buf, "new.");
                                else if (!strcmp(rte->refname, "*CURRENT*"))
-                                       strcat(buf, "old.");
+                                       appendStringInfo(buf, "old.");
                                else
-                               {
-                                       strcat(buf, "\"");
-                                       strcat(buf, rte->refname);
-                                       strcat(buf, "\".");
-                               }
-                               strcat(buf, "\"");
-                               strcat(buf, get_attribute_name(rte->relid, var->varattno));
-                               strcat(buf, "\"");
-
-                               return pstrdup(buf);
+                                       appendStringInfo(buf, "\"%s\".", rte->refname);
+                               appendStringInfo(buf, "\"%s\"",
+                                                                get_attribute_name(rte->relid,
+                                                                                                       var->varattno));
                        }
                        break;
 
@@ -1280,22 +1207,18 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
                                switch (expr->opType)
                                {
                                        case OP_EXPR:
-                                               strcat(buf, "(");
+                                               appendStringInfo(buf, "(");
                                                if (length(args) == 2)
                                                {
                                                        /* binary operator */
-                                                       strcat(buf,
-                                                                  get_rule_expr(qh, rt_index,
-                                                                                                (Node *) lfirst(args),
-                                                                                                varprefix));
-                                                       strcat(buf, " ");
-                                                       strcat(buf,
-                                                                  get_opname(((Oper *) expr->oper)->opno));
-                                                       strcat(buf, " ");
-                                                       strcat(buf,
-                                                                  get_rule_expr(qh, rt_index,
-                                                                                                (Node *) lsecond(args),
-                                                                                                varprefix));
+                                                       get_rule_expr(buf, qh, rt_index,
+                                                                                 (Node *) lfirst(args),
+                                                                                 varprefix);
+                                                       appendStringInfo(buf, " %s ",
+                                                                       get_opname(((Oper *) expr->oper)->opno));
+                                                       get_rule_expr(buf, qh, rt_index,
+                                                                                 (Node *) lsecond(args),
+                                                                                 varprefix);
                                                }
                                                else
                                                {
@@ -1312,82 +1235,76 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
                                                        switch (optup->oprkind)
                                                        {
                                                                case 'l':
-                                                                       strcat(buf, get_opname(opno));
-                                                                       strcat(buf, " ");
-                                                                       strcat(buf,
-                                                                                  get_rule_expr(qh, rt_index,
-                                                                                                                (Node *) lfirst(args),
-                                                                                                                varprefix));
+                                                                       appendStringInfo(buf, "%s ",
+                                                                                                        get_opname(opno));
+                                                                       get_rule_expr(buf, qh, rt_index,
+                                                                                                 (Node *) lfirst(args),
+                                                                                                 varprefix);
                                                                        break;
                                                                case 'r':
-                                                                       strcat(buf,
-                                                                                  get_rule_expr(qh, rt_index,
-                                                                                                                (Node *) lfirst(args),
-                                                                                                                varprefix));
-                                                                       strcat(buf, " ");
-                                                                       strcat(buf, get_opname(opno));
+                                                                       get_rule_expr(buf, qh, rt_index,
+                                                                                                 (Node *) lfirst(args),
+                                                                                                 varprefix);
+                                                                       appendStringInfo(buf, " %s",
+                                                                                                        get_opname(opno));
                                                                        break;
                                                                default:
                                                                        elog(ERROR, "get_rule_expr: bogus oprkind");
                                                        }
                                                }
-                                               strcat(buf, ")");
-                                               return pstrdup(buf);
+                                               appendStringInfo(buf, ")");
                                                break;
 
                                        case OR_EXPR:
-                                               strcat(buf, "(");
-                                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                                 (Node *) lfirst(args),
-                                                                                                 varprefix));
+                                               appendStringInfo(buf, "(");
+                                               get_rule_expr(buf, qh, rt_index,
+                                                                         (Node *) lfirst(args),
+                                                                         varprefix);
                                                /* It's not clear that we can ever see N-argument
                                                 * OR/AND clauses here, but might as well cope...
                                                 */
                                                while ((args = lnext(args)) != NIL)
                                                {
-                                                       strcat(buf, " OR ");
-                                                       strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                                         (Node *) lfirst(args),
-                                                                                                         varprefix));
+                                                       appendStringInfo(buf, " OR ");
+                                                       get_rule_expr(buf, qh, rt_index,
+                                                                                 (Node *) lfirst(args),
+                                                                                 varprefix);
                                                }
-                                               strcat(buf, ")");
-                                               return pstrdup(buf);
+                                               appendStringInfo(buf, ")");
                                                break;
 
                                        case AND_EXPR:
-                                               strcat(buf, "(");
-                                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                                 (Node *) lfirst(args),
-                                                                                                 varprefix));
+                                               appendStringInfo(buf, "(");
+                                               get_rule_expr(buf, qh, rt_index,
+                                                                         (Node *) lfirst(args),
+                                                                         varprefix);
                                                while ((args = lnext(args)) != NIL)
                                                {
-                                                       strcat(buf, " AND ");
-                                                       strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                                         (Node *) lfirst(args),
-                                                                                                         varprefix));
+                                                       appendStringInfo(buf, " AND ");
+                                                       get_rule_expr(buf, qh, rt_index,
+                                                                                 (Node *) lfirst(args),
+                                                                                 varprefix);
                                                }
-                                               strcat(buf, ")");
-                                               return pstrdup(buf);
+                                               appendStringInfo(buf, ")");
                                                break;
 
                                        case NOT_EXPR:
-                                               strcat(buf, "(NOT ");
-                                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                          (Node *) get_leftop(expr),
-                                                                                                 varprefix));
-                                               strcat(buf, ")");
-                                               return pstrdup(buf);
+                                               appendStringInfo(buf, "(NOT ");
+                                               get_rule_expr(buf, qh, rt_index,
+                                                                         (Node *) lfirst(args),
+                                                                         varprefix);
+                                               appendStringInfo(buf, ")");
                                                break;
 
                                        case FUNC_EXPR:
-                                               return get_func_expr(qh, rt_index,
-                                                                                        (Expr *) node,
-                                                                                        varprefix);
+                                               get_func_expr(buf, qh, rt_index,
+                                                                         (Expr *) node,
+                                                                         varprefix);
                                                break;
 
                                        default:
-                                               printf("\n%s\n", nodeToString(node));
-                                               elog(ERROR, "get_rule_expr: expr type not supported");
+                                               elog(ERROR, "get_rule_expr: expr opType %d not supported",
+                                                        expr->opType);
                                }
                        }
                        break;
@@ -1396,43 +1313,44 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
                        {
                                Aggref     *aggref = (Aggref *) node;
 
-                               strcat(buf, "\"");
-                               strcat(buf, aggref->aggname);
-                               strcat(buf, "\"(");
-                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                 (Node *) (aggref->target), varprefix));
-                               strcat(buf, ")");
-                               return pstrdup(buf);
+                               appendStringInfo(buf, "\"%s\"(", aggref->aggname);
+                               get_rule_expr(buf, qh, rt_index,
+                                                         (Node *) (aggref->target), varprefix);
+                               appendStringInfo(buf, ")");
                        }
                        break;
 
+               case T_Iter:
+                       get_rule_expr(buf, qh, rt_index,
+                                                 ((Iter *) node)->iterexpr, varprefix);
+                       break;
+
                case T_ArrayRef:
                        {
                                ArrayRef   *aref = (ArrayRef *) node;
                                List       *lowlist;
                                List       *uplist;
 
-                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                 aref->refexpr, varprefix));
+                               get_rule_expr(buf, qh, rt_index,
+                                                         aref->refexpr, varprefix);
                                lowlist = aref->reflowerindexpr;
                                foreach(uplist, aref->refupperindexpr)
                                {
-                                       strcat(buf, "[");
+                                       appendStringInfo(buf, "[");
                                        if (lowlist)
                                        {
-                                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                                 (Node *) lfirst(lowlist),
-                                                                                                 varprefix));
-                                               strcat(buf, ":");
+                                               get_rule_expr(buf, qh, rt_index,
+                                                                         (Node *) lfirst(lowlist),
+                                                                         varprefix);
+                                               appendStringInfo(buf, ":");
                                                lowlist = lnext(lowlist);
                                        }
-                                       strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                         (Node *) lfirst(uplist),
-                                                                                         varprefix));
-                                       strcat(buf, "]");
+                                       get_rule_expr(buf, qh, rt_index,
+                                                                 (Node *) lfirst(uplist),
+                                                                 varprefix);
+                                       appendStringInfo(buf, "]");
                                }
                                /* XXX need to do anything with refassgnexpr? */
-                               return pstrdup(buf);
                        }
                        break;
 
@@ -1441,28 +1359,27 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
                                CaseExpr   *caseexpr = (CaseExpr *) node;
                                List       *temp;
 
-                               strcat(buf, "CASE");
+                               appendStringInfo(buf, "CASE");
                                foreach(temp, caseexpr->args)
                                {
                                        CaseWhen   *when = (CaseWhen *) lfirst(temp);
 
-                                       strcat(buf, " WHEN ");
-                                       strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                         when->expr, varprefix));
-                                       strcat(buf, " THEN ");
-                                       strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                         when->result, varprefix));
+                                       appendStringInfo(buf, " WHEN ");
+                                       get_rule_expr(buf, qh, rt_index,
+                                                                 when->expr, varprefix);
+                                       appendStringInfo(buf, " THEN ");
+                                       get_rule_expr(buf, qh, rt_index,
+                                                                 when->result, varprefix);
                                }
-                               strcat(buf, " ELSE ");
-                               strcat(buf, get_rule_expr(qh, rt_index,
-                                                                                 caseexpr->defresult, varprefix));
-                               strcat(buf, " END");
-                               return pstrdup(buf);
+                               appendStringInfo(buf, " ELSE ");
+                               get_rule_expr(buf, qh, rt_index,
+                                                         caseexpr->defresult, varprefix);
+                               appendStringInfo(buf, " END");
                        }
                        break;
 
                case T_SubLink:
-                       return get_sublink_expr(qh, rt_index, node, varprefix);
+                       get_sublink_expr(buf, qh, rt_index, node, varprefix);
                        break;
 
                default:
@@ -1471,8 +1388,6 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
                                 rulename, nodeTag(node));
                        break;
        }
-
-       return FALSE;
 }
 
 
@@ -1480,10 +1395,10 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
  * get_func_expr                       - Parse back a Func node
  * ----------
  */
-static char *
-get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
+static void
+get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
+                         Expr *expr, bool varprefix)
 {
-       char            buf[BUFSIZE];
        HeapTuple       proctup;
        Form_pg_proc procStruct;
        List       *l;
@@ -1503,23 +1418,24 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
        procStruct = (Form_pg_proc) GETSTRUCT(proctup);
        proname = nameout(&(procStruct->proname));
 
+       /*
+        * nullvalue() and nonnullvalue() should get turned into special syntax
+        */
        if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
        {
                if (!strcmp(proname, "nullvalue"))
                {
-                       strcpy(buf, "(");
-                       strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
-                                                                         varprefix));
-                       strcat(buf, " ISNULL)");
-                       return pstrdup(buf);
+                       appendStringInfo(buf, "(");
+                       get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
+                       appendStringInfo(buf, " ISNULL)");
+                       return;
                }
                if (!strcmp(proname, "nonnullvalue"))
                {
-                       strcpy(buf, "(");
-                       strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
-                                                                         varprefix));
-                       strcat(buf, " NOTNULL)");
-                       return pstrdup(buf);
+                       appendStringInfo(buf, "(");
+                       get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
+                       appendStringInfo(buf, " NOTNULL)");
+                       return;
                }
        }
 
@@ -1527,40 +1443,31 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
         * Build a string of proname(args)
         * ----------
         */
-       strcpy(buf, "\"");
-       strcat(buf, proname);
-       strcat(buf, "\"(");
+       appendStringInfo(buf, "\"%s\"(", proname);
        sep = "";
        foreach(l, expr->args)
        {
-               strcat(buf, sep);
+               appendStringInfo(buf, sep);
                sep = ", ";
-               strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
+               get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix);
        }
-       strcat(buf, ")");
-
-       /* ----------
-        * Copy the function call string into allocated space and return it
-        * ----------
-        */
-       return pstrdup(buf);
+       appendStringInfo(buf, ")");
 }
 
 
 /* ----------
- * get_tle_expr                                - A target list expression is a bit
- *                                       different from a normal expression.
- *                                       If the target column has an
- *                                       an atttypmod, the parser usually
- *                                       puts a padding-/cut-function call
- *                                       around the expression itself. We
- *                                       we must get rid of it, otherwise
- *                                       dump/reload/dump... would blow up
- *                                       the expressions.
+ * get_tle_expr
+ *
+ *             A target list expression is a bit different from a normal expression.
+ *             If the target column has an atttypmod, the parser usually puts a
+ *             padding-/cut-function call around the expression itself.
+ *             We must get rid of it, otherwise dump/reload/dump... would blow up
+ *             the expressions.
  * ----------
  */
-static char *
-get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
+static void
+get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
+                        TargetEntry *tle, bool varprefix)
 {
        Expr       *expr = (Expr *) (tle->expr);
        Func       *func;
@@ -1577,7 +1484,10 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
        if (tle->resdom->restypmod < 0 ||
                ! IsA(expr, Expr) ||
                expr->opType != FUNC_EXPR)
-               return get_rule_expr(qh, rt_index, tle->expr, varprefix);
+       {
+               get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
+               return;
+       }
 
        func = (Func *) (expr->oper);
 
@@ -1600,7 +1510,10 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
        if (procStruct->pronargs != 2 ||
                procStruct->prorettype != procStruct->proargtypes[0] ||
                procStruct->proargtypes[1] != INT4OID)
-               return get_rule_expr(qh, rt_index, tle->expr, varprefix);
+       {
+               get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
+               return;
+       }
 
        /*
         * Furthermore, the name of the function must be the same
@@ -1615,47 +1528,57 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
        typeStruct = (Form_pg_type) GETSTRUCT(tup);
        if (strncmp(procStruct->proname.data, typeStruct->typname.data,
                                NAMEDATALEN) != 0)
-        return get_rule_expr(qh, rt_index, tle->expr, varprefix);
+       {
+               get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
+               return;
+       }
 
        /* ----------
         * Finally (to be totally safe) the second argument must be a
         * const and match the value in the results atttypmod.
         * ----------
         */
-       second_arg = (Const *) nth(1, expr->args);
+       second_arg = (Const *) lsecond(expr->args);
        if (! IsA(second_arg, Const) ||
-               ((int4) second_arg->constvalue) != tle->resdom->restypmod)
-               return get_rule_expr(qh, rt_index, tle->expr, varprefix);
+               DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod)
+       {
+               get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
+               return;
+       }
 
        /* ----------
         * Whow - got it. Now get rid of the padding function
         * ----------
         */
-       return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
+       get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
 }
 
 
 /* ----------
- * get_const_expr                      - Make a string representation
- *                                       with the type cast out of a Const
+ * get_const_expr
+ *
+ *     Make a string representation with the type cast out of a Const
  * ----------
  */
-static char *
-get_const_expr(Const *constval)
+static void
+get_const_expr(StringInfo buf, Const *constval)
 {
        HeapTuple       typetup;
        Form_pg_type typeStruct;
        FmgrInfo        finfo_output;
        char       *extval;
+       char       *valptr;
        bool            isnull = FALSE;
-       char            buf[BUFSIZE];
-       char            namebuf[64];
 
        if (constval->constisnull)
-               return pstrdup("NULL");
+       {
+               appendStringInfo(buf, "NULL");
+               return;
+       }
 
        typetup = SearchSysCacheTuple(TYPOID,
-                                                ObjectIdGetDatum(constval->consttype), 0, 0, 0);
+                                                                 ObjectIdGetDatum(constval->consttype),
+                                                                 0, 0, 0);
        if (!HeapTupleIsValid(typetup))
                elog(ERROR, "cache lookup of type %u failed", constval->consttype);
 
@@ -1665,11 +1588,34 @@ get_const_expr(Const *constval)
        extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
                                                                                                        &isnull, -1);
 
-       sprintf(namebuf, "::\"%s\"", nameout(&(typeStruct->typname)));
-       if (strcmp(namebuf, "::unknown") == 0)
-               namebuf[0] = '\0';
-       sprintf(buf, "'%s'%s", extval, namebuf);
-       return pstrdup(buf);
+       /*
+        * We must quote any funny characters in the constant's representation.
+        * XXX Any MULTIBYTE considerations here?
+        */
+       appendStringInfoChar(buf, '\'');
+       for (valptr = extval; *valptr; valptr++)
+       {
+               char    ch = *valptr;
+               if (ch == '\'' || ch == '\\')
+               {
+                       appendStringInfoChar(buf, '\\');
+                       appendStringInfoChar(buf, ch);
+               }
+               else if (ch >= 0 && ch < ' ')
+               {
+                       appendStringInfo(buf, "\\%03o", ch);
+               }
+               else
+                       appendStringInfoChar(buf, ch);
+       }
+       appendStringInfoChar(buf, '\'');
+       pfree(extval);
+
+       extval = (char *) nameout(&(typeStruct->typname));
+       /* probably would be better to recognize UNKNOWN by OID... */
+       if (strcmp(extval, "unknown") != 0)
+               appendStringInfo(buf, "::\"%s\"", extval);
+       pfree(extval);
 }
 
 
@@ -1677,75 +1623,67 @@ get_const_expr(Const *constval)
  * get_sublink_expr                    - Parse back a sublink
  * ----------
  */
-static char *
-get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
+static void
+get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
+                                Node *node, bool varprefix)
 {
        SubLink    *sublink = (SubLink *) node;
        Query      *query = (Query *) (sublink->subselect);
        Oper       *oper;
        List       *l;
        char       *sep;
-       char            buf[BUFSIZE];
-
-       buf[0] = '\0';
 
-       strcat(buf, "(");
+       appendStringInfo(buf, "(");
 
        if (sublink->lefthand != NULL)
        {
                if (length(sublink->lefthand) > 1)
-                       strcat(buf, "(");
+                       appendStringInfo(buf, "(");
 
                sep = "";
                foreach(l, sublink->lefthand)
                {
-                       strcat(buf, sep);
+                       appendStringInfo(buf, sep);
                        sep = ", ";
-                       strcat(buf, get_rule_expr(qh, rt_index,
-                                                                         lfirst(l), varprefix));
+                       get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix);
                }
 
                if (length(sublink->lefthand) > 1)
-                       strcat(buf, ") ");
+                       appendStringInfo(buf, ") ");
                else
-                       strcat(buf, " ");
+                       appendStringInfo(buf, " ");
        }
 
        switch (sublink->subLinkType)
        {
                case EXISTS_SUBLINK:
-                       strcat(buf, "EXISTS ");
+                       appendStringInfo(buf, "EXISTS ");
                        break;
 
                case ANY_SUBLINK:
                        oper = (Oper *) lfirst(sublink->oper);
-                       strcat(buf, get_opname(oper->opno));
-                       strcat(buf, " ANY ");
+                       appendStringInfo(buf, "%s ANY ", get_opname(oper->opno));
                        break;
 
                case ALL_SUBLINK:
                        oper = (Oper *) lfirst(sublink->oper);
-                       strcat(buf, get_opname(oper->opno));
-                       strcat(buf, " ALL ");
+                       appendStringInfo(buf, "%s ALL ", get_opname(oper->opno));
                        break;
 
                case EXPR_SUBLINK:
                        oper = (Oper *) lfirst(sublink->oper);
-                       strcat(buf, get_opname(oper->opno));
-                       strcat(buf, " ");
+                       appendStringInfo(buf, "%s ", get_opname(oper->opno));
                        break;
 
                default:
-                       elog(ERROR, "unupported sublink type %d",
+                       elog(ERROR, "get_sublink_expr: unsupported sublink type %d",
                                 sublink->subLinkType);
                        break;
        }
 
-       strcat(buf, "(");
-       strcat(buf, get_query_def(query, qh));
-       strcat(buf, "))");
-
-       return pstrdup(buf);
+       appendStringInfo(buf, "(");
+       get_query_def(buf, query, qh);
+       appendStringInfo(buf, "))");
 }
 
 
index 80b8b34..6e09822 100644 (file)
@@ -1091,34 +1091,34 @@ toyemp            |SELECT "emp"."name", "emp"."age", "emp"."location", ('12'::"i
 
 QUERY: SELECT tablename, rulename, definition FROM pg_rules
        ORDER BY tablename, rulename;
-tablename    |rulename       |definition                                                                                                                                                                                                                                                                          
--------------+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-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", "getpgusername"(), '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", "getpgusername"(), '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", "getpgusername"(), 'honored'::"bpchar", new."salary", old."salary");                 
-rtest_nothn1 |rtest_nothn_r1 |CREATE RULE "rtest_nothn_r1" AS ON INSERT TO "rtest_nothn1" WHERE ((new."a" >= '10'::"int4") AND (new."a" < '20'::"int4")) DO INSTEAD SELECT '1'::"int4";                                                                                                                           
-rtest_nothn1 |rtest_nothn_r2 |CREATE RULE "rtest_nothn_r2" AS ON INSERT TO "rtest_nothn1" WHERE ((new."a" >= '30'::"int4") AND (new."a" < '40'::"int4")) DO INSTEAD NOTHING;                                                                                                                                      
-rtest_nothn2 |rtest_nothn_r3 |CREATE RULE "rtest_nothn_r3" AS ON INSERT TO "rtest_nothn2" WHERE (new."a" >= '100'::"int4") DO INSTEAD INSERT INTO "rtest_nothn3" ("a", "b") VALUES (new."a", new."b");                                                                                                            
-rtest_nothn2 |rtest_nothn_r4 |CREATE RULE "rtest_nothn_r4" AS ON INSERT TO "rtest_nothn2" DO INSTEAD NOTHING;                                                                                                                                                                                                     
-rtest_order1 |rtest_order_r1 |CREATE RULE "rtest_order_r1" AS ON INSERT TO "rtest_order1" DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 1 - this should run 3rd or 4th'::"text");                                                                  
-rtest_order1 |rtest_order_r2 |CREATE RULE "rtest_order_r2" AS ON INSERT TO "rtest_order1" DO INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 2 - this should run 1st'::"text");                                                                                 
-rtest_order1 |rtest_order_r3 |CREATE RULE "rtest_order_r3" AS ON INSERT TO "rtest_order1" DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 3 - this should run 3rd or 4th'::"text");                                                                  
-rtest_order1 |rtest_order_r4 |CREATE RULE "rtest_order_r4" AS ON INSERT TO "rtest_order1" WHERE ("rtest_order2"."a" < '100'::"int4") DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 4 - this should run 2nd'::"text");                              
-rtest_person |rtest_pers_del |CREATE RULE "rtest_pers_del" AS ON DELETE TO "rtest_person" DO DELETE FROM "rtest_admin" WHERE ("rtest_admin"."pname" = old."pname");                                                                                                                                               
-rtest_person |rtest_pers_upd |CREATE RULE "rtest_pers_upd" AS ON UPDATE TO "rtest_person" DO UPDATE rtest_admin SET "pname" = new."pname" WHERE ("rtest_admin"."pname" = old."pname");                                                                                                                            
-rtest_system |rtest_sys_del  |CREATE RULE "rtest_sys_del" AS ON DELETE TO "rtest_system" DO (DELETE FROM "rtest_interface" WHERE ("rtest_interface"."sysname" = old."sysname"); DELETE FROM "rtest_admin" WHERE ("rtest_admin"."sysname" = old."sysname"); );                                                     
-rtest_system |rtest_sys_upd  |CREATE RULE "rtest_sys_upd" AS ON UPDATE TO "rtest_system" DO (UPDATE rtest_interface SET "sysname" = new."sysname" WHERE ("rtest_interface"."sysname" = old."sysname"); UPDATE rtest_admin SET "sysname" = new."sysname" WHERE ("rtest_admin"."sysname" = old."sysname"); );       
-rtest_t4     |rtest_t4_ins1  |CREATE RULE "rtest_t4_ins1" AS ON INSERT TO "rtest_t4" WHERE ((new."a" >= '10'::"int4") AND (new."a" < '20'::"int4")) DO INSTEAD INSERT INTO "rtest_t5" ("a", "b") VALUES (new."a", new."b");                                                                                       
-rtest_t4     |rtest_t4_ins2  |CREATE RULE "rtest_t4_ins2" AS ON INSERT TO "rtest_t4" WHERE ((new."a" >= '20'::"int4") AND (new."a" < '30'::"int4")) DO INSERT INTO "rtest_t6" ("a", "b") VALUES (new."a", new."b");                                                                                               
-rtest_t5     |rtest_t5_ins   |CREATE RULE "rtest_t5_ins" AS ON INSERT TO "rtest_t5" WHERE (new."a" > '15'::"int4") DO INSERT INTO "rtest_t7" ("a", "b") VALUES (new."a", new."b");                                                                                                                                
-rtest_t6     |rtest_t6_ins   |CREATE RULE "rtest_t6_ins" AS ON INSERT TO "rtest_t6" WHERE (new."a" > '25'::"int4") DO INSTEAD INSERT INTO "rtest_t8" ("a", "b") VALUES (new."a", new."b");                                                                                                                        
-rtest_v1     |rtest_v1_del   |CREATE RULE "rtest_v1_del" AS ON DELETE TO "rtest_v1" DO INSTEAD DELETE FROM "rtest_t1" WHERE ("rtest_t1"."a" = old."a");                                                                                                                                                           
-rtest_v1     |rtest_v1_ins   |CREATE RULE "rtest_v1_ins" AS ON INSERT TO "rtest_v1" DO INSTEAD INSERT INTO "rtest_t1" ("a", "b") VALUES (new."a", new."b");                                                                                                                                                       
-rtest_v1     |rtest_v1_upd   |CREATE RULE "rtest_v1_upd" AS ON UPDATE TO "rtest_v1" DO INSTEAD UPDATE rtest_t1 SET "a" = new."a", "b" = new."b" WHERE ("rtest_t1"."a" = old."a");                                                                                                                                 
-shoelace     |shoelace_del   |CREATE RULE "shoelace_del" AS ON DELETE TO "shoelace" DO INSTEAD DELETE FROM "shoelace_data" WHERE ("shoelace_data"."sl_name" = old."sl_name");                                                                                                                                     
-shoelace     |shoelace_ins   |CREATE RULE "shoelace_ins" AS ON INSERT TO "shoelace" DO INSTEAD INSERT INTO "shoelace_data" ("sl_name", "sl_avail", "sl_color", "sl_len", "sl_unit") VALUES (new."sl_name", new."sl_avail", new."sl_color", new."sl_len", new."sl_unit");                                          
-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", 'epoch'::"datetime");                   
-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");                                                                                      
+tablename    |rulename       |definition                                                                                                                                                                                                                                                                            
+-------------+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+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", "getpgusername"(), '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", "getpgusername"(), '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", "getpgusername"(), 'honored'::"bpchar", new."salary", old."salary");                   
+rtest_nothn1 |rtest_nothn_r1 |CREATE RULE "rtest_nothn_r1" AS ON INSERT TO "rtest_nothn1" WHERE ((new."a" >= '10'::"int4") AND (new."a" < '20'::"int4")) DO INSTEAD SELECT '1'::"int4";                                                                                                                             
+rtest_nothn1 |rtest_nothn_r2 |CREATE RULE "rtest_nothn_r2" AS ON INSERT TO "rtest_nothn1" WHERE ((new."a" >= '30'::"int4") AND (new."a" < '40'::"int4")) DO INSTEAD NOTHING;                                                                                                                                        
+rtest_nothn2 |rtest_nothn_r3 |CREATE RULE "rtest_nothn_r3" AS ON INSERT TO "rtest_nothn2" WHERE (new."a" >= '100'::"int4") DO INSTEAD INSERT INTO "rtest_nothn3" ("a", "b") VALUES (new."a", new."b");                                                                                                              
+rtest_nothn2 |rtest_nothn_r4 |CREATE RULE "rtest_nothn_r4" AS ON INSERT TO "rtest_nothn2" DO INSTEAD NOTHING;                                                                                                                                                                                                       
+rtest_order1 |rtest_order_r1 |CREATE RULE "rtest_order_r1" AS ON INSERT TO "rtest_order1" DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 1 - this should run 3rd or 4th'::"text");                                                                    
+rtest_order1 |rtest_order_r2 |CREATE RULE "rtest_order_r2" AS ON INSERT TO "rtest_order1" DO INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 2 - this should run 1st'::"text");                                                                                   
+rtest_order1 |rtest_order_r3 |CREATE RULE "rtest_order_r3" AS ON INSERT TO "rtest_order1" DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 3 - this should run 3rd or 4th'::"text");                                                                    
+rtest_order1 |rtest_order_r4 |CREATE RULE "rtest_order_r4" AS ON INSERT TO "rtest_order1" WHERE ("rtest_order2"."a" < '100'::"int4") DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 4 - this should run 2nd'::"text");                                
+rtest_person |rtest_pers_del |CREATE RULE "rtest_pers_del" AS ON DELETE TO "rtest_person" DO DELETE FROM "rtest_admin" WHERE ("rtest_admin"."pname" = old."pname");                                                                                                                                                 
+rtest_person |rtest_pers_upd |CREATE RULE "rtest_pers_upd" AS ON UPDATE TO "rtest_person" DO UPDATE "rtest_admin" SET "pname" = new."pname" WHERE ("rtest_admin"."pname" = old."pname");                                                                                                                            
+rtest_system |rtest_sys_del  |CREATE RULE "rtest_sys_del" AS ON DELETE TO "rtest_system" DO (DELETE FROM "rtest_interface" WHERE ("rtest_interface"."sysname" = old."sysname"); DELETE FROM "rtest_admin" WHERE ("rtest_admin"."sysname" = old."sysname"); );                                                       
+rtest_system |rtest_sys_upd  |CREATE RULE "rtest_sys_upd" AS ON UPDATE TO "rtest_system" DO (UPDATE "rtest_interface" SET "sysname" = new."sysname" WHERE ("rtest_interface"."sysname" = old."sysname"); UPDATE "rtest_admin" SET "sysname" = new."sysname" WHERE ("rtest_admin"."sysname" = old."sysname"); );     
+rtest_t4     |rtest_t4_ins1  |CREATE RULE "rtest_t4_ins1" AS ON INSERT TO "rtest_t4" WHERE ((new."a" >= '10'::"int4") AND (new."a" < '20'::"int4")) DO INSTEAD INSERT INTO "rtest_t5" ("a", "b") VALUES (new."a", new."b");                                                                                         
+rtest_t4     |rtest_t4_ins2  |CREATE RULE "rtest_t4_ins2" AS ON INSERT TO "rtest_t4" WHERE ((new."a" >= '20'::"int4") AND (new."a" < '30'::"int4")) DO INSERT INTO "rtest_t6" ("a", "b") VALUES (new."a", new."b");                                                                                                 
+rtest_t5     |rtest_t5_ins   |CREATE RULE "rtest_t5_ins" AS ON INSERT TO "rtest_t5" WHERE (new."a" > '15'::"int4") DO INSERT INTO "rtest_t7" ("a", "b") VALUES (new."a", new."b");                                                                                                                                  
+rtest_t6     |rtest_t6_ins   |CREATE RULE "rtest_t6_ins" AS ON INSERT TO "rtest_t6" WHERE (new."a" > '25'::"int4") DO INSTEAD INSERT INTO "rtest_t8" ("a", "b") VALUES (new."a", new."b");                                                                                                                          
+rtest_v1     |rtest_v1_del   |CREATE RULE "rtest_v1_del" AS ON DELETE TO "rtest_v1" DO INSTEAD DELETE FROM "rtest_t1" WHERE ("rtest_t1"."a" = old."a");                                                                                                                                                             
+rtest_v1     |rtest_v1_ins   |CREATE RULE "rtest_v1_ins" AS ON INSERT TO "rtest_v1" DO INSTEAD INSERT INTO "rtest_t1" ("a", "b") VALUES (new."a", new."b");                                                                                                                                                         
+rtest_v1     |rtest_v1_upd   |CREATE RULE "rtest_v1_upd" AS ON UPDATE TO "rtest_v1" DO INSTEAD UPDATE "rtest_t1" SET "a" = new."a", "b" = new."b" WHERE ("rtest_t1"."a" = old."a");                                                                                                                                 
+shoelace     |shoelace_del   |CREATE RULE "shoelace_del" AS ON DELETE TO "shoelace" DO INSTEAD DELETE FROM "shoelace_data" WHERE ("shoelace_data"."sl_name" = old."sl_name");                                                                                                                                       
+shoelace     |shoelace_ins   |CREATE RULE "shoelace_ins" AS ON INSERT TO "shoelace" DO INSTEAD INSERT INTO "shoelace_data" ("sl_name", "sl_avail", "sl_color", "sl_len", "sl_unit") VALUES (new."sl_name", new."sl_avail", new."sl_color", new."sl_len", new."sl_unit");                                            
+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", 'epoch'::"datetime");                     
+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)