OSDN Git Service

Revise quoting conventions in outfuncs/readfuncs so that nodeRead doesn't
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 14 Jan 2000 00:53:21 +0000 (00:53 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 14 Jan 2000 00:53:21 +0000 (00:53 +0000)
choke on relation or attribute names containing spaces, quotes, or other
special characters.  This fixes a TODO item.  It also forces initdb,
since stored rule strings change.

src/backend/nodes/outfuncs.c
src/backend/nodes/read.c
src/backend/nodes/readfuncs.c
src/include/nodes/readfuncs.h

index 1c8754f..2bf112e 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: outfuncs.c,v 1.101 2000/01/09 00:26:23 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.102 2000/01/14 00:53:21 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
  *       representation plus some other information (string length, etc.)
  *
  */
+#include <ctype.h>
 
 #include "postgres.h"
+
 #include "access/heapam.h"
 #include "access/htup.h"
 #include "catalog/pg_type.h"
 static void _outDatum(StringInfo str, Datum value, Oid type);
 static void _outNode(StringInfo str, void *obj);
 
-/* Convert a null string pointer into "<>" */
-#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
-
+/*
+ * _outToken
+ *       Convert an ordinary string (eg, an identifier) into a form that
+ *       will be decoded back to a plain token by read.c's functions.
+ *
+ *       If a null or empty string is given, it is encoded as "<>".
+ */
+static void
+_outToken(StringInfo str, char *s)
+{
+       if (s == NULL || *s == '\0')
+       {
+               appendStringInfo(str, "<>");
+               return;
+       }
+       /*
+        * Look for characters or patterns that are treated specially by
+        * read.c (either in lsptok() or in nodeRead()), and therefore need
+        * a protective backslash.
+        */
+       /* These characters only need to be quoted at the start of the string */
+       if (*s == '<' ||
+               *s == '\"' ||
+               *s == '@' ||
+               isdigit(*s) ||
+               (*s == '-' && isdigit(s[1])))
+               appendStringInfoChar(str, '\\');
+       while (*s)
+       {
+               /* These chars must be backslashed anywhere in the string */
+               if (*s == ' ' || *s == '\n' || *s == '\t' ||
+                       *s == '(' || *s == ')' || *s == '{' || *s == '}' ||
+                       *s == '\\')
+                       appendStringInfoChar(str, '\\');
+               appendStringInfoChar(str, *s++);
+       }
+}
 
 /*
  * _outIntList -
@@ -55,17 +91,17 @@ _outIntList(StringInfo str, List *list)
 {
        List       *l;
 
-       appendStringInfo(str, "(");
+       appendStringInfoChar(str, '(');
        foreach(l, list)
-               appendStringInfo(str, " %d ", lfirsti(l));
-       appendStringInfo(str, ")");
+               appendStringInfo(str, " %d", lfirsti(l));
+       appendStringInfoChar(str, ')');
 }
 
 static void
 _outCreateStmt(StringInfo str, CreateStmt *node)
 {
-       appendStringInfo(str, " CREATE :relname %s ",
-                                        stringStringInfo(node->relname));
+       appendStringInfo(str, " CREATE :relname ");
+       _outToken(str, node->relname);
 
        appendStringInfo(str, " :istemp %s ",
                                         node->istemp ? "true" : "false");
@@ -83,11 +119,13 @@ _outCreateStmt(StringInfo str, CreateStmt *node)
 static void
 _outIndexStmt(StringInfo str, IndexStmt *node)
 {
-       appendStringInfo(str,
-                " INDEX :idxname %s :relname %s :accessMethod %s :indexParams ",
-                                        stringStringInfo(node->idxname),
-                                        stringStringInfo(node->relname),
-                                        stringStringInfo(node->accessMethod));
+       appendStringInfo(str, " INDEX :idxname ");
+       _outToken(str, node->idxname);
+       appendStringInfo(str, " :relname ");
+       _outToken(str, node->relname);
+       appendStringInfo(str, " :accessMethod ");
+       _outToken(str, node->accessMethod);
+       appendStringInfo(str, " :indexParams ");
        _outNode(str, node->indexParams);
 
        appendStringInfo(str, " :withClause ");
@@ -114,8 +152,9 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
 static void
 _outFuncCall(StringInfo str, FuncCall *node)
 {
-       appendStringInfo(str, "FUNCTION %s :args ",
-                                        stringStringInfo(node->funcname));
+       appendStringInfo(str, "FUNCTION ");
+       _outToken(str, node->funcname);
+       appendStringInfo(str, " :args ");
        _outNode(str, node->args);
        appendStringInfo(str, " :agg_star %s :agg_distinct %s ",
                                         node->agg_star ? "true" : "false",
@@ -125,40 +164,42 @@ _outFuncCall(StringInfo str, FuncCall *node)
 static void
 _outColumnDef(StringInfo str, ColumnDef *node)
 {
-       appendStringInfo(str, " COLUMNDEF :colname %s :typename ",
-                                        stringStringInfo(node->colname));
+       appendStringInfo(str, " COLUMNDEF :colname ");
+       _outToken(str, node->colname);
+       appendStringInfo(str, " :typename ");
        _outNode(str, node->typename);
        appendStringInfo(str, " :is_not_null %s :is_sequence %s :raw_default ",
                                         node->is_not_null ? "true" : "false",
                                         node->is_sequence ? "true" : "false");
        _outNode(str, node->raw_default);
-       appendStringInfo(str, " :cooked_default %s :constraints ",
-                                        stringStringInfo(node->cooked_default));
+       appendStringInfo(str, " :cooked_default ");
+       _outToken(str, node->cooked_default);
+       appendStringInfo(str, " :constraints ");
        _outNode(str, node->constraints);
 }
 
 static void
 _outTypeName(StringInfo str, TypeName *node)
 {
-       appendStringInfo(str,
-        " TYPENAME :name %s :timezone %s :setof %s typmod %d :arrayBounds ",
-                                        stringStringInfo(node->name),
+       appendStringInfo(str, " TYPENAME :name ");
+       _outToken(str, node->name);
+       appendStringInfo(str, " :timezone %s :setof %s typmod %d :arrayBounds ",
                                         node->timezone ? "true" : "false",
                                         node->setof ? "true" : "false",
                                         node->typmod);
-
-       appendStringInfo(str, " :arrayBounds ");
        _outNode(str, node->arrayBounds);
 }
 
 static void
 _outIndexElem(StringInfo str, IndexElem *node)
 {
-       appendStringInfo(str, " INDEXELEM :name %s :args ",
-                                        stringStringInfo(node->name));
+       appendStringInfo(str, " INDEXELEM :name ");
+       _outToken(str, node->name);
+       appendStringInfo(str, " :args ");
        _outNode(str, node->args);
-
-       appendStringInfo(str, " :class %s :typename ", stringStringInfo(node->class));
+       appendStringInfo(str, " :class ");
+       _outToken(str, node->class);
+       appendStringInfo(str, " :typename ");
        _outNode(str, node->typename);
 }
 
@@ -173,21 +214,24 @@ _outQuery(StringInfo str, Query *node)
                switch (nodeTag(node->utilityStmt))
                {
                        case T_CreateStmt:
-                               appendStringInfo(str, " :create %s ",
-                                                                stringStringInfo(((CreateStmt *) (node->utilityStmt))->relname));
+                               appendStringInfo(str, " :create ");
+                               _outToken(str, ((CreateStmt *) (node->utilityStmt))->relname);
+                               appendStringInfo(str, " ");
                                _outNode(str, node->utilityStmt);
                                break;
 
                        case T_IndexStmt:
-                               appendStringInfo(str, " :index %s on %s ",
-                                                                stringStringInfo(((IndexStmt *) (node->utilityStmt))->idxname),
-                                                                stringStringInfo(((IndexStmt *) (node->utilityStmt))->relname));
+                               appendStringInfo(str, " :index ");
+                               _outToken(str, ((IndexStmt *) (node->utilityStmt))->idxname);
+                               appendStringInfo(str, " on ");
+                               _outToken(str, ((IndexStmt *) (node->utilityStmt))->relname);
+                               appendStringInfo(str, " ");
                                _outNode(str, node->utilityStmt);
                                break;
 
                        case T_NotifyStmt:
-                               appendStringInfo(str, " :utility %s ",
-                                                                stringStringInfo(((NotifyStmt *) (node->utilityStmt))->relname));
+                               appendStringInfo(str, " :utility ");
+                               _outToken(str, ((NotifyStmt *) (node->utilityStmt))->relname);
                                break;
 
                        default:
@@ -197,17 +241,18 @@ _outQuery(StringInfo str, Query *node)
        else
                appendStringInfo(str, " :utility <>");
 
+       appendStringInfo(str, " :resultRelation %u :into ",
+                                        node->resultRelation);
+       _outToken(str, node->into);
+
        appendStringInfo(str,
-                                        " :resultRelation %u :into %s :isPortal %s :isBinary %s :isTemp %s :unionall %s ",
-                                        node->resultRelation,
-                                        stringStringInfo(node->into),
+                                        " :isPortal %s :isBinary %s :isTemp %s :unionall %s :unique ",
                                         node->isPortal ? "true" : "false",
                                         node->isBinary ? "true" : "false",
                                         node->isTemp ? "true" : "false",
                                         node->unionall ? "true" : "false");
-
-       appendStringInfo(str, " :unique %s :sortClause ",
-                                        stringStringInfo(node->uniqueFlag));
+       _outToken(str, node->uniqueFlag);
+       appendStringInfo(str, " :sortClause ");
        _outNode(str, node->sortClause);
 
        appendStringInfo(str, " :rtable ");
@@ -560,17 +605,15 @@ _outHash(StringInfo str, Hash *node)
 static void
 _outResdom(StringInfo str, Resdom *node)
 {
-       appendStringInfo(str, " RESDOM :resno %d :restype %u :restypmod %d",
+       appendStringInfo(str,
+                                        " RESDOM :resno %d :restype %u :restypmod %d :resname ",
                                         node->resno,
                                         node->restype,
                                         node->restypmod);
-
-       appendStringInfo(str, " :resname \"%s\" :reskey %d :reskeyop %u",
-                                        stringStringInfo(node->resname),
+       _outToken(str, node->resname);
+       appendStringInfo(str, " :reskey %d :reskeyop %u :ressortgroupref %d :resjunk %s ",
                                         node->reskey,
-                                        node->reskeyop);
-
-       appendStringInfo(str, " :ressortgroupref %d :resjunk %s ",
+                                        node->reskeyop,
                                         node->ressortgroupref,
                                         node->resjunk ? "true" : "false");
 }
@@ -626,7 +669,9 @@ _outExpr(StringInfo str, Expr *node)
                        opstr = "subp";
                        break;
        }
-       appendStringInfo(str, " :opType %s :oper ", stringStringInfo(opstr));
+       appendStringInfo(str, " :opType ");
+       _outToken(str, opstr);
+       appendStringInfo(str, " :oper ");
        _outNode(str, node->oper);
 
        appendStringInfo(str, " :args ");
@@ -679,9 +724,9 @@ _outConst(StringInfo str, Const *node)
 static void
 _outAggref(StringInfo str, Aggref *node)
 {
-       appendStringInfo(str,
-                                        " AGGREG :aggname %s :basetype %u :aggtype %u :target ",
-                                        stringStringInfo(node->aggname),
+       appendStringInfo(str, " AGGREG :aggname ");
+       _outToken(str, node->aggname);
+       appendStringInfo(str, " :basetype %u :aggtype %u :target ",
                                         node->basetype,
                                         node->aggtype);
        _outNode(str, node->target);
@@ -801,14 +846,12 @@ _outOper(StringInfo str, Oper *node)
 static void
 _outParam(StringInfo str, Param *node)
 {
-       appendStringInfo(str,
-                " PARAM :paramkind %d :paramid %d :paramname %s :paramtype %u ",
+       appendStringInfo(str, " PARAM :paramkind %d :paramid %d :paramname ",
                                         node->paramkind,
-                                        node->paramid,
-                                        stringStringInfo(node->paramname),
+                                        node->paramid);
+       _outToken(str, node->paramname);
+       appendStringInfo(str, " :paramtype %u :param_tlist ",
                                         node->paramtype);
-
-       appendStringInfo(str, " :param_tlist ");
        _outNode(str, node->param_tlist);
 }
 
@@ -897,10 +940,12 @@ _outTargetEntry(StringInfo str, TargetEntry *node)
 static void
 _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 {
+       appendStringInfo(str, " RTE :relname ");
+       _outToken(str, node->relname);
+       appendStringInfo(str, " :refname ");
+       _outToken(str, node->refname);
        appendStringInfo(str,
-                                        " RTE :relname %s :refname %s :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s",
-                                        stringStringInfo(node->relname),
-                                        stringStringInfo(node->refname),
+                                        " :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s",
                                         node->relid,
                                         node->inh ? "true" : "false",
                                         node->inFromCl ? "true" : "false",
@@ -1115,7 +1160,7 @@ _outDatum(StringInfo str, Datum value, Oid type)
                s = (char *) (&value);
                appendStringInfo(str, " %d [ ", length);
                for (i = 0; i < sizeof(Datum); i++)
-                       appendStringInfo(str, " %d ", (int) (s[i]));
+                       appendStringInfo(str, "%d ", (int) (s[i]));
                appendStringInfo(str, "] ");
        }
        else
@@ -1134,7 +1179,7 @@ _outDatum(StringInfo str, Datum value, Oid type)
                                length = VARSIZE(s);
                        appendStringInfo(str, " %d [ ", length);
                        for (i = 0; i < length; i++)
-                               appendStringInfo(str, " %d ", (int) (s[i]));
+                               appendStringInfo(str, "%d ", (int) (s[i]));
                        appendStringInfo(str, "] ");
                }
        }
@@ -1172,27 +1217,27 @@ _outAExpr(StringInfo str, A_Expr *node)
        switch (node->oper)
        {
                case AND:
-                       appendStringInfo(str, "AND");
+                       appendStringInfo(str, "AND ");
                        break;
                case OR:
-                       appendStringInfo(str, "OR");
+                       appendStringInfo(str, "OR ");
                        break;
                case NOT:
-                       appendStringInfo(str, "NOT");
+                       appendStringInfo(str, "NOT ");
                        break;
                case ISNULL:
-                       appendStringInfo(str, "ISNULL");
+                       appendStringInfo(str, "ISNULL ");
                        break;
                case NOTNULL:
-                       appendStringInfo(str, "NOTNULL");
+                       appendStringInfo(str, "NOTNULL ");
                        break;
                default:
-                       appendStringInfo(str, stringStringInfo(node->opname));
+                       _outToken(str, node->opname);
+                       appendStringInfo(str, " ");
                        break;
        }
        _outNode(str, node->lexpr);
        _outNode(str, node->rexpr);
-       return;
 }
 
 static void
@@ -1200,26 +1245,29 @@ _outValue(StringInfo str, Value *value)
 {
        switch (value->type)
        {
-                       case T_String:
-                       appendStringInfo(str, " \"%s\" ", stringStringInfo(value->val.str));
+               case T_String:
+                       appendStringInfo(str, " \"");
+                       _outToken(str, value->val.str);
+                       appendStringInfo(str, "\" ");
                        break;
                case T_Integer:
                        appendStringInfo(str, " %ld ", value->val.ival);
                        break;
                case T_Float:
-                       appendStringInfo(str, " %f ", value->val.dval);
+                       appendStringInfo(str, " %.17g ", value->val.dval);
                        break;
                default:
+                       elog(NOTICE, "_outValue: don't know how to print type %d ",
+                                value->type);
                        break;
        }
-       return;
 }
 
 static void
 _outIdent(StringInfo str, Ident *node)
 {
-       appendStringInfo(str, " IDENT \"%s\" ", stringStringInfo(node->name));
-       return;
+       appendStringInfo(str, " IDENT ");
+       _outToken(str, node->name);
 }
 
 static void
@@ -1227,17 +1275,16 @@ _outAttr(StringInfo str, Attr *node)
 {
        List       *l;
 
-       appendStringInfo(str, " ATTR \"%s\" ", stringStringInfo(node->relname));
-
-       appendStringInfo(str, "(");
+       appendStringInfo(str, " ATTR ");
+       _outToken(str, node->relname);
+       appendStringInfo(str, " (");
        foreach(l, node->attrs)
        {
                _outNode(str, lfirst(l));
                if (lnext(l))
-                       appendStringInfo(str, ",");
+                       appendStringInfo(str, " ");
        }
        appendStringInfo(str, ")");
-       return;
 }
 
 static void
@@ -1245,46 +1292,47 @@ _outAConst(StringInfo str, A_Const *node)
 {
        appendStringInfo(str, "CONST ");
        _outValue(str, &(node->val));
-       return;
 }
 
 static void
 _outConstraint(StringInfo str, Constraint *node)
 {
-       appendStringInfo(str, " %s :type", stringStringInfo(node->name));
+       appendStringInfo(str, " ");
+       _outToken(str, node->name);
+       appendStringInfo(str, " :type ");
 
        switch (node->contype)
        {
                case CONSTR_PRIMARY:
-                       appendStringInfo(str, " PRIMARY KEY ");
+                       appendStringInfo(str, "PRIMARY KEY ");
                        _outNode(str, node->keys);
                        break;
 
                case CONSTR_CHECK:
-                       appendStringInfo(str, " CHECK :raw ");
+                       appendStringInfo(str, "CHECK :raw ");
                        _outNode(str, node->raw_expr);
-                       appendStringInfo(str, " :cooked %s ",
-                                                        stringStringInfo(node->cooked_expr));
+                       appendStringInfo(str, " :cooked ");
+                       _outToken(str, node->cooked_expr);
                        break;
 
                case CONSTR_DEFAULT:
-                       appendStringInfo(str, " DEFAULT :raw ");
+                       appendStringInfo(str, "DEFAULT :raw ");
                        _outNode(str, node->raw_expr);
-                       appendStringInfo(str, " :cooked %s ",
-                                                        stringStringInfo(node->cooked_expr));
+                       appendStringInfo(str, " :cooked ");
+                       _outToken(str, node->cooked_expr);
                        break;
 
                case CONSTR_NOTNULL:
-                       appendStringInfo(str, " NOT NULL ");
+                       appendStringInfo(str, "NOT NULL");
                        break;
 
                case CONSTR_UNIQUE:
-                       appendStringInfo(str, " UNIQUE ");
+                       appendStringInfo(str, "UNIQUE ");
                        _outNode(str, node->keys);
                        break;
 
                default:
-                       appendStringInfo(str, "<unrecognized constraint>");
+                       appendStringInfo(str, "<unrecognized_constraint>");
                        break;
        }
 }
@@ -1297,8 +1345,6 @@ _outCaseExpr(StringInfo str, CaseExpr *node)
 
        appendStringInfo(str, " :default ");
        _outNode(str, node->defresult);
-
-       return;
 }
 
 static void
@@ -1309,8 +1355,6 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
 
        appendStringInfo(str, " :then ");
        _outNode(str, node->result);
-
-       return;
 }
 
 /*
@@ -1330,18 +1374,18 @@ _outNode(StringInfo str, void *obj)
        {
                List       *l;
 
-               appendStringInfo(str, "(");
+               appendStringInfoChar(str, '(');
                foreach(l, (List *) obj)
                {
                        _outNode(str, lfirst(l));
                        if (lnext(l))
-                               appendStringInfo(str, " ");
+                               appendStringInfoChar(str, ' ');
                }
-               appendStringInfo(str, ")");
+               appendStringInfoChar(str, ')');
        }
        else
        {
-               appendStringInfo(str, "{");
+               appendStringInfoChar(str, '{');
                switch (nodeTag(obj))
                {
                        case T_CreateStmt:
@@ -1350,7 +1394,6 @@ _outNode(StringInfo str, void *obj)
                        case T_IndexStmt:
                                _outIndexStmt(str, obj);
                                break;
-
                        case T_ColumnDef:
                                _outColumnDef(str, obj);
                                break;
@@ -1551,9 +1594,8 @@ _outNode(StringInfo str, void *obj)
                                         nodeTag(obj));
                                break;
                }
-               appendStringInfo(str, "}");
+               appendStringInfoChar(str, '}');
        }
-       return;
 }
 
 /*
index e80385e..93fedf3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.18 1999/07/17 20:17:08 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.19 2000/01/14 00:53:21 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -19,6 +19,7 @@
 #include <ctype.h>
 
 #include "postgres.h"
+
 #include "nodes/pg_list.h"
 #include "nodes/readfuncs.h"
 
@@ -43,6 +44,134 @@ stringToNode(char *str)
  *
  *****************************************************************************/
 
+/*
+ * lsptok --- retrieve next "token" from a string.
+ *
+ * Works kinda like strtok, except it never modifies the source string.
+ * (Instead of storing nulls into the string, the length of the token
+ * is returned to the caller.)
+ * Also, the rules about what is a token are hard-wired rather than being
+ * configured by passing a set of terminating characters.
+ *
+ * The string is initially set by passing a non-NULL "string" value,
+ * and subsequent calls with string==NULL read the previously given value.
+ * (Pass length==NULL to set the string without reading its first token.)
+ *
+ * The rules for tokens are:
+ *  * Whitespace (space, tab, newline) always separates tokens.
+ *  * The characters '(', ')', '{', '}' form individual tokens even
+ *    without any whitespace around them.
+ *  * Otherwise, a token is all the characters up to the next whitespace
+ *    or occurrence of one of the four special characters.
+ *  * A backslash '\' can be used to quote whitespace or one of the four
+ *    special characters, so that it is treated as a plain token character.
+ *    Backslashes themselves must also be backslashed for consistency.
+ *    Any other character can be, but need not be, backslashed as well.
+ *  * If the resulting token is '<>' (with no backslash), it is returned
+ *    as a non-NULL pointer to the token but with length == 0.  Note that
+ *    there is no other way to get a zero-length token.
+ *
+ * Returns a pointer to the start of the next token, and the length of the
+ * token (including any embedded backslashes!) in *length.  If there are
+ * no more tokens, NULL and 0 are returned.
+ *
+ * NOTE: this routine doesn't remove backslashes; the caller must do so
+ * if necessary (see "debackslash").
+ *
+ * NOTE: prior to release 7.0, this routine also had a special case to treat
+ * a token starting with '"' as extending to the next '"'.  This code was
+ * broken, however, since it would fail to cope with a string containing an
+ * embedded '"'.  I have therefore removed this special case, and instead
+ * introduced rules for using backslashes to quote characters.  Higher-level
+ * code should add backslashes to a string constant to ensure it is treated
+ * as a single token.
+ */
+char *
+lsptok(char *string, int *length)
+{
+       static char *saved_str = NULL;
+       char       *local_str;          /* working pointer to string */
+       char       *ret_str;            /* start of token to return */
+
+       if (string != NULL)
+       {
+               saved_str = string;
+               if (length == NULL)
+                       return NULL;
+       }
+
+       local_str = saved_str;
+
+       while (*local_str == ' ' || *local_str == '\n' || *local_str == '\t')
+               local_str++;
+
+       if (*local_str == '\0')
+       {
+               *length = 0;
+               saved_str = local_str;
+               return NULL;                    /* no more tokens */
+       }
+
+       /*
+        * Now pointing at start of next token.
+        */
+       ret_str = local_str;
+
+       if (*local_str == '(' || *local_str == ')' ||
+               *local_str == '{' || *local_str == '}')
+       {
+               /* special 1-character token */
+               local_str++;
+       }
+       else
+       {
+               /* Normal token, possibly containing backslashes */
+               while (*local_str != '\0' &&
+                          *local_str != ' ' && *local_str != '\n' &&
+                          *local_str != '\t' &&
+                          *local_str != '(' && *local_str != ')' &&
+                          *local_str != '{' && *local_str != '}')
+               {
+                       if (*local_str == '\\' && local_str[1] != '\0')
+                               local_str += 2;
+                       else
+                               local_str++;
+               }
+       }
+
+       *length = local_str - ret_str;
+
+       /* Recognize special case for "empty" token */
+       if (*length == 2 && ret_str[0] == '<' && ret_str[1] == '>')
+               *length = 0;
+
+       saved_str = local_str;
+
+       return ret_str;
+}
+
+/*
+ * debackslash -
+ *       create a palloc'd string holding the given token.
+ *       any protective backslashes in the token are removed.
+ */
+char *
+debackslash(char *token, int length)
+{
+       char   *result = palloc(length+1);
+       char   *ptr = result;
+
+       while (length > 0)
+       {
+               if (*token == '\\' && length > 1)
+                       token++, length--;
+               *ptr++ = *token++;
+               length--;
+       }
+       *ptr = '\0';
+       return result;
+}
+
 #define RIGHT_PAREN (1000000 + 1)
 #define LEFT_PAREN     (1000000 + 2)
 #define PLAN_SYM       (1000000 + 3)
@@ -62,127 +191,72 @@ stringToNode(char *str)
 static NodeTag
 nodeTokenType(char *token, int length)
 {
-       NodeTag         retval = 0;
+       NodeTag         retval;
 
        /*
         * Check if the token is a number (decimal or integer, positive or
-        * negative
+        * negative)
         */
        if (isdigit(*token) ||
-               (length >= 2 && *token == '-' && isdigit(*(token + 1))))
+               (length >= 2 && *token == '-' && isdigit(token[1])))
        {
-
                /*
                 * skip the optional '-' (i.e. negative number)
                 */
                if (*token == '-')
-                       token++;
+                       token++, length--;
 
                /*
                 * See if there is a decimal point
                 */
-
-               for (; length && *token != '.'; token++, length--);
+               while (length > 0 && *token != '.')
+                       token++, length--;
 
                /*
                 * if there isn't, token's an int, otherwise it's a float.
                 */
-
                retval = (*token != '.') ? T_Integer : T_Float;
        }
-       else if (isalpha(*token) || *token == '_' ||
-                        (token[0] == '<' && token[1] == '>'))
-               retval = ATOM_TOKEN;
+       /*
+        * these three cases do not need length checks, since lsptok()
+        * will always treat them as single-byte tokens
+        */
        else if (*token == '(')
                retval = LEFT_PAREN;
        else if (*token == ')')
                retval = RIGHT_PAREN;
-       else if (*token == '@')
-               retval = AT_SYMBOL;
-       else if (*token == '\"')
-               retval = T_String;
        else if (*token == '{')
                retval = PLAN_SYM;
+       else if (*token == '@' && length == 1)
+               retval = AT_SYMBOL;
+       else if (*token == '\"' && length > 1 && token[length-1] == '\"')
+               retval = T_String;
+       else
+               retval = ATOM_TOKEN;
        return retval;
 }
 
 /*
- * Works kinda like strtok, except it doesn't put nulls into string.
- *
- * Returns the length in length instead.  The string can be set without
- * returning a token by calling lsptok with length == NULL.
+ * nodeRead -
+ *       Slightly higher-level reader.
  *
- */
-char *
-lsptok(char *string, int *length)
-{
-       static char *local_str;
-       char       *ret_string;
-
-       if (string != NULL)
-       {
-               local_str = string;
-               if (length == NULL)
-                       return NULL;
-       }
-
-       for (; *local_str == ' '
-                || *local_str == '\n'
-                || *local_str == '\t'; local_str++);
-
-       /*
-        * Now pointing at next token.
-        */
-       ret_string = local_str;
-       if (*local_str == '\0')
-               return NULL;
-       *length = 1;
-
-       if (*local_str == '"')
-       {
-               for (local_str++; *local_str != '"'; (*length)++, local_str++)
-                       ;
-               (*length)++;
-               local_str++;
-       }
-       /* NULL */
-       else if (local_str[0] == '<' && local_str[1] == '>')
-       {
-               *length = 0;
-               local_str += 2;
-       }
-       else if (*local_str == ')' || *local_str == '(' ||
-                        *local_str == '}' || *local_str == '{')
-               local_str++;
-       else
-       {
-               for (; *local_str != ' '
-                        && *local_str != '\n'
-                        && *local_str != '\t'
-                        && *local_str != '{'
-                        && *local_str != '}'
-                        && *local_str != '('
-                        && *local_str != ')'; local_str++, (*length)++);
-               (*length)--;
-       }
-       return ret_string;
-}
-
-/*
- * This guy does all the reading.
+ * This routine applies some semantic knowledge on top of the purely
+ * lexical tokenizer lsptok().  It can read
+ *     * Value token nodes (integers, floats, or strings);
+ *  * Plan nodes (via parsePlanString() from readfuncs.c);
+ *  * Lists of the above.
  *
- * Secrets:  He assumes that lsptok already has the string (see below).
+ * Secrets:  He assumes that lsptok already has the string (see above).
  * Any callers should set read_car_only to true.
  */
 void *
 nodeRead(bool read_car_only)
 {
        char       *token;
-       NodeTag         type;
-       Node       *this_value = NULL,
-                          *return_value = NULL;
        int                     tok_len;
-       char            tmp;
+       NodeTag         type;
+       Node       *this_value,
+                          *return_value;
        bool            make_dotted_pair_cell = false;
 
        token = lsptok(NULL, &tok_len);
@@ -198,8 +272,7 @@ nodeRead(bool read_car_only)
                        this_value = parsePlanString();
                        token = lsptok(NULL, &tok_len);
                        if (token[0] != '}')
-                               return NULL;
-
+                               elog(ERROR, "nodeRead: did not find '}' at end of plan node");
                        if (!read_car_only)
                                make_dotted_pair_cell = true;
                        else
@@ -221,12 +294,13 @@ nodeRead(bool read_car_only)
                        this_value = NULL;
                        break;
                case AT_SYMBOL:
+                       this_value = NULL;
                        break;
                case ATOM_TOKEN:
-                       if (!strncmp(token, "<>", 2))
+                       if (tok_len == 0)
                        {
+                               /* must be "<>" */
                                this_value = NULL;
-
                                /*
                                 * It might be NULL but it is an atom!
                                 */
@@ -237,39 +311,28 @@ nodeRead(bool read_car_only)
                        }
                        else
                        {
-                               tmp = token[tok_len];
-                               token[tok_len] = '\0';
-                               this_value = (Node *) pstrdup(token);   /* !attention! not a
-                                                                                                                * Node. use with
-                                                                                                                * caution */
-                               token[tok_len] = tmp;
+                               /* !attention! result is not a Node.  Use with caution. */
+                               this_value = (Node *) debackslash(token, tok_len);
                                make_dotted_pair_cell = true;
                        }
                        break;
                case T_Float:
-                       tmp = token[tok_len];
-                       token[tok_len] = '\0';
+                       /* we know that the token terminates on a char atof will stop at */
                        this_value = (Node *) makeFloat(atof(token));
-                       token[tok_len] = tmp;
                        make_dotted_pair_cell = true;
                        break;
                case T_Integer:
-                       tmp = token[tok_len];
-                       token[tok_len] = '\0';
+                       /* we know that the token terminates on a char atoi will stop at */
                        this_value = (Node *) makeInteger(atoi(token));
-                       token[tok_len] = tmp;
                        make_dotted_pair_cell = true;
                        break;
                case T_String:
-                       tmp = token[tok_len - 1];
-                       token[tok_len - 1] = '\0';
-                       token++;
-                       this_value = (Node *) makeString(token);        /* !! not strdup'd */
-                       token[tok_len - 2] = tmp;
+                       this_value = (Node *) makeString(debackslash(token+1, tok_len-2));
                        make_dotted_pair_cell = true;
                        break;
                default:
                        elog(ERROR, "nodeRead: Bad type %d", type);
+                       this_value = NULL;      /* keep compiler happy */
                        break;
        }
        if (make_dotted_pair_cell)
