/*------------------------------------------------------------------------- * * readfuncs.c * Reader functions for Postgres tree nodes. * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.149 2003/02/16 02:30:37 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we * never have occasion to read them in. (There was once code here that * claimed to read them, but it was broken as well as unused.) We * never read executor state trees, either. * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include "nodes/parsenodes.h" #include "nodes/readfuncs.h" /* * Macros to simplify reading of different kinds of fields. Use these * wherever possible to reduce the chance for silly typos. Note that these * hard-wire conventions about the names of the local variables in a Read * routine. */ /* Declare appropriate local variables */ #define READ_LOCALS(nodeTypeName) \ nodeTypeName *local_node = makeNode(nodeTypeName); \ char *token; \ int length /* A few guys need only local_node */ #define READ_LOCALS_NO_FIELDS(nodeTypeName) \ nodeTypeName *local_node = makeNode(nodeTypeName) /* And a few guys need only the pg_strtok support fields */ #define READ_TEMP_LOCALS() \ char *token; \ int length /* Read an integer field (anything written as ":fldname %d") */ #define READ_INT_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = atoi(token) /* Read an unsigned integer field (anything written as ":fldname %u") */ #define READ_UINT_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = atoui(token) /* Read an OID field (don't hard-wire assumption that OID is same as uint) */ #define READ_OID_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = atooid(token) /* Read a char field (ie, one ascii character) */ #define READ_CHAR_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = token[0] /* Read an enumerated-type field that was written as an integer code */ #define READ_ENUM_FIELD(fldname, enumtype) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = (enumtype) atoi(token) /* Read a float field */ #define READ_FLOAT_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = atof(token) /* Read a boolean field */ #define READ_BOOL_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = strtobool(token) /* Read a character-string field */ #define READ_STRING_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ token = pg_strtok(&length); /* get field value */ \ local_node->fldname = nullable_string(token, length) /* Read a Node field */ #define READ_NODE_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ local_node->fldname = nodeRead(true) /* Read an integer-list field */ #define READ_INTLIST_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ local_node->fldname = toIntList(nodeRead(true)) /* Read an OID-list field */ #define READ_OIDLIST_FIELD(fldname) \ token = pg_strtok(&length); /* skip :fldname */ \ local_node->fldname = toOidList(nodeRead(true)) /* Routine exit */ #define READ_DONE() \ return local_node /* * NOTE: use atoi() to read values written with %d, or atoui() to read * values written with %u in outfuncs.c. An exception is OID values, * for which use atooid(). (As of 7.1, outfuncs.c writes OIDs as %u, * but this will probably change in the future.) */ #define atoui(x) ((unsigned int) strtoul((x), NULL, 10)) #define atooid(x) ((Oid) strtoul((x), NULL, 10)) #define strtobool(x) ((*(x) == 't') ? true : false) #define nullable_string(token,length) \ ((length) == 0 ? (char *) NULL : debackslash(token, length)) static Datum readDatum(bool typbyval); /* Convert Value list returned by nodeRead into list of integers */ static List * toIntList(List *list) { List *l; foreach(l, list) { Value *v = (Value *) lfirst(l); if (!IsA(v, Integer)) elog(ERROR, "toIntList: unexpected datatype"); lfirsti(l) = intVal(v); pfree(v); } return list; } /* Convert Value list returned by nodeRead into list of OIDs */ static List * toOidList(List *list) { List *l; foreach(l, list) { Value *v = (Value *) lfirst(l); /* * This is a bit tricky because OID is unsigned, and so nodeRead * might have concluded the value doesn't fit in an integer. Must * cope with T_Float as well. */ if (IsA(v, Integer)) { lfirsto(l) = (Oid) intVal(v); pfree(v); } else if (IsA(v, Float)) { lfirsto(l) = atooid(strVal(v)); pfree(strVal(v)); pfree(v); } else elog(ERROR, "toOidList: unexpected datatype"); } return list; } /* * _readQuery */ static Query * _readQuery(void) { READ_LOCALS(Query); READ_ENUM_FIELD(commandType, CmdType); READ_ENUM_FIELD(querySource, QuerySource); READ_NODE_FIELD(utilityStmt); READ_INT_FIELD(resultRelation); READ_NODE_FIELD(into); READ_BOOL_FIELD(isPortal); READ_BOOL_FIELD(isBinary); READ_BOOL_FIELD(hasAggs); READ_BOOL_FIELD(hasSubLinks); READ_NODE_FIELD(rtable); READ_NODE_FIELD(jointree); READ_INTLIST_FIELD(rowMarks); READ_NODE_FIELD(targetList); READ_NODE_FIELD(groupClause); READ_NODE_FIELD(havingQual); READ_NODE_FIELD(distinctClause); READ_NODE_FIELD(sortClause); READ_NODE_FIELD(limitOffset); READ_NODE_FIELD(limitCount); READ_NODE_FIELD(setOperations); READ_INTLIST_FIELD(resultRelations); /* planner-internal fields are left zero */ READ_DONE(); } /* * _readNotifyStmt */ static NotifyStmt * _readNotifyStmt(void) { READ_LOCALS(NotifyStmt); READ_NODE_FIELD(relation); READ_DONE(); } /* * _readSortClause */ static SortClause * _readSortClause(void) { READ_LOCALS(SortClause); READ_UINT_FIELD(tleSortGroupRef); READ_OID_FIELD(sortop); READ_DONE(); } /* * _readGroupClause */ static GroupClause * _readGroupClause(void) { READ_LOCALS(GroupClause); READ_UINT_FIELD(tleSortGroupRef); READ_OID_FIELD(sortop); READ_DONE(); } /* * _readSetOperationStmt */ static SetOperationStmt * _readSetOperationStmt(void) { READ_LOCALS(SetOperationStmt); READ_ENUM_FIELD(op, SetOperation); READ_BOOL_FIELD(all); READ_NODE_FIELD(larg); READ_NODE_FIELD(rarg); READ_OIDLIST_FIELD(colTypes); READ_DONE(); } /* * Stuff from primnodes.h. */ /* * _readResdom */ static Resdom * _readResdom(void) { READ_LOCALS(Resdom); READ_INT_FIELD(resno); READ_OID_FIELD(restype); READ_INT_FIELD(restypmod); READ_STRING_FIELD(resname); READ_UINT_FIELD(ressortgroupref); READ_UINT_FIELD(reskey); READ_OID_FIELD(reskeyop); READ_BOOL_FIELD(resjunk); READ_DONE(); } static Alias * _readAlias(void) { READ_LOCALS(Alias); READ_STRING_FIELD(aliasname); READ_NODE_FIELD(colnames); READ_DONE(); } static RangeVar * _readRangeVar(void) { READ_LOCALS(RangeVar); local_node->catalogname = NULL; /* not currently saved in output * format */ READ_STRING_FIELD(schemaname); READ_STRING_FIELD(relname); READ_ENUM_FIELD(inhOpt, InhOption); READ_BOOL_FIELD(istemp); READ_NODE_FIELD(alias); READ_DONE(); } /* * _readVar */ static Var * _readVar(void) { READ_LOCALS(Var); READ_UINT_FIELD(varno); READ_INT_FIELD(varattno); READ_OID_FIELD(vartype); READ_INT_FIELD(vartypmod); READ_UINT_FIELD(varlevelsup); READ_UINT_FIELD(varnoold); READ_INT_FIELD(varoattno); READ_DONE(); } /* * _readConst */ static Const * _readConst(void) { READ_LOCALS(Const); READ_OID_FIELD(consttype); READ_INT_FIELD(constlen); READ_BOOL_FIELD(constbyval); READ_BOOL_FIELD(constisnull); token = pg_strtok(&length); /* skip :constvalue */ if (local_node->constisnull) token = pg_strtok(&length); /* skip "<>" */ else local_node->constvalue = readDatum(local_node->constbyval); READ_DONE(); } /* * _readParam */ static Param * _readParam(void) { READ_LOCALS(Param); READ_INT_FIELD(paramkind); READ_INT_FIELD(paramid); READ_STRING_FIELD(paramname); READ_OID_FIELD(paramtype); READ_DONE(); } /* * _readAggref */ static Aggref * _readAggref(void) { READ_LOCALS(Aggref); READ_OID_FIELD(aggfnoid); READ_OID_FIELD(aggtype); READ_NODE_FIELD(target); READ_BOOL_FIELD(aggstar); READ_BOOL_FIELD(aggdistinct); READ_DONE(); } /* * _readArrayRef */ static ArrayRef * _readArrayRef(void) { READ_LOCALS(ArrayRef); READ_OID_FIELD(refrestype); READ_INT_FIELD(refattrlength); READ_INT_FIELD(refelemlength); READ_BOOL_FIELD(refelembyval); READ_CHAR_FIELD(refelemalign); READ_NODE_FIELD(refupperindexpr); READ_NODE_FIELD(reflowerindexpr); READ_NODE_FIELD(refexpr); READ_NODE_FIELD(refassgnexpr); READ_DONE(); } /* * _readFuncExpr */ static FuncExpr * _readFuncExpr(void) { READ_LOCALS(FuncExpr); READ_OID_FIELD(funcid); READ_OID_FIELD(funcresulttype); READ_BOOL_FIELD(funcretset); READ_ENUM_FIELD(funcformat, CoercionForm); READ_NODE_FIELD(args); READ_DONE(); } /* * _readOpExpr */ static OpExpr * _readOpExpr(void) { READ_LOCALS(OpExpr); READ_OID_FIELD(opno); READ_OID_FIELD(opfuncid); /* * The opfuncid is stored in the textual format primarily for debugging * and documentation reasons. We want to always read it as zero to force * it to be re-looked-up in the pg_operator entry. This ensures that * stored rules don't have hidden dependencies on operators' functions. * (We don't currently support an ALTER OPERATOR command, but might * someday.) */ local_node->opfuncid = InvalidOid; READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); READ_DONE(); } /* * _readDistinctExpr */ static DistinctExpr * _readDistinctExpr(void) { READ_LOCALS(DistinctExpr); READ_OID_FIELD(opno); READ_OID_FIELD(opfuncid); /* * The opfuncid is stored in the textual format primarily for debugging * and documentation reasons. We want to always read it as zero to force * it to be re-looked-up in the pg_operator entry. This ensures that * stored rules don't have hidden dependencies on operators' functions. * (We don't currently support an ALTER OPERATOR command, but might * someday.) */ local_node->opfuncid = InvalidOid; READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); READ_DONE(); } /* * _readBoolExpr */ static BoolExpr * _readBoolExpr(void) { READ_LOCALS(BoolExpr); /* do-it-yourself enum representation */ token = pg_strtok(&length); /* skip :boolop */ token = pg_strtok(&length); /* get field value */ if (strncmp(token, "and", 3) == 0) local_node->boolop = AND_EXPR; else if (strncmp(token, "or", 2) == 0) local_node->boolop = OR_EXPR; else if (strncmp(token, "not", 3) == 0) local_node->boolop = NOT_EXPR; else elog(ERROR, "_readBoolExpr: unknown boolop \"%.*s\"", length, token); READ_NODE_FIELD(args); READ_DONE(); } /* * _readSubLink */ static SubLink * _readSubLink(void) { READ_LOCALS(SubLink); READ_ENUM_FIELD(subLinkType, SubLinkType); READ_BOOL_FIELD(useOr); READ_NODE_FIELD(lefthand); READ_NODE_FIELD(operName); READ_OIDLIST_FIELD(operOids); READ_NODE_FIELD(subselect); READ_DONE(); } /* * _readSubPlan is not needed since it doesn't appear in stored rules. */ /* * _readFieldSelect */ static FieldSelect * _readFieldSelect(void) { READ_LOCALS(FieldSelect); READ_NODE_FIELD(arg); READ_INT_FIELD(fieldnum); READ_OID_FIELD(resulttype); READ_INT_FIELD(resulttypmod); READ_DONE(); } /* * _readRelabelType */ static RelabelType * _readRelabelType(void) { READ_LOCALS(RelabelType); READ_NODE_FIELD(arg); READ_OID_FIELD(resulttype); READ_INT_FIELD(resulttypmod); READ_ENUM_FIELD(relabelformat, CoercionForm); READ_DONE(); } /* * _readCaseExpr */ static CaseExpr * _readCaseExpr(void) { READ_LOCALS(CaseExpr); READ_OID_FIELD(casetype); READ_NODE_FIELD(arg); READ_NODE_FIELD(args); READ_NODE_FIELD(defresult); READ_DONE(); } /* * _readCaseWhen */ static CaseWhen * _readCaseWhen(void) { READ_LOCALS(CaseWhen); READ_NODE_FIELD(expr); READ_NODE_FIELD(result); READ_DONE(); } /* * _readCoalesceExpr */ static CoalesceExpr * _readCoalesceExpr(void) { READ_LOCALS(CoalesceExpr); READ_OID_FIELD(coalescetype); READ_NODE_FIELD(args); READ_DONE(); } /* * _readNullIfExpr */ static NullIfExpr * _readNullIfExpr(void) { READ_LOCALS(NullIfExpr); READ_OID_FIELD(opno); READ_OID_FIELD(opfuncid); /* * The opfuncid is stored in the textual format primarily for debugging * and documentation reasons. We want to always read it as zero to force * it to be re-looked-up in the pg_operator entry. This ensures that * stored rules don't have hidden dependencies on operators' functions. * (We don't currently support an ALTER OPERATOR command, but might * someday.) */ local_node->opfuncid = InvalidOid; READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); READ_DONE(); } /* * _readNullTest */ static NullTest * _readNullTest(void) { READ_LOCALS(NullTest); READ_NODE_FIELD(arg); READ_ENUM_FIELD(nulltesttype, NullTestType); READ_DONE(); } /* * _readBooleanTest */ static BooleanTest * _readBooleanTest(void) { READ_LOCALS(BooleanTest); READ_NODE_FIELD(arg); READ_ENUM_FIELD(booltesttype, BoolTestType); READ_DONE(); } /* * _readCoerceToDomain */ static CoerceToDomain * _readCoerceToDomain(void) { READ_LOCALS(CoerceToDomain); READ_NODE_FIELD(arg); READ_OID_FIELD(resulttype); READ_INT_FIELD(resulttypmod); READ_ENUM_FIELD(coercionformat, CoercionForm); READ_DONE(); } /* * _readCoerceToDomainValue */ static CoerceToDomainValue * _readCoerceToDomainValue(void) { READ_LOCALS(CoerceToDomainValue); READ_OID_FIELD(typeId); READ_INT_FIELD(typeMod); READ_DONE(); } /* * _readTargetEntry */ static TargetEntry * _readTargetEntry(void) { READ_LOCALS(TargetEntry); READ_NODE_FIELD(resdom); READ_NODE_FIELD(expr); READ_DONE(); } /* * _readRangeTblRef */ static RangeTblRef * _readRangeTblRef(void) { READ_LOCALS(RangeTblRef); READ_INT_FIELD(rtindex); READ_DONE(); } /* * _readJoinExpr */ static JoinExpr * _readJoinExpr(void) { READ_LOCALS(JoinExpr); READ_ENUM_FIELD(jointype, JoinType); READ_BOOL_FIELD(isNatural); READ_NODE_FIELD(larg); READ_NODE_FIELD(rarg); READ_NODE_FIELD(using); READ_NODE_FIELD(quals); READ_NODE_FIELD(alias); READ_INT_FIELD(rtindex); READ_DONE(); } /* * _readFromExpr */ static FromExpr * _readFromExpr(void) { READ_LOCALS(FromExpr); READ_NODE_FIELD(fromlist); READ_NODE_FIELD(quals); READ_DONE(); } /* * Stuff from parsenodes.h. */ static ColumnRef * _readColumnRef(void) { READ_LOCALS(ColumnRef); READ_NODE_FIELD(fields); READ_NODE_FIELD(indirection); READ_DONE(); } static ColumnDef * _readColumnDef(void) { READ_LOCALS(ColumnDef); READ_STRING_FIELD(colname); READ_NODE_FIELD(typename); READ_INT_FIELD(inhcount); READ_BOOL_FIELD(is_local); READ_BOOL_FIELD(is_not_null); READ_NODE_FIELD(raw_default); READ_STRING_FIELD(cooked_default); READ_NODE_FIELD(constraints); READ_NODE_FIELD(support); READ_DONE(); } static TypeName * _readTypeName(void) { READ_LOCALS(TypeName); READ_NODE_FIELD(names); READ_OID_FIELD(typeid); READ_BOOL_FIELD(timezone); READ_BOOL_FIELD(setof); READ_BOOL_FIELD(pct_type); READ_INT_FIELD(typmod); READ_NODE_FIELD(arrayBounds); READ_DONE(); } static ExprFieldSelect * _readExprFieldSelect(void) { READ_LOCALS(ExprFieldSelect); READ_NODE_FIELD(arg); READ_NODE_FIELD(fields); READ_NODE_FIELD(indirection); READ_DONE(); } /* * _readRangeTblEntry */ static RangeTblEntry * _readRangeTblEntry(void) { READ_LOCALS(RangeTblEntry); /* put alias + eref first to make dump more legible */ READ_NODE_FIELD(alias); READ_NODE_FIELD(eref); READ_ENUM_FIELD(rtekind, RTEKind); switch (local_node->rtekind) { case RTE_RELATION: case RTE_SPECIAL: READ_OID_FIELD(relid); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); break; case RTE_FUNCTION: READ_NODE_FIELD(funcexpr); READ_NODE_FIELD(coldeflist); break; case RTE_JOIN: READ_ENUM_FIELD(jointype, JoinType); READ_NODE_FIELD(joinaliasvars); break; default: elog(ERROR, "bogus rte kind %d", (int) local_node->rtekind); break; } READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); READ_BOOL_FIELD(checkForRead); READ_BOOL_FIELD(checkForWrite); READ_OID_FIELD(checkAsUser); READ_DONE(); } /* * parseNodeString * * Given a character string representing a node tree, parseNodeString creates * the internal node structure. * * The string to be read must already have been loaded into pg_strtok(). */ Node * parseNodeString(void) { void *return_value; READ_TEMP_LOCALS(); token = pg_strtok(&length); #define MATCH(tokname, namelen) \ (length == namelen && strncmp(token, tokname, namelen) == 0) if (MATCH("QUERY", 5)) return_value = _readQuery(); else if (MATCH("NOTIFY", 6)) return_value = _readNotifyStmt(); else if (MATCH("SORTCLAUSE", 10)) return_value = _readSortClause(); else if (MATCH("GROUPCLAUSE", 11)) return_value = _readGroupClause(); else if (MATCH("SETOPERATIONSTMT", 16)) return_value = _readSetOperationStmt(); else if (MATCH("RESDOM", 6)) return_value = _readResdom(); else if (MATCH("ALIAS", 5)) return_value = _readAlias(); else if (MATCH("RANGEVAR", 8)) return_value = _readRangeVar(); else if (MATCH("VAR", 3)) return_value = _readVar(); else if (MATCH("CONST", 5)) return_value = _readConst(); else if (MATCH("PARAM", 5)) return_value = _readParam(); else if (MATCH("AGGREF", 6)) return_value = _readAggref(); else if (MATCH("ARRAYREF", 8)) return_value = _readArrayRef(); else if (MATCH("FUNCEXPR", 8)) return_value = _readFuncExpr(); else if (MATCH("OPEXPR", 6)) return_value = _readOpExpr(); else if (MATCH("DISTINCTEXPR", 12)) return_value = _readDistinctExpr(); else if (MATCH("BOOLEXPR", 8)) return_value = _readBoolExpr(); else if (MATCH("SUBLINK", 7)) return_value = _readSubLink(); else if (MATCH("FIELDSELECT", 11)) return_value = _readFieldSelect(); else if (MATCH("RELABELTYPE", 11)) return_value = _readRelabelType(); else if (MATCH("CASE", 4)) return_value = _readCaseExpr(); else if (MATCH("WHEN", 4)) return_value = _readCaseWhen(); else if (MATCH("COALESCE", 8)) return_value = _readCoalesceExpr(); else if (MATCH("NULLIFEXPR", 10)) return_value = _readNullIfExpr(); else if (MATCH("NULLTEST", 8)) return_value = _readNullTest(); else if (MATCH("BOOLEANTEST", 11)) return_value = _readBooleanTest(); else if (MATCH("COERCETODOMAIN", 14)) return_value = _readCoerceToDomain(); else if (MATCH("COERCETODOMAINVALUE", 19)) return_value = _readCoerceToDomainValue(); else if (MATCH("TARGETENTRY", 11)) return_value = _readTargetEntry(); else if (MATCH("RANGETBLREF", 11)) return_value = _readRangeTblRef(); else if (MATCH("JOINEXPR", 8)) return_value = _readJoinExpr(); else if (MATCH("FROMEXPR", 8)) return_value = _readFromExpr(); else if (MATCH("COLUMNREF", 9)) return_value = _readColumnRef(); else if (MATCH("COLUMNDEF", 9)) return_value = _readColumnDef(); else if (MATCH("TYPENAME", 8)) return_value = _readTypeName(); else if (MATCH("EXPRFIELDSELECT", 15)) return_value = _readExprFieldSelect(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); else { elog(ERROR, "badly formatted node string \"%.32s\"...", token); return_value = NULL; /* keep compiler quiet */ } return (Node *) return_value; } /* * readDatum * * Given a string representation of a constant, recreate the appropriate * Datum. The string representation embeds length info, but not byValue, * so we must be told that. */ static Datum readDatum(bool typbyval) { Size length, i; int tokenLength; char *token; Datum res; char *s; /* * read the actual length of the value */ token = pg_strtok(&tokenLength); length = atoui(token); token = pg_strtok(&tokenLength); /* read the '[' */ if (token == NULL || token[0] != '[') elog(ERROR, "readDatum: expected '%s', got '%s'; length = %lu", "[", token ? (const char *) token : "[NULL]", (unsigned long) length); if (typbyval) { if (length > (Size) sizeof(Datum)) elog(ERROR, "readDatum: byval & length = %lu", (unsigned long) length); res = (Datum) 0; s = (char *) (&res); for (i = 0; i < (Size) sizeof(Datum); i++) { token = pg_strtok(&tokenLength); s[i] = (char) atoi(token); } } else if (length <= 0) res = (Datum) NULL; else { s = (char *) palloc(length); for (i = 0; i < length; i++) { token = pg_strtok(&tokenLength); s[i] = (char) atoi(token); } res = PointerGetDatum(s); } token = pg_strtok(&tokenLength); /* read the ']' */ if (token == NULL || token[0] != ']') elog(ERROR, "readDatum: expected '%s', got '%s'; length = %lu", "]", token ? (const char *) token : "[NULL]", (unsigned long) length); return res; }