*
* 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 -
{
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");
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 ");
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",
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);
}
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:
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 ");
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");
}
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 ");
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);
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);
}
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",
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
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, "] ");
}
}
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
{
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
{
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
{
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;
}
}
appendStringInfo(str, " :default ");
_outNode(str, node->defresult);
-
- return;
}
static void
appendStringInfo(str, " :then ");
_outNode(str, node->result);
-
- return;
}
/*
{
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:
case T_IndexStmt:
_outIndexStmt(str, obj);
break;
-
case T_ColumnDef:
_outColumnDef(str, obj);
break;
nodeTag(obj));
break;
}
- appendStringInfo(str, "}");
+ appendStringInfoChar(str, '}');
}
- return;
}
/*
*
*
* 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
#include <ctype.h>
#include "postgres.h"
+
#include "nodes/pg_list.h"
#include "nodes/readfuncs.h"
*
*****************************************************************************/
+/*
+ * 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)
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);
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
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!
*/
}
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)
*
*
* 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
#include "postgres.h"
-
-
#include "catalog/pg_index.h"
#include "nodes/plannodes.h"
#include "nodes/readfuncs.h"
{
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;
}
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 */
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);
/* ----------------
* _readResult
- *
- * Does some obscene, possibly unportable, magic with
- * sizes of things.
* ----------------
*/
static Result *
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 */
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);
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 */
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 */
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 */
* 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 *
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);
{
if (length > sizeof(Datum))
elog(ERROR, "readValue: byval & length = %d", length);
+ res = (Datum) 0;
s = (char *) (&res);
for (i = 0; i < sizeof(Datum); i++)
{
}
}
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);
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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);
/*