index 8f7bb52..9c1219b 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.77 2000/01/09 00:26:24 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.78 2000/01/14 00:53:21 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -26,8 +26,6 @@
 
 #include "postgres.h"
 
-
-
 #include "catalog/pg_index.h"
 #include "nodes/plannodes.h"
 #include "nodes/readfuncs.h"
@@ -81,8 +79,7 @@ _readQuery()
        {
                NotifyStmt *n = makeNode(NotifyStmt);
 
-               n->relname = palloc(length + 1);
-               StrNCpy(n->relname, token, length + 1);
+               n->relname = debackslash(token, length);
                local_node->utilityStmt = (Node *) n;
        }
 
@@ -95,10 +92,7 @@ _readQuery()
        if (length == 0)
                local_node->into = NULL;
        else
-       {
-               local_node->into = palloc(length + 1);
-               StrNCpy(local_node->into, token, length + 1);
-       }
+               local_node->into = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* skip :isPortal */
        token = lsptok(NULL, &length);          /* get isPortal */
@@ -121,10 +115,7 @@ _readQuery()
        if (length == 0)
                local_node->uniqueFlag = NULL;
        else
-       {
-               local_node->uniqueFlag = palloc(length + 1);
-               StrNCpy(local_node->uniqueFlag, token, length + 1);
-       }
+               local_node->uniqueFlag = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* skip :sortClause */
        local_node->sortClause = nodeRead(true);
