aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/clausesel.c13
-rw-r--r--src/backend/optimizer/path/indxpath.c168
-rw-r--r--src/backend/optimizer/plan/createplan.c8
-rw-r--r--src/backend/optimizer/plan/initsplan.c19
-rw-r--r--src/backend/optimizer/plan/planmain.c22
-rw-r--r--src/backend/optimizer/util/clauses.c156
6 files changed, 293 insertions, 93 deletions
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index ba2e7e67b32..7d80e288199 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.38 2000/06/08 22:37:09 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.39 2000/08/13 02:50:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -123,7 +123,7 @@ clauselist_selectivity(Query *root,
Selectivity s2;
/*
- * See if it looks like a restriction clause with a Const or Param
+ * See if it looks like a restriction clause with a pseudoconstant
* on one side. (Anything more complicated than that might not
* behave in the simple way we are expecting.)
*
@@ -146,7 +146,7 @@ clauselist_selectivity(Query *root,
other = (flag & SEL_RIGHT) ? get_rightop((Expr *) clause) :
get_leftop((Expr *) clause);
- if (IsA(other, Const) || IsA(other, Param))
+ if (is_pseudo_constant_clause((Node *) other))
{
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
RegProcedure oprrest = get_oprrest(opno);
@@ -533,6 +533,13 @@ clause_selectivity(Query *root,
*/
s1 = 1.0;
}
+ else if (IsA(clause, RelabelType))
+ {
+ /* Not sure this case is needed, but it can't hurt */
+ s1 = clause_selectivity(root,
+ ((RelabelType *) clause)->arg,
+ varRelid);
+ }
return s1;
}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 13bf65e6990..27ae4056d99 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.92 2000/08/08 15:41:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.93 2000/08/13 02:50:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -649,7 +649,7 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
* a key of an index.
*
* To match, the clause:
-
+ *
* (1a) for a restriction clause: must be in the form (indexkey op const)
* or (const op indexkey), or
* (1b) for a join clause: must be in the form (indexkey op others)
@@ -708,11 +708,11 @@ match_clause_to_indexkey(RelOptInfo *rel,
/*
* Not considering joins, so check for clauses of the form:
* (indexkey operator constant) or (constant operator indexkey).
- * We will accept a Param as being constant.
+ * Anything that is a "pseudo constant" expression will do.
*/
- if ((IsA(rightop, Const) ||IsA(rightop, Param)) &&
- match_index_to_operand(indexkey, leftop, rel, index))
+ if (match_index_to_operand(indexkey, leftop, rel, index) &&
+ is_pseudo_constant_clause((Node *) rightop))
{
if (is_indexable_operator(clause, opclass, index->relam, true))
return true;
@@ -726,8 +726,8 @@ match_clause_to_indexkey(RelOptInfo *rel,
return true;
return false;
}
- if ((IsA(leftop, Const) ||IsA(leftop, Param)) &&
- match_index_to_operand(indexkey, rightop, rel, index))
+ if (match_index_to_operand(indexkey, rightop, rel, index) &&
+ is_pseudo_constant_clause((Node *) leftop))
{
if (is_indexable_operator(clause, opclass, index->relam, false))
return true;
@@ -748,29 +748,32 @@ match_clause_to_indexkey(RelOptInfo *rel,
/*
* Check for an indexqual that could be handled by a nestloop
* join. We need the index key to be compared against an
- * expression that uses none of the indexed relation's vars.
+ * expression that uses none of the indexed relation's vars
+ * and contains no non-cachable functions.
*/
if (match_index_to_operand(indexkey, leftop, rel, index))
{
List *othervarnos = pull_varnos((Node *) rightop);
bool isIndexable;
- isIndexable = !intMember(lfirsti(rel->relids), othervarnos);
+ isIndexable =
+ !intMember(lfirsti(rel->relids), othervarnos) &&
+ !contain_noncachable_functions((Node *) rightop) &&
+ is_indexable_operator(clause, opclass, index->relam, true);
freeList(othervarnos);
- if (isIndexable &&
- is_indexable_operator(clause, opclass, index->relam, true))
- return true;
+ return isIndexable;
}
else if (match_index_to_operand(indexkey, rightop, rel, index))
{
List *othervarnos = pull_varnos((Node *) leftop);
bool isIndexable;
- isIndexable = !intMember(lfirsti(rel->relids), othervarnos);
+ isIndexable =
+ !intMember(lfirsti(rel->relids), othervarnos) &&
+ !contain_noncachable_functions((Node *) leftop) &&
+ is_indexable_operator(clause, opclass, index->relam, false);
freeList(othervarnos);
- if (isIndexable &&
- is_indexable_operator(clause, opclass, index->relam, false))
- return true;
+ return isIndexable;
}
}
@@ -790,7 +793,9 @@ match_clause_to_indexkey(RelOptInfo *rel,
* recognizing binary-compatible datatypes. For example, if we have
* an expression like "oid = 123", the operator will be oideqint4,
* which we need to replace with oideq in order to recognize it as
- * matching an oid_ops index on the oid field.
+ * matching an oid_ops index on the oid field. A variant case is where
+ * the expression is like "oid::int4 = 123", where the given operator
+ * will be int4eq and again we need to intuit that we want to use oideq.
*
* Returns the OID of the matching operator, or InvalidOid if no match.
* Note that the returned OID will be different from the one in the given
@@ -804,8 +809,13 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
{
Oid expr_op = ((Oper *) clause->oper)->opno;
Oid commuted_op;
+ Operator oldop,
+ newop;
+ Form_pg_operator oldopform;
+ char *opname;
Oid ltype,
- rtype;
+ rtype,
+ indexkeytype;
/* Get the commuted operator if necessary */
if (indexkey_on_left)
@@ -821,48 +831,72 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
/*
* Maybe the index uses a binary-compatible operator set.
+ *
+ * Get the nominal input types of the given operator and the actual
+ * type (before binary-compatible relabeling) of the index key.
*/
- ltype = exprType((Node *) get_leftop(clause));
- rtype = exprType((Node *) get_rightop(clause));
+ oldop = get_operator_tuple(expr_op);
+ if (! HeapTupleIsValid(oldop))
+ return InvalidOid; /* probably can't happen */
+ oldopform = (Form_pg_operator) GETSTRUCT(oldop);
+ opname = NameStr(oldopform->oprname);
+ ltype = oldopform->oprleft;
+ rtype = oldopform->oprright;
+
+ if (indexkey_on_left)
+ {
+ Node *leftop = (Node *) get_leftop(clause);
+
+ if (leftop && IsA(leftop, RelabelType))
+ leftop = ((RelabelType *) leftop)->arg;
+ indexkeytype = exprType(leftop);
+ }
+ else
+ {
+ Node *rightop = (Node *) get_rightop(clause);
+
+ if (rightop && IsA(rightop, RelabelType))
+ rightop = ((RelabelType *) rightop)->arg;
+ indexkeytype = exprType(rightop);
+ }
/*
- * make sure we have two different binary-compatible types...
+ * Make sure we have different but binary-compatible types.
*/
- if (ltype != rtype && IS_BINARY_COMPATIBLE(ltype, rtype))
- {
- char *opname = get_opname(expr_op);
- Operator newop;
+ if (ltype == indexkeytype && rtype == indexkeytype)
+ return InvalidOid; /* no chance for a different operator */
+ if (ltype != indexkeytype && !IS_BINARY_COMPATIBLE(ltype, indexkeytype))
+ return InvalidOid;
+ if (rtype != indexkeytype && !IS_BINARY_COMPATIBLE(rtype, indexkeytype))
+ return InvalidOid;
- if (opname == NULL)
- return InvalidOid; /* probably shouldn't happen */
+ /*
+ * OK, look for operator of the same name with the indexkey's data type.
+ * (In theory this might find a non-semantically-comparable operator,
+ * but in practice that seems pretty unlikely for binary-compatible types.)
+ */
+ newop = oper(opname, indexkeytype, indexkeytype, TRUE);
- /* Use the datatype of the index key */
- if (indexkey_on_left)
- newop = oper(opname, ltype, ltype, TRUE);
- else
- newop = oper(opname, rtype, rtype, TRUE);
+ if (HeapTupleIsValid(newop))
+ {
+ Oid new_expr_op = oprid(newop);
- if (HeapTupleIsValid(newop))
+ if (new_expr_op != expr_op)
{
- Oid new_expr_op = oprid(newop);
-
- if (new_expr_op != expr_op)
- {
- /*
- * OK, we found a binary-compatible operator of the same
- * name; now does it match the index?
- */
- if (indexkey_on_left)
- commuted_op = new_expr_op;
- else
- commuted_op = get_commutator(new_expr_op);
- if (commuted_op == InvalidOid)
- return InvalidOid;
-
- if (op_class(commuted_op, opclass, relam))
- return new_expr_op;
- }
+ /*
+ * OK, we found a binary-compatible operator of the same
+ * name; now does it match the index?
+ */
+ if (indexkey_on_left)
+ commuted_op = new_expr_op;
+ else
+ commuted_op = get_commutator(new_expr_op);
+ if (commuted_op == InvalidOid)
+ return InvalidOid;
+
+ if (op_class(commuted_op, opclass, relam))
+ return new_expr_op;
}
}
@@ -1526,13 +1560,22 @@ match_index_to_operand(int indexkey,
RelOptInfo *rel,
IndexOptInfo *index)
{
+ /*
+ * Ignore any RelabelType node above the indexkey. This is needed to
+ * be able to apply indexscanning in binary-compatible-operator cases.
+ * Note: we can assume there is at most one RelabelType node;
+ * eval_const_expressions() will have simplified if more than one.
+ */
+ if (operand && IsA(operand, RelabelType))
+ operand = (Var *) ((RelabelType *) operand)->arg;
+
if (index->indproc == InvalidOid)
{
/*
- * Normal index.
+ * Simple index.
*/
- if (IsA(operand, Var) &&
+ if (operand && IsA(operand, Var) &&
lfirsti(rel->relids) == operand->varno &&
indexkey == operand->varattno)
return true;
@@ -1541,7 +1584,7 @@ match_index_to_operand(int indexkey,
}
/*
- * functional index check
+ * Functional index.
*/
return function_index_operand((Expr *) operand, rel, index);
}
@@ -1570,18 +1613,23 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
if (function->funcid != index->indproc)
return false;
- /*
+ /*----------
* Check that the arguments correspond to the same arguments used to
- * create the functional index. To do this we must check that 1.
- * refer to the right relation. 2. the args have the right attr.
- * numbers in the right order.
+ * create the functional index. To do this we must check that
+ * 1. they refer to the right relation.
+ * 2. the args have the right attr. numbers in the right order.
+ * We must ignore RelabelType nodes above the argument Vars in order
+ * to recognize binary-compatible-function cases correctly.
+ *----------
*/
i = 0;
foreach(arg, funcargs)
{
Var *var = (Var *) lfirst(arg);
- if (!IsA(var, Var))
+ if (var && IsA(var, RelabelType))
+ var = (Var *) ((RelabelType *) var)->arg;
+ if (var == NULL || !IsA(var, Var))
return false;
if (indexKeys[i] == 0)
return false;
@@ -1643,7 +1691,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
* additional indexscanable qualifications.
*
* The given clause is already known to be a binary opclause having
- * the form (indexkey OP const/param) or (const/param OP indexkey),
+ * the form (indexkey OP pseudoconst) or (pseudoconst OP indexkey),
* but the OP proved not to be one of the index's opclass operators.
* Return 'true' if we can do something with it anyway.
*/
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index bf91b8d1a6c..c049f5d86b6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.94 2000/07/12 02:37:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.95 2000/08/13 02:50:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -866,6 +866,12 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
Oid *opclass)
{
/*
+ * Remove any binary-compatible relabeling of the indexkey
+ */
+ if (IsA(node, RelabelType))
+ node = ((RelabelType *) node)->arg;
+
+ /*
* We represent index keys by Var nodes having the varno of the base
* table but varattno equal to the index's attribute number (index
* column position). This is a bit hokey ... would be cleaner to use
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index d2bbff7e600..8ffd35c9bb0 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.48 2000/08/08 15:41:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.49 2000/08/13 02:50:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -184,7 +184,7 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
/*
* There is only one relation participating in 'clause', so
- * 'clause' must be a restriction clause for that relation.
+ * 'clause' is a restriction clause for that relation.
*/
RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
@@ -201,11 +201,11 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
*/
check_mergejoinable(restrictinfo);
}
- else
+ else if (relids != NIL)
{
/*
- * 'clause' is a join clause, since there is more than one atom in
+ * 'clause' is a join clause, since there is more than one rel in
* the relid list. Set additional RestrictInfo fields for
* joining.
*
@@ -219,8 +219,6 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
/*
* Add clause to the join lists of all the relevant relations.
- * (If, perchance, 'clause' contains NO vars, then nothing will
- * happen...)
*/
add_join_info_to_rels(root, restrictinfo, relids);
@@ -232,6 +230,15 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
*/
add_vars_to_targetlist(root, vars);
}
+ else
+ {
+ /*
+ * 'clause' references no rels, and therefore we have no place to
+ * attach it. This means query_planner() screwed up --- it should
+ * treat variable-less clauses separately.
+ */
+ elog(ERROR, "add_restrict_and_join_to_rel: can't cope with variable-free clause");
+ }
/*
* If the clause has a mergejoinable operator, then the two sides
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 6b6bc15bda7..abb468aa8d1 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.57 2000/07/27 04:51:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.58 2000/08/13 02:50:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -78,7 +78,8 @@ query_planner(Query *root,
List *qual,
double tuple_fraction)
{
- List *constant_qual = NIL;
+ List *noncachable_qual;
+ List *constant_qual;
List *var_only_tlist;
Plan *subplan;
@@ -106,9 +107,14 @@ query_planner(Query *root,
* have been optimized away by eval_const_expressions(). What we're
* mostly interested in here is quals that depend only on outer-level
* vars, although if the qual reduces to "WHERE FALSE" this path will
- * also be taken.
+ * also be taken. We also need a special case for quals that contain
+ * noncachable functions but no vars, such as "WHERE random() < 0.5".
+ * These cannot be treated as normal restriction or join quals, but
+ * they're not constants either. Instead, attach them to the qpqual
+ * of the top-level plan, so that they get evaluated once per potential
+ * output tuple.
*/
- qual = pull_constant_clauses(qual, &constant_qual);
+ qual = pull_constant_clauses(qual, &noncachable_qual, &constant_qual);
/*
* Create a target list that consists solely of (resdom var) target
@@ -129,6 +135,12 @@ query_planner(Query *root,
subplan = subplanner(root, var_only_tlist, qual, tuple_fraction);
/*
+ * Handle the noncachable quals.
+ */
+ if (noncachable_qual)
+ subplan->qual = nconc(subplan->qual, noncachable_qual);
+
+ /*
* Build a result node to control the plan if we have constant quals.
*/
if (constant_qual)
@@ -163,7 +175,7 @@ query_planner(Query *root,
* for processing a single level of attributes.
*
* flat_tlist is the flattened target list
- * qual is the qualification to be satisfied
+ * qual is the qualification to be satisfied (restrict and join quals only)
* tuple_fraction is the fraction of tuples we expect will be retrieved
*
* See query_planner() comments about the interpretation of tuple_fraction.
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index adda68b636a..65e71456360 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.70 2000/08/08 15:41:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.71 2000/08/13 02:50:10 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -46,6 +46,7 @@ static bool contain_subplans_walker(Node *node, void *context);
static bool pull_subplans_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
Query *context);
+static bool contain_noncachable_functions_walker(Node *node, void *context);
static int is_single_func(Node *node);
static Node *eval_const_expressions_mutator(Node *node, void *context);
static Expr *simplify_op_or_func(Expr *expr, List *args);
@@ -601,40 +602,135 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
/*****************************************************************************
- * *
- * General clause-manipulating routines *
- * *
+ * Check clauses for noncachable functions
*****************************************************************************/
-
/*
- * pull_constant_clauses
- * Scans through a list of qualifications and find those that
- * contain no variables (of the current query level).
+ * contain_noncachable_functions
+ * Recursively search for noncachable functions within a clause.
*
- * Returns a list of the constant clauses in constantQual and the remaining
- * quals as the return value.
+ * Returns true if any noncachable function (or operator implemented by a
+ * noncachable function) is found. This test is needed so that we don't
+ * mistakenly think that something like "WHERE random() < 0.5" can be treated
+ * as a constant qualification.
*
+ * XXX we do not examine sublinks/subplans to see if they contain uses of
+ * noncachable functions. It's not real clear if that is correct or not...
+ */
+bool
+contain_noncachable_functions(Node *clause)
+{
+ return contain_noncachable_functions_walker(clause, NULL);
+}
+
+static bool
+contain_noncachable_functions_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Expr))
+ {
+ Expr *expr = (Expr *) node;
+
+ switch (expr->opType)
+ {
+ case OP_EXPR:
+ if (! op_iscachable(((Oper *) expr->oper)->opno))
+ return true;
+ break;
+ case FUNC_EXPR:
+ if (! func_iscachable(((Func *) expr->oper)->funcid))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+ return expression_tree_walker(node, contain_noncachable_functions_walker,
+ context);
+}
+
+
+/*****************************************************************************
+ * Check for "pseudo-constant" clauses
+ *****************************************************************************/
+
+/*
+ * is_pseudo_constant_clause
+ * Detect whether a clause is "constant", ie, it contains no variables
+ * of the current query level and no uses of noncachable functions.
+ * Such a clause is not necessarily a true constant: it can still contain
+ * Params and outer-level Vars. However, its value will be constant over
+ * any one scan of the current query, so it can be used as an indexscan
+ * key or (if a top-level qual) can be pushed up to become a gating qual.
+ */
+bool
+is_pseudo_constant_clause(Node *clause)
+{
+ /*
+ * We could implement this check in one recursive scan. But since the
+ * check for noncachable functions is both moderately expensive and
+ * unlikely to fail, it seems better to look for Vars first and only
+ * check for noncachable functions if we find no Vars.
+ */
+ if (!contain_var_clause(clause) &&
+ !contain_noncachable_functions(clause))
+ return true;
+ return false;
+}
+
+/*----------
+ * pull_constant_clauses
+ * Scan through a list of qualifications and separate "constant" quals
+ * from those that are not.
+ *
+ * The input qual list is divided into three parts:
+ * * The function's return value is a list of all those quals that contain
+ * variable(s) of the current query level. (These quals will become
+ * restrict and join quals.)
+ * * *noncachableQual receives a list of quals that have no Vars, yet
+ * cannot be treated as constants because they contain noncachable
+ * function calls. (Example: WHERE random() < 0.5)
+ * * *constantQual receives a list of the remaining quals, which can be
+ * treated as constants for any one scan of the current query level.
+ * (They are really only pseudo-constant, since they may contain
+ * Params or outer-level Vars.)
+ *----------
*/
List *
-pull_constant_clauses(List *quals, List **constantQual)
+pull_constant_clauses(List *quals,
+ List **noncachableQual,
+ List **constantQual)
{
List *q;
+ List *normqual = NIL;
+ List *noncachequal = NIL;
List *constqual = NIL;
- List *restqual = NIL;
foreach(q, quals)
{
- if (!contain_var_clause(lfirst(q)))
- constqual = lcons(lfirst(q), constqual);
+ Node *qual = (Node *) lfirst(q);
+
+ if (contain_var_clause(qual))
+ normqual = lappend(normqual, qual);
+ else if (contain_noncachable_functions(qual))
+ noncachequal = lappend(noncachequal, qual);
else
- restqual = lcons(lfirst(q), restqual);
+ constqual = lappend(constqual, qual);
}
+
+ *noncachableQual = noncachequal;
*constantQual = constqual;
- return restqual;
+ return normqual;
}
+/*****************************************************************************
+ * *
+ * General clause-manipulating routines *
+ * *
+ *****************************************************************************/
+
/*
* clause_relids_vars
* Retrieves distinct relids and vars appearing within a clause.
@@ -744,6 +840,13 @@ get_relattval(Node *clause,
if (!right)
goto default_results;
+ /* Ignore any binary-compatible relabeling */
+
+ if (IsA(left, RelabelType))
+ left = (Var *) ((RelabelType *) left)->arg;
+ if (IsA(right, RelabelType))
+ right = (Var *) ((RelabelType *) right)->arg;
+
/* First look for the var or func */
if (IsA(left, Var) &&
@@ -856,6 +959,12 @@ get_rels_atts(Node *clause,
{
int funcvarno;
+ /* Ignore any binary-compatible relabeling */
+ if (IsA(left, RelabelType))
+ left = (Var *) ((RelabelType *) left)->arg;
+ if (IsA(right, RelabelType))
+ right = (Var *) ((RelabelType *) right)->arg;
+
if (IsA(left, Var))
{
*relid1 = left->varno;
@@ -1147,12 +1256,20 @@ eval_const_expressions_mutator(Node *node, void *context)
/*
* 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, copy the RelabelType node.
+ * Const node. Otherwise, must copy the RelabelType node.
*/
RelabelType *relabel = (RelabelType *) node;
Node *arg;
arg = eval_const_expressions_mutator(relabel->arg, context);
+
+ /*
+ * If we find stacked RelabelTypes (eg, from foo :: int :: oid)
+ * we can discard all but the top one.
+ */
+ while (arg && IsA(arg, RelabelType))
+ arg = ((RelabelType *) arg)->arg;
+
if (arg && IsA(arg, Const))
{
Const *con = (Const *) arg;
@@ -1369,7 +1486,10 @@ simplify_op_or_func(Expr *expr, List *args)
funcid = func->funcid;
result_typeid = func->functype;
}
- /* Someday lsyscache.c might provide a function for this */
+ /*
+ * we could use func_iscachable() here, but we need several fields
+ * out of the func tuple, so might as well just look it up once.
+ */
func_tuple = SearchSysCacheTuple(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);