Create a new expression node type RelabelType, which exists solely to

represent the result of a binary-compatible type coercion.  At runtime
it just evaluates its argument --- but during type resolution, exprType
will pick up the output type of the RelabelType node instead of the type
of the argument.  This solves some longstanding problems with dropped
type coercions, an example being 'select now()::abstime::int4' which
used to produce date-formatted output, not an integer, because the
coercion to int4 was dropped on the floor.
This commit is contained in:
Tom Lane 2000-02-20 21:32:16 +00:00
parent bd8e071482
commit 57b30e8e22
13 changed files with 325 additions and 86 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.67 2000/01/26 05:56:21 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.68 2000/02/20 21:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1176,7 +1176,7 @@ ExecEvalExpr(Node *expression,
bool *isNull,
bool *isDone)
{
Datum retDatum = 0;
Datum retDatum;
*isNull = false;
@ -1200,36 +1200,33 @@ ExecEvalExpr(Node *expression,
switch (nodeTag(expression))
{
case T_Var:
retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
break;
case T_Const:
{
Const *con = (Const *) expression;
if (con->constisnull)
*isNull = true;
retDatum = con->constvalue;
*isNull = con->constisnull;
break;
}
case T_Param:
retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull);
retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
break;
case T_Iter:
retDatum = (Datum) ExecEvalIter((Iter *) expression,
econtext,
isNull,
isDone);
retDatum = ExecEvalIter((Iter *) expression,
econtext,
isNull,
isDone);
break;
case T_Aggref:
retDatum = (Datum) ExecEvalAggref((Aggref *) expression,
econtext,
isNull);
retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
break;
case T_ArrayRef:
retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression,
econtext,
isNull,
isDone);
retDatum = ExecEvalArrayRef((ArrayRef *) expression,
econtext,
isNull,
isDone);
break;
case T_Expr:
{
@ -1238,37 +1235,48 @@ ExecEvalExpr(Node *expression,
switch (expr->opType)
{
case OP_EXPR:
retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
retDatum = ExecEvalOper(expr, econtext, isNull);
break;
case FUNC_EXPR:
retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
retDatum = ExecEvalFunc(expr, econtext,
isNull, isDone);
break;
case OR_EXPR:
retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
retDatum = ExecEvalOr(expr, econtext, isNull);
break;
case AND_EXPR:
retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull);
retDatum = ExecEvalAnd(expr, econtext, isNull);
break;
case NOT_EXPR:
retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
retDatum = ExecEvalNot(expr, econtext, isNull);
break;
case SUBPLAN_EXPR:
retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper,
expr->args, econtext,
isNull);
retDatum = ExecSubPlan((SubPlan *) expr->oper,
expr->args, econtext,
isNull);
break;
default:
elog(ERROR, "ExecEvalExpr: unknown expression type %d", expr->opType);
elog(ERROR, "ExecEvalExpr: unknown expression type %d",
expr->opType);
retDatum = 0; /* keep compiler quiet */
break;
}
break;
}
case T_RelabelType:
retDatum = ExecEvalExpr(((RelabelType *) expression)->arg,
econtext,
isNull,
isDone);
break;
case T_CaseExpr:
retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull);
retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull);
break;
default:
elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression));
elog(ERROR, "ExecEvalExpr: unknown expression type %d",
nodeTag(expression));
retDatum = 0; /* keep compiler quiet */
break;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.106 2000/02/15 20:49:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.107 2000/02/20 21:32:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -873,6 +873,26 @@ _copySubLink(SubLink *from)
return newnode;
}
/* ----------------
* _copyRelabelType
* ----------------
*/
static RelabelType *
_copyRelabelType(RelabelType *from)
{
RelabelType *newnode = makeNode(RelabelType);
/* ----------------
* copy remainder of node
* ----------------
*/
Node_Copy(from, newnode, arg);
newnode->resulttype = from->resulttype;
newnode->resulttypmod = from->resulttypmod;
return newnode;
}
/* ----------------
* _copyCaseExpr
* ----------------
@ -1617,6 +1637,9 @@ copyObject(void *from)
case T_SubLink:
retval = _copySubLink(from);
break;
case T_RelabelType:
retval = _copyRelabelType(from);
break;
case T_CaseExpr:
retval = _copyCaseExpr(from);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.61 2000/02/15 20:49:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.62 2000/02/20 21:32:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -262,6 +262,18 @@ _equalSubLink(SubLink *a, SubLink *b)
return true;
}
static bool
_equalRelabelType(RelabelType *a, RelabelType *b)
{
if (!equal(a->arg, b->arg))
return false;
if (a->resulttype != b->resulttype)
return false;
if (a->resulttypmod != b->resulttypmod)
return false;
return true;
}
static bool
_equalArray(Array *a, Array *b)
{
@ -806,6 +818,9 @@ equal(void *a, void *b)
case T_SubLink:
retval = _equalSubLink(a, b);
break;
case T_RelabelType:
retval = _equalRelabelType(a, b);
break;
case T_Func:
retval = _equalFunc(a, b);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.36 2000/02/15 20:49:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.37 2000/02/20 21:32:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -650,6 +650,22 @@ _freeSubLink(SubLink *node)
pfree(node);
}
/* ----------------
* _freeRelabelType
* ----------------
*/
static void
_freeRelabelType(RelabelType *node)
{
/* ----------------
* free remainder of node
* ----------------
*/
freeObject(node->arg);
pfree(node);
}
/* ----------------
* _freeCaseExpr
* ----------------
@ -1241,6 +1257,9 @@ freeObject(void *node)
case T_SubLink:
_freeSubLink(node);
break;
case T_RelabelType:
_freeRelabelType(node);
break;
case T_CaseExpr:
_freeCaseExpr(node);
break;

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.108 2000/02/15 20:49:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.109 2000/02/20 21:32:05 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -770,6 +770,19 @@ _outSubLink(StringInfo str, SubLink *node)
_outNode(str, node->subselect);
}
/*
* RelabelType
*/
static void
_outRelabelType(StringInfo str, RelabelType *node)
{
appendStringInfo(str, " RELABELTYPE :arg ");
_outNode(str, node->arg);
appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
node->resulttype, node->resulttypmod);
}
/*
* Array is a subclass of Expr
*/
@ -1496,6 +1509,9 @@ _outNode(StringInfo str, void *obj)
case T_SubLink:
_outSubLink(str, obj);
break;
case T_RelabelType:
_outRelabelType(str, obj);
break;
case T_Array:
_outArray(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.84 2000/02/15 20:49:12 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.85 2000/02/20 21:32:05 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@ -1191,6 +1191,35 @@ _readSubLink()
return local_node;
}
/* ----------------
* _readRelabelType
*
* RelabelType is a subclass of Node
* ----------------
*/
static RelabelType *
_readRelabelType()
{
RelabelType *local_node;
char *token;
int length;
local_node = makeNode(RelabelType);
token = lsptok(NULL, &length); /* eat :arg */
local_node->arg = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :resulttype */
token = lsptok(NULL, &length); /* get resulttype */
local_node->resulttype = (Oid) atol(token);
token = lsptok(NULL, &length); /* eat :resulttypmod */
token = lsptok(NULL, &length); /* get resulttypmod */
local_node->resulttypmod = atoi(token);
return local_node;
}
/*
* Stuff from execnodes.h
*/
@ -1820,6 +1849,8 @@ parsePlanString(void)
return_value = _readAggref();
else if (length == 7 && strncmp(token, "SUBLINK", length) == 0)
return_value = _readSubLink();
else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0)
return_value = _readRelabelType();
else if (length == 3 && strncmp(token, "AGG", length) == 0)
return_value = _readAgg();
else if (length == 4 && strncmp(token, "HASH", length) == 0)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.59 2000/02/15 03:37:36 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.60 2000/02/20 21:32:06 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -1150,6 +1150,37 @@ eval_const_expressions_mutator (Node *node, void *context)
newexpr->args = args;
return (Node *) newexpr;
}
if (IsA(node, RelabelType))
{
/*
* If we can simplify the input to a constant, then we don't need
* the RelabelType node anymore: just change the type field of
* the Const node. Otherwise keep the RelabelType node.
*
* XXX if relabel has a nondefault resulttypmod, do we need to
* keep it to show that? At present I don't think so.
*/
RelabelType *relabel = (RelabelType *) node;
Node *arg;
arg = eval_const_expressions_mutator(relabel->arg, context);
if (arg && IsA(arg, Const))
{
Const *con = (Const *) arg;
con->consttype = relabel->resulttype;
return (Node *) con;
}
else
{
RelabelType *newrelabel = makeNode(RelabelType);
newrelabel->arg = arg;
newrelabel->resulttype = relabel->resulttype;
newrelabel->resulttypmod = relabel->resulttypmod;
return (Node *) newrelabel;
}
}
if (IsA(node, CaseExpr))
{
/*
@ -1392,6 +1423,8 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
return true;
}
break;
case T_RelabelType:
return walker(((RelabelType *) node)->arg, context);
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
@ -1603,6 +1636,16 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
return (Node *) newnode;
}
break;
case T_RelabelType:
{
RelabelType *relabel = (RelabelType *) node;
RelabelType *newnode;
FLATCOPY(newnode, relabel, RelabelType);
MUTATE(newnode->arg, relabel->arg, Node *);
return (Node *) newnode;
}
break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;

View File

@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* parse_coerce.c
* handle type coersions/conversions for parser
* handle type coercions/conversions for parser
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.31 2000/02/20 06:28:42 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.32 2000/02/20 21:32:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,7 +36,7 @@ Node *
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
Oid targetTypeId, int32 atttypmod)
{
Node *result = NULL;
Node *result;
if (targetTypeId == InvalidOid ||
targetTypeId == inputTypeId)
@ -44,11 +44,6 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
/* no conversion needed */
result = node;
}
else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
{
/* no work if one of the known-good transparent conversions */
result = node;
}
else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
{
/*
@ -87,6 +82,27 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
result = (Node *) newcon;
}
else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
{
/*
* We don't really need to do a conversion, but we do need to attach
* a RelabelType node so that the expression will be seen to have
* the intended type when inspected by higher-level code.
*/
RelabelType *relabel = makeNode(RelabelType);
relabel->arg = node;
relabel->resulttype = targetTypeId;
/*
* XXX could we label result with exprTypmod(node) instead of
* default -1 typmod, to save a possible length-coercion later?
* Would work if both types have same interpretation of typmod,
* which is likely but not certain.
*/
relabel->resulttypmod = -1;
result = (Node *) relabel;
}
else
{
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.68 2000/02/15 03:37:47 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.69 2000/02/20 21:32:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -516,6 +516,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
case T_Param:
case T_Aggref:
case T_ArrayRef:
case T_RelabelType:
{
result = (Node *) expr;
break;
@ -627,6 +628,9 @@ exprType(Node *expr)
case T_Param:
type = ((Param *) expr)->paramtype;
break;
case T_RelabelType:
type = ((RelabelType *) expr)->resulttype;
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
@ -697,6 +701,9 @@ exprTypmod(Node *expr)
}
}
break;
case T_RelabelType:
return ((RelabelType *) expr)->resulttypmod;
break;
default:
break;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.70 2000/02/20 06:35:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.71 2000/02/20 21:32:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -163,7 +163,10 @@ agg_get_candidates(char *aggname,
} /* agg_get_candidates() */
/* agg_select_candidate()
* Try to choose only one candidate aggregate function from a list of possibles.
*
* Try to choose only one candidate aggregate function from a list of
* possible matches. Return value is Oid of input type of aggregate
* if successful, else InvalidOid.
*/
static Oid
agg_select_candidate(Oid typeid, CandidateList candidates)
@ -175,10 +178,12 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
CATEGORY category,
current_category;
/*
* First look for exact matches or binary compatible matches.
* (Of course exact matches shouldn't even get here, but anyway.)
*/
/*
* First look for exact matches or binary compatible matches.
* (Of course exact matches shouldn't even get here, but anyway.)
*/
ncandidates = 0;
last_candidate = NULL;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
@ -188,15 +193,17 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
if (current_typeid == typeid
|| IS_BINARY_COMPATIBLE(current_typeid, typeid))
{
/* we're home free */
return current_typeid;
last_candidate = current_candidate;
ncandidates++;
}
}
if (ncandidates == 1)
return last_candidate->args[0];
/*
* If no luck that way, look for candidates which allow coersion
* and have a preferred type. Keep all candidates if none match.
*/
/*
* If no luck that way, look for candidates which allow coercion
* and have a preferred type. Keep all candidates if none match.
*/
category = TypeCategory(typeid);
ncandidates = 0;
last_candidate = NULL;
@ -232,7 +239,10 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
if (last_candidate) /* terminate rebuilt list */
last_candidate->next = NULL;
return ((ncandidates == 1) ? candidates->args[0] : 0);
if (ncandidates == 1)
return candidates->args[0];
return InvalidOid;
} /* agg_select_candidate() */
@ -471,10 +481,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
/*
* See if this is a single argument function with the function
* name also a type name and the input argument and type name
* binary compatible... This means that you are trying for a
* type conversion which does not need to take place, so we'll
* just pass through the argument itself. (make this clearer
* with some extra brackets - thomas 1998-12-05)
* binary compatible. If so, we do not need to do any real
* conversion, but we do need to build a RelabelType node
* so that exprType() sees the result as being of the output type.
*/
if (nargs == 1)
{
@ -486,8 +495,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
if (HeapTupleIsValid(tp) &&
IS_BINARY_COMPATIBLE(typeTypeId(tp), exprType(lfirst(fargs))))
{
/* XXX FIXME: probably need to change expression's marked type? */
return (Node *) lfirst(fargs);
RelabelType *relabel = makeNode(RelabelType);
relabel->arg = (Node *) lfirst(fargs);
relabel->resulttype = typeTypeId(tp);
relabel->resulttypmod = -1;
return (Node *) relabel;
}
}
@ -1128,7 +1142,7 @@ func_get_detail(char *funcname,
* inheritance properties of the supplied argv.
*
* This function is used to disambiguate among functions with the
* same name but different signatures. It takes an array of eight
* same name but different signatures. It takes an array of input
* type ids. For each type id in the array that's a complex type
* (a class), it walks up the inheritance tree, finding all
* superclasses of that type. A vector of new Oid type arrays
@ -1342,7 +1356,7 @@ gen_cross_product(InhPaths *arginh, int nargs)
* 2) the input type can be typecast into the function type
* Right now, we only typecast unknowns, and that is all we check for.
*
* func_get_detail() now can find coersions for function arguments which
* func_get_detail() now can find coercions for function arguments which
* will make this function executable. So, we need to recover these
* results here too.
* - thomas 1998-03-25
@ -1361,7 +1375,7 @@ make_arguments(ParseState *pstate,
i < nargs;
i++, current_fargs = lnext(current_fargs))
{
/* types don't match? then force coersion using a function call... */
/* types don't match? then force coercion using a function call... */
if (input_typeids[i] != function_typeids[i])
{
lfirst(current_fargs) = coerce_type(pstate,

View File

@ -3,7 +3,7 @@
* out of its tuple
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.41 2000/02/15 08:24:12 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.42 2000/02/20 21:32:12 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -1006,7 +1006,7 @@ get_select_query_def(Query *query, deparse_context *context)
appendStringInfo(buf, "%s",
quote_identifier(strVal(lfirst(col))));
}
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
}
}
}
@ -1127,7 +1127,7 @@ get_insert_query_def(Query *query, deparse_context *context)
sep = ", ";
get_tle_expr(tle, context);
}
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
}
else
get_select_query_def(query, context);
@ -1281,7 +1281,7 @@ get_rule_expr(Node *node, deparse_context *context)
switch (expr->opType)
{
case OP_EXPR:
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
if (length(args) == 2)
{
/* binary operator */
@ -1320,35 +1320,35 @@ get_rule_expr(Node *node, deparse_context *context)
elog(ERROR, "get_rule_expr: bogus oprkind");
}
}
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
break;
case OR_EXPR:
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(args), context);
while ((args = lnext(args)) != NIL)
{
appendStringInfo(buf, " OR ");
get_rule_expr((Node *) lfirst(args), context);
}
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
break;
case AND_EXPR:
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(args), context);
while ((args = lnext(args)) != NIL)
{
appendStringInfo(buf, " AND ");
get_rule_expr((Node *) lfirst(args), context);
}
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
break;
case NOT_EXPR:
appendStringInfo(buf, "(NOT ");
get_rule_expr((Node *) lfirst(args), context);
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
break;
case FUNC_EXPR:
@ -1373,7 +1373,7 @@ get_rule_expr(Node *node, deparse_context *context)
appendStringInfo(buf, "*");
else
get_rule_expr(aggref->target, context);
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
}
break;
@ -1405,6 +1405,28 @@ get_rule_expr(Node *node, deparse_context *context)
}
break;
case T_RelabelType:
{
RelabelType *relabel = (RelabelType *) node;
HeapTuple typetup;
Form_pg_type typeStruct;
char *extval;
appendStringInfoChar(buf, '(');
get_rule_expr(relabel->arg, context);
typetup = SearchSysCacheTuple(TYPEOID,
ObjectIdGetDatum(relabel->resulttype),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup of type %u failed",
relabel->resulttype);
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
extval = pstrdup(NameStr(typeStruct->typname));
appendStringInfo(buf, ")::%s", quote_identifier(extval));
pfree(extval);
}
break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
@ -1474,14 +1496,14 @@ get_func_expr(Expr *expr, deparse_context *context)
{
if (!strcmp(proname, "nullvalue"))
{
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(expr->args), context);
appendStringInfo(buf, " ISNULL)");
return;
}
if (!strcmp(proname, "nonnullvalue"))
{
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) lfirst(expr->args), context);
appendStringInfo(buf, " NOTNULL)");
return;
@ -1500,7 +1522,7 @@ get_func_expr(Expr *expr, deparse_context *context)
sep = ", ";
get_rule_expr((Node *) lfirst(l), context);
}
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
}
@ -1712,13 +1734,13 @@ get_sublink_expr(Node *node, deparse_context *context)
Oper *oper;
bool need_paren;
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
if (sublink->lefthand != NIL)
{
need_paren = (length(sublink->lefthand) > 1);
if (need_paren)
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
sep = "";
foreach(l, sublink->lefthand)
@ -1731,7 +1753,7 @@ get_sublink_expr(Node *node, deparse_context *context)
if (need_paren)
appendStringInfo(buf, ") ");
else
appendStringInfo(buf, " ");
appendStringInfoChar(buf, ' ');
}
need_paren = true;
@ -1768,14 +1790,14 @@ get_sublink_expr(Node *node, deparse_context *context)
}
if (need_paren)
appendStringInfo(buf, "(");
appendStringInfoChar(buf, '(');
get_query_def(query, buf, context->rangetables);
if (need_paren)
appendStringInfo(buf, "))");
else
appendStringInfo(buf, ")");
appendStringInfoChar(buf, ')');
}
/* ----------

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.65 2000/02/18 09:29:43 inoue Exp $
* $Id: nodes.h,v 1.66 2000/02/20 21:32:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -67,6 +67,7 @@ typedef enum NodeTag
T_Array,
T_ArrayRef,
T_Iter,
T_RelabelType,
/*---------------------
* TAGS FOR PLANNER NODES (relation.h)

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.39 2000/01/26 05:58:16 momjian Exp $
* $Id: primnodes.h,v 1.40 2000/02/20 21:32:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -467,4 +467,28 @@ typedef struct ArrayRef
Node *refassgnexpr;
} ArrayRef;
/* ----------------
* RelabelType
* arg - input expression
* resulttype - output type of coercion expression
* resulttypmod - output typmod (usually -1)
*
* RelabelType represents a "dummy" type coercion between two binary-
* compatible datatypes, such as reinterpreting the result of an OID
* expression as an int4. It is a no-op at runtime; we only need it
* to provide a place to store the correct type to be attributed to
* the expression result during type resolution. (We can't get away
* with just overwriting the type field of the input expression node,
* so we need a separate node to show the coercion's result type.)
* ----------------
*/
typedef struct RelabelType
{
NodeTag type;
Node *arg;
Oid resulttype;
int32 resulttypmod;
} RelabelType;
#endif /* PRIMNODES_H */