@@ -289,9 +280,6 @@ _readPlan()
 
 /* ----------------
  *             _readResult
- *
- *             Does some obscene, possibly unportable, magic with
- *             sizes of things.
  * ----------------
  */
 static Result *
@@ -718,14 +706,10 @@ _readResdom()
 
        token = lsptok(NULL, &length);          /* eat :resname */
        token = lsptok(NULL, &length);          /* get the name */
-
        if (length == 0)
                local_node->resname = NULL;
        else
-       {
-               local_node->resname = (char *) palloc(length + 1);
-               StrNCpy(local_node->resname, token + 1, length + 1 - 2);                /* strip quotes */
-       }
+               local_node->resname = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* eat :reskey */
        token = lsptok(NULL, &length);          /* get reskey */
@@ -779,6 +763,8 @@ _readExpr()
                local_node->opType = NOT_EXPR;
        else if (!strncmp(token, "subp", 4))
                local_node->opType = SUBPLAN_EXPR;
+       else
+               elog(ERROR, "_readExpr: unknown opType \"%.10s\"", token);
 
        token = lsptok(NULL, &length);          /* eat :oper */
        local_node->oper = nodeRead(true);
@@ -1140,10 +1126,7 @@ _readParam()
        if (length == 0)
                local_node->paramname = NULL;
        else
