From 338fd40bfcbd0f666be0d1b34f2ff32952df9216 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 14 Jan 2000 00:53:21 +0000 Subject: [PATCH] Revise quoting conventions in outfuncs/readfuncs so that nodeRead doesn't 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 | 252 +++++++++++++++++++++++---------------- src/backend/nodes/read.c | 271 ++++++++++++++++++++++++++---------------- src/backend/nodes/readfuncs.c | 150 ++++++++++------------- src/include/nodes/readfuncs.h | 3 +- 4 files changed, 379 insertions(+), 297 deletions(-) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 1c8754fef5..2bf112e309 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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 @@ -19,8 +19,10 @@ * representation plus some other information (string length, etc.) * */ +#include #include "postgres.h" + #include "access/heapam.h" #include "access/htup.h" #include "catalog/pg_type.h" @@ -42,9 +44,43 @@ 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, ""); + appendStringInfo(str, ""); 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; } /* diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c index e80385e6e1..93fedf3caa 100644 --- a/src/backend/nodes/read.c +++ b/src/backend/nodes/read.c @@ -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 #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) diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 8f7bb5271a..9c1219b786 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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); diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h index 035c14e6b5..ade510052e 100644 --- a/src/include/nodes/readfuncs.h +++ b/src/include/nodes/readfuncs.h @@ -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); /* -- 2.11.0