Use Params, rather than run-time-modified Const nodes, to handle

sublink results and COPY's domain constraint checking.  A Const that
isn't really constant is just a Bad Idea(tm).  Remove hacks in
parse_coerce and other places that were needed because of the former
klugery.
This commit is contained in:
Tom Lane 2002-11-26 03:01:59 +00:00
parent ac47950238
commit ea0b5c8569
9 changed files with 166 additions and 190 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.182 2002/11/25 21:29:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.183 2002/11/26 03:01:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -749,7 +749,6 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
FmgrInfo *in_functions;
Oid *elements;
Node **constraintexprs;
Const **constraintconsts;
bool hasConstraints = false;
int i;
List *cur;
@ -805,7 +804,6 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
defmap = (int *) palloc(num_phys_attrs * sizeof(int));
defexprs = (Node **) palloc(num_phys_attrs * sizeof(Node *));
constraintexprs = (Node **) palloc0(num_phys_attrs * sizeof(Node *));
constraintconsts = (Const **) palloc(num_phys_attrs * sizeof(Const *));
for (i = 0; i < num_phys_attrs; i++)
{
@ -840,7 +838,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* If it's a domain type, get info on domain constraints */
if (get_typtype(attr[i]->atttypid) == 'd')
{
Const *con;
Param *prm;
Node *node;
/*
@ -848,28 +846,21 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
* an expression that checks the constraints. (At present,
* the expression might contain a length-coercion-function call
* and/or ConstraintTest nodes.) The bottom of the expression
* is a Const node that we fill in with the actual datum during
* is a Param node so that we can fill in the actual datum during
* the data input loop.
*
* XXX to prevent premature constant folding in parse_coerce,
* pass in a NULL constant to start with. See the comments in
* coerce_type; this should be changed someday to use some sort
* of Param node instead of a Const.
*/
con = makeConst(attr[i]->atttypid,
attr[i]->attlen,
(Datum) 0,
true, /* is null */
attr[i]->attbyval);
prm = makeNode(Param);
prm->paramkind = PARAM_EXEC;
prm->paramid = 0;
prm->paramtype = attr[i]->atttypid;
node = coerce_type_constraints((Node *) con, attr[i]->atttypid,
node = coerce_type_constraints((Node *) prm, attr[i]->atttypid,
COERCE_IMPLICIT_CAST);
/* check whether any constraints actually found */
if (node != (Node *) con)
if (node != (Node *) prm)
{
constraintexprs[i] = node;
constraintconsts[i] = con;
hasConstraints = true;
}
}
@ -931,6 +922,11 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
econtext = GetPerTupleExprContext(estate);
/* Make room for a PARAM_EXEC value for domain constraint checks */
if (hasConstraints)
econtext->ecxt_param_exec_vals = (ParamExecData *)
palloc0(sizeof(ParamExecData));
while (!done)
{
bool skip_tuple;
@ -1153,19 +1149,19 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
*/
if (hasConstraints)
{
ParamExecData *prmdata = &econtext->ecxt_param_exec_vals[0];
for (i = 0; i < num_phys_attrs; i++)
{
Node *node = constraintexprs[i];
Const *con;
bool isnull;
if (node == NULL)
continue; /* no constraint for this attr */
/* Insert current row's value into the Const node */
con = constraintconsts[i];
con->constvalue = values[i];
con->constisnull = (nulls[i] == 'n');
/* Insert current row's value into the Param value */
prmdata->value = values[i];
prmdata->isnull = (nulls[i] == 'n');
/*
* Execute the constraint expression. Allow the expression

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.33 2002/06/20 20:29:28 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.34 2002/11/26 03:01:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -150,37 +150,44 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
foreach(lst, sublink->oper)
{
Expr *expr = (Expr *) lfirst(lst);
Const *con = lsecond(expr->args);
Param *prm = lsecond(expr->args);
ParamExecData *prmdata;
Datum expresult;
bool expnull;
/*
* The righthand side of the expression should be either a
* Const or a function call or RelabelType node taking a Const
* Param or a function call or RelabelType node taking a Param
* as arg (these nodes represent run-time type coercions
* inserted by the parser to get to the input type needed by
* the operator). Find the Const node and insert the actual
* righthand-side value into it.
* the operator). Find the Param node and insert the actual
* righthand-side value into the param's econtext slot.
*
* XXX possible improvement: could make a list of the ParamIDs
* at startup time, instead of repeating this check at each row.
*/
if (!IsA(con, Const))
if (!IsA(prm, Param))
{
switch (con->type)
switch (nodeTag(prm))
{
case T_Expr:
con = lfirst(((Expr *) con)->args);
prm = lfirst(((Expr *) prm)->args);
break;
case T_RelabelType:
con = (Const *) (((RelabelType *) con)->arg);
prm = (Param *) (((RelabelType *) prm)->arg);
break;
default:
/* will fail below */
break;
}
if (!IsA(con, Const))
if (!IsA(prm, Param))
elog(ERROR, "ExecSubPlan: failed to find placeholder for subplan result");
}
con->constvalue = heap_getattr(tup, col, tdesc,
&(con->constisnull));
Assert(prm->paramkind == PARAM_EXEC);
prmdata = &(econtext->ecxt_param_exec_vals[prm->paramid]);
Assert(prmdata->execPlan == NULL);
prmdata->value = heap_getattr(tup, col, tdesc,
&(prmdata->isnull));
/*
* Now we can eval the combining operator for this column.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.130 2002/11/21 00:42:19 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.131 2002/11/26 03:01:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -720,8 +720,6 @@ preprocess_expression(Query *parse, Node *expr, int kind)
*
* Note that at this point quals have not yet been converted to
* implicit-AND form, so we can apply eval_const_expressions directly.
* Also note that we need to do this before SS_process_sublinks,
* because that routine inserts bogus "Const" nodes.
*/
expr = eval_const_expressions(expr);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.55 2002/09/04 20:31:21 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.56 2002/11/26 03:01:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -44,24 +44,32 @@ int PlannerPlanId = 0; /* to assign unique ID to subquery plans */
* and update the list elements when we enter or exit a subplan
* recursion level. But we must pay attention not to confuse this
* meaning with the normal meaning of varlevelsup.
*
* We also need to create Param slots that don't correspond to any outer Var.
* For these, we set varno = 0 and varlevelsup = 0, so that they can't
* accidentally match an outer Var.
*--------------------
*/
static void convert_sublink_opers(SubLink *slink, List *targetlist,
List **setParams);
/*
* Create a new entry in the PlannerParamVar list, and return its index.
*
* var contains the data to be copied, except for varlevelsup which
* is set from the absolute level value given by varlevel.
* var contains the data to use, except for varlevelsup which
* is set from the absolute level value given by varlevel. NOTE that
* the passed var is scribbled on and placed directly into the list!
* Generally, caller should have just created or copied it.
*/
static int
new_param(Var *var, Index varlevel)
{
Var *paramVar = (Var *) copyObject(var);
var->varlevelsup = varlevel;
paramVar->varlevelsup = varlevel;
PlannerParamVar = lappend(PlannerParamVar, paramVar);
PlannerParamVar = lappend(PlannerParamVar, var);
return length(PlannerParamVar) - 1;
}
@ -107,7 +115,7 @@ replace_var(Var *var)
if (!ppv)
{
/* Nope, so make a new one */
i = new_param(var, varlevel);
i = new_param((Var *) copyObject(var), varlevel);
}
retval = makeNode(Param);
@ -118,6 +126,22 @@ replace_var(Var *var)
return retval;
}
/*
* Generate a new Param node that will not conflict with any other.
*/
static Param *
generate_new_param(Oid paramtype, int32 paramtypmod)
{
Var *var = makeVar(0, 0, paramtype, paramtypmod, 0);
Param *retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
retval->paramid = (AttrNumber) new_param(var, 0);
retval->paramtype = paramtype;
return retval;
}
/*
* Convert a bare SubLink (as created by the parser) into a SubPlan.
*/
@ -216,13 +240,9 @@ make_subplan(SubLink *slink)
*/
if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
{
Var *var = makeVar(0, 0, BOOLOID, -1, 0);
Param *prm = makeNode(Param);
Param *prm;
prm->paramkind = PARAM_EXEC;
prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
prm->paramtype = var->vartype;
pfree(var); /* var is only needed for new_param */
prm = generate_new_param(BOOLOID, -1);
node->setParam = lappendi(node->setParam, prm->paramid);
PlannerInitPlan = lappend(PlannerInitPlan, node);
result = (Node *) prm;
@ -230,89 +250,27 @@ make_subplan(SubLink *slink)
else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
{
TargetEntry *te = lfirst(plan->targetlist);
Param *prm;
/* need a var node just to pass to new_param()... */
Var *var = makeVar(0, 0, te->resdom->restype,
te->resdom->restypmod, 0);
Param *prm = makeNode(Param);
prm->paramkind = PARAM_EXEC;
prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
prm->paramtype = var->vartype;
pfree(var); /* var is only needed for new_param */
prm = generate_new_param(te->resdom->restype, te->resdom->restypmod);
node->setParam = lappendi(node->setParam, prm->paramid);
PlannerInitPlan = lappend(PlannerInitPlan, node);
result = (Node *) prm;
}
else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
{
List *newoper = NIL;
int i = 0;
/*
* Convert oper list of Opers into a list of Exprs, using lefthand
* arguments and Params representing inside results.
*/
foreach(lst, slink->oper)
{
Oper *oper = (Oper *) lfirst(lst);
Node *lefthand = nth(i, slink->lefthand);
TargetEntry *te = nth(i, plan->targetlist);
/* need a var node just to pass to new_param()... */
Var *var = makeVar(0, 0, te->resdom->restype,
te->resdom->restypmod, 0);
Param *prm = makeNode(Param);
Operator tup;
Form_pg_operator opform;
Node *left,
*right;
prm->paramkind = PARAM_EXEC;
prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
prm->paramtype = var->vartype;
pfree(var); /* var is only needed for new_param */
Assert(IsA(oper, Oper));
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oper->opno),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for operator %u", oper->opno);
opform = (Form_pg_operator) GETSTRUCT(tup);
/*
* Note: we use make_operand in case runtime type conversion
* function calls must be inserted for this operator!
*/
left = make_operand(lefthand,
exprType(lefthand), opform->oprleft);
right = make_operand((Node *) prm,
prm->paramtype, opform->oprright);
ReleaseSysCache(tup);
newoper = lappend(newoper,
make_opclause(oper,
(Var *) left,
(Var *) right));
node->setParam = lappendi(node->setParam, prm->paramid);
i++;
}
slink->oper = newoper;
slink->lefthand = NIL;
convert_sublink_opers(slink, plan->targetlist, &node->setParam);
PlannerInitPlan = lappend(PlannerInitPlan, node);
if (i > 1)
result = (Node *) ((slink->useor) ? make_orclause(newoper) :
make_andclause(newoper));
if (length(slink->oper) > 1)
result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
make_andclause(slink->oper));
else
result = (Node *) lfirst(newoper);
result = (Node *) lfirst(slink->oper);
}
else
{
Expr *expr = makeNode(Expr);
List *args = NIL;
List *newoper = NIL;
int i = 0;
/*
* We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
@ -379,6 +337,9 @@ make_subplan(SubLink *slink)
}
}
/* Fix the SubLink's oper list */
convert_sublink_opers(slink, plan->targetlist, NULL);
/*
* Make expression of SUBPLAN type
*/
@ -405,55 +366,84 @@ make_subplan(SubLink *slink)
}
expr->args = args;
/*
* Convert oper list of Opers into a list of Exprs, using lefthand
* arguments and Consts representing inside results.
*/
foreach(lst, slink->oper)
{
Oper *oper = (Oper *) lfirst(lst);
Node *lefthand = nth(i, slink->lefthand);
TargetEntry *te = nth(i, plan->targetlist);
Const *con;
Operator tup;
Form_pg_operator opform;
Node *left,
*right;
con = makeNullConst(te->resdom->restype);
Assert(IsA(oper, Oper));
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oper->opno),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for operator %u", oper->opno);
opform = (Form_pg_operator) GETSTRUCT(tup);
/*
* Note: we use make_operand in case runtime type conversion
* function calls must be inserted for this operator!
*/
left = make_operand(lefthand,
exprType(lefthand), opform->oprleft);
right = make_operand((Node *) con,
con->consttype, opform->oprright);
ReleaseSysCache(tup);
newoper = lappend(newoper,
make_opclause(oper,
(Var *) left,
(Var *) right));
i++;
}
slink->oper = newoper;
slink->lefthand = NIL;
result = (Node *) expr;
}
return result;
}
/*
* convert_sublink_opers: convert a SubLink's oper list from the
* parser/rewriter format into the executor's format.
*
* The oper list is initially just a list of Oper nodes. We replace it
* with a list of actually executable expressions, in which the specified
* operators are applied to corresponding elements of the lefthand list
* and Params representing the results of the subplan. lefthand is then
* set to NIL.
*
* If setParams is not NULL, the paramids of the Params created are added
* to the *setParams list.
*/
static void
convert_sublink_opers(SubLink *slink, List *targetlist,
List **setParams)
{
List *newoper = NIL;
List *leftlist = slink->lefthand;
List *lst;
foreach(lst, slink->oper)
{
Oper *oper = (Oper *) lfirst(lst);
Node *lefthand = lfirst(leftlist);
TargetEntry *te = lfirst(targetlist);
Param *prm;
Operator tup;
Form_pg_operator opform;
Node *left,
*right;
/* Make the Param node representing the subplan's result */
prm = generate_new_param(te->resdom->restype,
te->resdom->restypmod);
/* Record its ID if needed */
if (setParams)
*setParams = lappendi(*setParams, prm->paramid);
/* Look up the operator to check its declared input types */
Assert(IsA(oper, Oper));
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oper->opno),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for operator %u", oper->opno);
opform = (Form_pg_operator) GETSTRUCT(tup);
/*
* Make the expression node.
*
* Note: we use make_operand in case runtime type conversion
* function calls must be inserted for this operator!
*/
left = make_operand(lefthand, exprType(lefthand), opform->oprleft);
right = make_operand((Node *) prm, prm->paramtype, opform->oprright);
newoper = lappend(newoper,
make_opclause(oper,
(Var *) left,
(Var *) right));
ReleaseSysCache(tup);
leftlist = lnext(leftlist);
targetlist = lnext(targetlist);
}
slink->oper = newoper;
slink->lefthand = NIL;
}
/*
* finalize_primnode: build lists of subplans and params appearing
* in the given expression tree. NOTE: items are added to lists passed in,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.112 2002/11/25 21:29:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.113 2002/11/26 03:01:58 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -1135,14 +1135,6 @@ CommuteClause(Expr *clause)
*
* We assume that the tree has already been type-checked and contains
* only operators and functions that are reasonable to try to execute.
*
* This routine should be invoked before converting sublinks to subplans
* (subselect.c's SS_process_sublinks()). The converted form contains
* bogus "Const" nodes that are actually placeholders where the executor
* will insert values from the inner plan, and obviously we mustn't try
* to reduce the expression as though these were really constants.
* As a safeguard, if we happen to find an already-converted SubPlan node,
* we will return it unchanged rather than recursing into it.
*--------------------
*/
Node *
@ -1411,11 +1403,13 @@ eval_const_expressions_mutator(Node *node, void *context)
case SUBPLAN_EXPR:
/*
* Safety measure per notes at head of this routine:
* return a SubPlan unchanged. Too late to do anything
* Return a SubPlan unchanged --- too late to do anything
* with it. The arglist simplification above was wasted
* work (the list probably only contains Var nodes
* anyway).
*
* XXX should we elog() here instead? Probably this routine
* should never be invoked after SubPlan creation.
*/
return (Node *) expr;
default:
@ -1629,7 +1623,6 @@ simplify_op_or_func(Expr *expr, List *args)
*
* Note we take the result type from the Oper or Func node, not the
* pg_proc tuple; probably necessary for binary-compatibility cases.
*
*/
if (expr->opType == OP_EXPR)
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.87 2002/11/25 21:29:41 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.88 2002/11/26 03:01:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -241,14 +241,8 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
*
* Note that no folding will occur if the conversion function is
* not marked 'immutable'.
*
* HACK: if constant is NULL, don't fold it here. This is needed
* by make_subplan(), which calls this routine on placeholder
* Const nodes that mustn't be collapsed. (It'd be a lot
* cleaner to make a separate node type for that purpose...)
*/
if (IsA(node, Const) &&
!((Const *) node)->constisnull)
if (IsA(node, Const))
result = eval_const_expressions(result);
}
else

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.130 2002/11/15 02:50:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.131 2002/11/26 03:01:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -122,7 +122,6 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
param = makeNode(Param);
param->paramkind = PARAM_NUM;
param->paramid = (AttrNumber) paramno;
param->paramname = "<unnamed>";
param->paramtype = paramtyp;
result = (Node *) param;
/* handle qualification, if any */

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.126 2002/11/25 21:29:41 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.127 2002/11/26 03:01:58 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -1921,7 +1921,6 @@ get_rule_expr(Node *node, deparse_context *context,
* same expression tree.
*
* There might be some work left here to support additional node types.
* Can we ever see Param nodes here?
*/
switch (nodeTag(node))
{

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.69 2002/11/25 21:29:42 tgl Exp $
* $Id: primnodes.h,v 1.70 2002/11/26 03:01:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -357,7 +357,7 @@ typedef struct Aggref
* 3. Finally, the planner converts the oper list to a list of normal Expr
* nodes representing the application of the operator(s) to the lefthand
* expressions and values from the inner targetlist. The inner
* targetlist items are represented by placeholder Param or Const nodes.
* targetlist items are represented by placeholder Param nodes.
* The lefthand field is set to NIL, since its expressions are now in
* the Expr list. This representation is passed to the executor.
*