-       {
-               local_node->paramname = (char *) palloc(length + 1);
-               StrNCpy(local_node->paramname, token, length + 1);
-       }
+               local_node->paramname = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* get :paramtype */
        token = lsptok(NULL, &length);          /* now read it */
@@ -1172,8 +1155,7 @@ _readAggref()
 
        token = lsptok(NULL, &length);          /* eat :aggname */
        token = lsptok(NULL, &length);          /* get aggname */
-       local_node->aggname = (char *) palloc(length + 1);
-       StrNCpy(local_node->aggname, token, length + 1);
+       local_node->aggname = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* eat :basetype */
        token = lsptok(NULL, &length);          /* get basetype */
@@ -1416,20 +1398,14 @@ _readRangeTblEntry()
        if (length == 0)
                local_node->relname = NULL;
        else
-       {
-               local_node->relname = (char *) palloc(length + 1);
-               StrNCpy(local_node->relname, token, length + 1);
-       }
+               local_node->relname = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* eat :refname */
        token = lsptok(NULL, &length);          /* get :refname */
        if (length == 0)
                local_node->refname = NULL;
        else
-       {
-               local_node->refname = (char *) palloc(length + 1);
-               StrNCpy(local_node->refname, token, length + 1);
-       }
+               local_node->refname = debackslash(token, length);
 
        token = lsptok(NULL, &length);          /* eat :relid */
        token = lsptok(NULL, &length);          /* get :relid */
@@ -1854,7 +1830,7 @@ _readIter()
  * Given a character string containing a plan, parsePlanString sets up the
  * plan structure representing that plan.
  *
- * The string passed to parsePlanString must be null-terminated.
+ * The string to be read must already have been loaded into lsptok().
  * ----------------
  */
 Node *
@@ -1866,101 +1842,101 @@ parsePlanString(void)
 
        token = lsptok(NULL, &length);
 
-       if (!strncmp(token, "PLAN", length))
+       if (length == 4 && strncmp(token, "PLAN", length) == 0)
                return_value = _readPlan();
-       else if (!strncmp(token, "RESULT", length))
+       else if (length == 6 && strncmp(token, "RESULT", length) == 0)
                return_value = _readResult();
-       else if (!strncmp(token, "APPEND", length))
+       else if (length == 6 && strncmp(token, "APPEND", length) == 0)
                return_value = _readAppend();
-       else if (!strncmp(token, "JOIN", length))
+       else if (length == 4 && strncmp(token, "JOIN", length) == 0)
                return_value = _readJoin();
-       else if (!strncmp(token, "NESTLOOP", length))
+       else if (length == 8 && strncmp(token, "NESTLOOP", length) == 0)
                return_value = _readNestLoop();
-       else if (!strncmp(token, "MERGEJOIN", length))
+       else if (length == 9 && strncmp(token, "MERGEJOIN", length) == 0)
                return_value = _readMergeJoin();
-       else if (!strncmp(token, "HASHJOIN", length))
+       else if (length == 8 && strncmp(token, "HASHJOIN", length) == 0)
                return_value = _readHashJoin();
-       else if (!strncmp(token, "SCAN", length))
+       else if (length == 4 && strncmp(token, "SCAN", length) == 0)
                return_value = _readScan();
-       else if (!strncmp(token, "SEQSCAN", length))
+       else if (length == 7 && strncmp(token, "SEQSCAN", length) == 0)
                return_value = _readSeqScan();
-       else if (!strncmp(token, "INDEXSCAN", length))
+       else if (length == 9 && strncmp(token, "INDEXSCAN", length) == 0)
                return_value = _readIndexScan();
-       else if (!strncmp(token, "TIDSCAN", length))
+       else if (length == 7 && strncmp(token, "TIDSCAN", length) == 0)
                return_value = _readTidScan();
-       else if (!strncmp(token, "NONAME", length))
+       else if (length == 6 && strncmp(token, "NONAME", length) == 0)
                return_value = _readNoname();
-       else if (!strncmp(token, "SORT", length))
+       else if (length == 4 && strncmp(token, "SORT", length) == 0)
                return_value = _readSort();
-       else if (!strncmp(token, "AGGREG", length))
+       else if (length == 6 && strncmp(token, "AGGREG", length) == 0)
                return_value = _readAggref();
-       else if (!strncmp(token, "SUBLINK", length))
+       else if (length == 7 && strncmp(token, "SUBLINK", length) == 0)
                return_value = _readSubLink();
-       else if (!strncmp(token, "AGG", length))
+       else if (length == 3 && strncmp(token, "AGG", length) == 0)
                return_value = _readAgg();
-       else if (!strncmp(token, "UNIQUE", length))
+       else if (length == 6 && strncmp(token, "UNIQUE", length) == 0)
                return_value = _readUnique();
-       else if (!strncmp(token, "HASH", length))
+       else if (length == 4 && strncmp(token, "HASH", length) == 0)
                return_value = _readHash();
-       else if (!strncmp(token, "RESDOM", length))
+       else if (length == 6 && strncmp(token, "RESDOM", length) == 0)
                return_value = _readResdom();
-       else if (!strncmp(token, "EXPR", length))
+       else if (length == 4 && strncmp(token, "EXPR", length) == 0)
                return_value = _readExpr();
-       else if (!strncmp(token, "ARRAYREF", length))
+       else if (length == 8 && strncmp(token, "ARRAYREF", length) == 0)
                return_value = _readArrayRef();
-       else if (!strncmp(token, "ARRAY", length))
+       else if (length == 5 && strncmp(token, "ARRAY", length) == 0)
                return_value = _readArray();
-       else if (!strncmp(token, "VAR", length))
+       else if (length == 3 && strncmp(token, "VAR", length) == 0)
                return_value = _readVar();
-       else if (!strncmp(token, "CONST", length))
+       else if (length == 5 && strncmp(token, "CONST", length) == 0)
                return_value = _readConst();
-       else if (!strncmp(token, "FUNC", length))
+       else if (length == 4 && strncmp(token, "FUNC", length) == 0)
                return_value = _readFunc();
-       else if (!strncmp(token, "OPER", length))
+       else if (length == 4 && strncmp(token, "OPER", length) == 0)
                return_value = _readOper();
-       else if (!strncmp(token, "PARAM", length))
+       else if (length == 5 && strncmp(token, "PARAM", length) == 0)
                return_value = _readParam();
-       else if (!strncmp(token, "ESTATE", length))
+       else if (length == 6 && strncmp(token, "ESTATE", length) == 0)
                return_value = _readEState();
-       else if (!strncmp(token, "RELOPTINFO", length))
+       else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0)
                return_value = _readRelOptInfo();
-       else if (!strncmp(token, "INDEXOPTINFO", length))
+       else if (length == 12 && strncmp(token, "INDEXOPTINFO", length) == 0)
                return_value = _readIndexOptInfo();
-       else if (!strncmp(token, "TARGETENTRY", length))
+       else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0)
                return_value = _readTargetEntry();
-       else if (!strncmp(token, "RTE", length))
+       else if (length == 3 && strncmp(token, "RTE", length) == 0)
                return_value = _readRangeTblEntry();
-       else if (!strncmp(token, "PATH", length))
+       else if (length == 4 && strncmp(token, "PATH", length) == 0)
                return_value = _readPath();
-       else if (!strncmp(token, "INDEXPATH", length))
+       else if (length == 9 && strncmp(token, "INDEXPATH", length) == 0)
                return_value = _readIndexPath();
-       else if (!strncmp(token, "TIDPATH", length))
+       else if (length == 7 && strncmp(token, "TIDPATH", length) == 0)
                return_value = _readTidPath();
-       else if (!strncmp(token, "NESTPATH", length))
+       else if (length == 8 && strncmp(token, "NESTPATH", length) == 0)
                return_value = _readNestPath();
-       else if (!strncmp(token, "MERGEPATH", length))
+       else if (length == 9 && strncmp(token, "MERGEPATH", length) == 0)
                return_value = _readMergePath();
-       else if (!strncmp(token, "HASHPATH", length))
+       else if (length == 8 && strncmp(token, "HASHPATH", length) == 0)
                return_value = _readHashPath();
-       else if (!strncmp(token, "PATHKEYITEM", length))
+       else if (length == 11 && strncmp(token, "PATHKEYITEM", length) == 0)
                return_value = _readPathKeyItem();
-       else if (!strncmp(token, "RESTRICTINFO", length))
+       else if (length == 12 && strncmp(token, "RESTRICTINFO", length) == 0)
                return_value = _readRestrictInfo();
-       else if (!strncmp(token, "JOININFO", length))
+       else if (length == 8 && strncmp(token, "JOININFO", length) == 0)
                return_value = _readJoinInfo();
-       else if (!strncmp(token, "ITER", length))
+       else if (length == 4 && strncmp(token, "ITER", length) == 0)
                return_value = _readIter();
-       else if (!strncmp(token, "QUERY", length))
+       else if (length == 5 && strncmp(token, "QUERY", length) == 0)
                return_value = _readQuery();
-       else if (!strncmp(token, "SORTCLAUSE", length))
+       else if (length == 10 && strncmp(token, "SORTCLAUSE", length) == 0)
                return_value = _readSortClause();
-       else if (!strncmp(token, "GROUPCLAUSE", length))
+       else if (length == 11 && strncmp(token, "GROUPCLAUSE", length) == 0)
                return_value = _readGroupClause();
-       else if (!strncmp(token, "CASE", length))
+       else if (length == 4 && strncmp(token, "CASE", length) == 0)
                return_value = _readCaseExpr();
-       else if (!strncmp(token, "WHEN", length))
+       else if (length == 4 && strncmp(token, "WHEN", length) == 0)
                return_value = _readCaseWhen();
-       else if (!strncmp(token, "ROWMARK", length))
+       else if (length == 7 && strncmp(token, "ROWMARK", length) == 0)
                return_value = _readRowMark();
        else
                elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
@@ -2001,6 +1977,7 @@ readDatum(Oid type)
        {
                if (length > sizeof(Datum))
                        elog(ERROR, "readValue: byval & length = %d", length);
+               res = (Datum) 0;
                s = (char *) (&res);
                for (i = 0; i < sizeof(Datum); i++)
                {
@@ -2009,11 +1986,10 @@ readDatum(Oid type)
                }
        }
        else if (length <= 0)
-               s = NULL;
-       else if (length >= 1)
+               res = (Datum) NULL;
+       else
        {
                s = (char *) palloc(length);
-               Assert(s != NULL);
                for (i = 0; i < length; i++)
                {
                        token = lsptok(NULL, &tokenLength);
index 035c14e..ade5100 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: readfuncs.h,v 1.7 1999/02/13 23:21:41 momjian Exp $
+ * $Id: readfuncs.h,v 1.8 2000/01/14 00:53:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
  * prototypes for functions in read.c (the lisp token parser)
  */
 extern char *lsptok(char *string, int *length);
+extern char *debackslash(char *token, int length);
 extern void *nodeRead(bool read_car_only);
 
 /*