aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/var.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/var.c')
-rw-r--r--src/backend/optimizer/util/var.c375
1 files changed, 374 insertions, 1 deletions
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 2521ffb2b6c..568e024d20b 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,15 +8,18 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "nodes/makefuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
#include "optimizer/var.h"
+#include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
typedef struct
@@ -38,6 +41,12 @@ typedef struct
bool includeUpperVars;
} pull_var_clause_context;
+typedef struct
+{
+ Query *root;
+ int expandRTI;
+} flatten_join_alias_vars_context;
+
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
static bool contain_var_reference_walker(Node *node,
@@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node,
static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context);
+static Node *flatten_join_alias_vars_mutator(Node *node,
+ flatten_join_alias_vars_context *context);
+static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI);
+static Node *find_jointree_item(Node *jtnode, int rtindex);
/*
@@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
return expression_tree_walker(node, pull_var_clause_walker,
(void *) context);
}
+
+
+/*
+ * flatten_join_alias_vars
+ * Whereever possible, replace Vars that reference JOIN outputs with
+ * references to the original relation variables instead. This allows
+ * quals involving such vars to be pushed down. Vars that cannot be
+ * simplified to non-join Vars are replaced by COALESCE expressions
+ * if they have varno = expandRTI, and are left as JOIN RTE references
+ * otherwise. (Pass expandRTI = 0 to prevent all COALESCE expansion.)
+ *
+ * Upper-level vars (with varlevelsup > 0) are ignored; normally there
+ * should not be any by the time this routine is called.
+ *
+ * Does not examine subqueries, therefore must only be used after reduction
+ * of sublinks to subplans!
+ */
+Node *
+flatten_join_alias_vars(Node *node, Query *root, int expandRTI)
+{
+ flatten_join_alias_vars_context context;
+
+ context.root = root;
+ context.expandRTI = expandRTI;
+
+ return flatten_join_alias_vars_mutator(node, &context);
+}
+
+static Node *
+flatten_join_alias_vars_mutator(Node *node,
+ flatten_join_alias_vars_context *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup != 0)
+ return node; /* no need to copy, really */
+ return flatten_join_alias_var(var, context->root, context->expandRTI);
+ }
+ return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
+ (void *) context);
+}
+
+static Node *
+flatten_join_alias_var(Var *var, Query *root, int expandRTI)
+{
+ Index varno = var->varno;
+ AttrNumber varattno = var->varattno;
+ Oid vartype = var->vartype;
+ int32 vartypmod = var->vartypmod;
+ JoinExpr *jexpr = NULL;
+
+ /*
+ * Loop to cope with joins of joins
+ */
+ for (;;)
+ {
+ RangeTblEntry *rte = rt_fetch(varno, root->rtable);
+ Index leftrti,
+ rightrti;
+ AttrNumber leftattno,
+ rightattno;
+ RangeTblEntry *subrte;
+ Oid subtype;
+ int32 subtypmod;
+
+ if (rte->rtekind != RTE_JOIN)
+ break; /* reached a non-join RTE */
+ /*
+ * Find the RT indexes of the left and right children of the
+ * join node. We have to search the join tree to do this,
+ * which is a major pain in the neck --- but keeping RT indexes
+ * in other RT entries is worse, because it makes modifying
+ * querytrees difficult. (Perhaps we can improve on the
+ * rangetable/jointree datastructure someday.) One thing we
+ * can do is avoid repeated searches while tracing a single
+ * variable down to its baserel.
+ */
+ if (jexpr == NULL)
+ jexpr = (JoinExpr *)
+ find_jointree_item((Node *) root->jointree, varno);
+ if (jexpr == NULL ||
+ !IsA(jexpr, JoinExpr) ||
+ jexpr->rtindex != varno)
+ elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr");
+ if (IsA(jexpr->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+ else if (IsA(jexpr->larg, JoinExpr))
+ leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+ else
+ {
+ elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
+ }
+ if (IsA(jexpr->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+ else if (IsA(jexpr->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+ else
+ {
+ elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
+ }
+ /*
+ * See if the join var is from the left side, the right side,
+ * or both (ie, it is a USING/NATURAL JOIN merger column).
+ */
+ Assert(varattno > 0);
+ leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+ rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+ if (leftattno && rightattno)
+ {
+ /*
+ * Var is a merge var. If a left or right join, we can replace
+ * it by the left or right input var respectively; we only need
+ * a COALESCE for a full join. However, beware of the possibility
+ * that there's been a type promotion to make the input vars
+ * compatible; do not replace a var by one of a different type!
+ */
+ if (rte->jointype == JOIN_INNER ||
+ rte->jointype == JOIN_LEFT)
+ {
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ if (vartype == subtype && vartypmod == subtypmod)
+ {
+ varno = leftrti;
+ varattno = leftattno;
+ jexpr = (JoinExpr *) jexpr->larg;
+ continue;
+ }
+ }
+ if (rte->jointype == JOIN_INNER ||
+ rte->jointype == JOIN_RIGHT)
+ {
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ if (vartype == subtype && vartypmod == subtypmod)
+ {
+ varno = rightrti;
+ varattno = rightattno;
+ jexpr = (JoinExpr *) jexpr->rarg;
+ continue;
+ }
+ }
+ /*
+ * This var cannot be substituted directly, only with a COALESCE.
+ * Do so only if it belongs to the particular join indicated by
+ * the caller.
+ */
+ if (varno != expandRTI)
+ break;
+ {
+ Node *l_var,
+ *r_var;
+ CaseExpr *c = makeNode(CaseExpr);
+ CaseWhen *w = makeNode(CaseWhen);
+ NullTest *n = makeNode(NullTest);
+
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ l_var = (Node *) makeVar(leftrti,
+ leftattno,
+ subtype,
+ subtypmod,
+ 0);
+ if (subtype != vartype)
+ {
+ l_var = coerce_type(NULL, l_var, subtype,
+ vartype, vartypmod);
+ l_var = coerce_type_typmod(NULL, l_var,
+ vartype, vartypmod);
+ }
+ else if (subtypmod != vartypmod)
+ l_var = coerce_type_typmod(NULL, l_var,
+ vartype, vartypmod);
+
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ r_var = (Node *) makeVar(rightrti,
+ rightattno,
+ subtype,
+ subtypmod,
+ 0);
+ if (subtype != vartype)
+ {
+ r_var = coerce_type(NULL, r_var, subtype,
+ vartype, vartypmod);
+ r_var = coerce_type_typmod(NULL, r_var,
+ vartype, vartypmod);
+ }
+ else if (subtypmod != vartypmod)
+ r_var = coerce_type_typmod(NULL, r_var,
+ vartype, vartypmod);
+
+ n->arg = l_var;
+ n->nulltesttype = IS_NOT_NULL;
+ w->expr = (Node *) n;
+ w->result = l_var;
+ c->casetype = vartype;
+ c->args = makeList1(w);
+ c->defresult = r_var;
+ return (Node *) c;
+ }
+ }
+ else if (leftattno)
+ {
+ /* Here we do not need to check the type */
+ varno = leftrti;
+ varattno = leftattno;
+ jexpr = (JoinExpr *) jexpr->larg;
+ }
+ else
+ {
+ Assert(rightattno);
+ /* Here we do not need to check the type */
+ varno = rightrti;
+ varattno = rightattno;
+ jexpr = (JoinExpr *) jexpr->rarg;
+ }
+ }
+
+ /*
+ * When we fall out of the loop, we've reached the base Var.
+ */
+ return (Node *) makeVar(varno,
+ varattno,
+ vartype,
+ vartypmod,
+ 0);
+}
+
+/*
+ * Given a join alias Var, construct Vars for the two input vars it directly
+ * depends on. Note that this should *only* be called for merger alias Vars.
+ * In practice it is only used for Vars that got past flatten_join_alias_vars.
+ */
+void
+build_join_alias_subvars(Query *root, Var *aliasvar,
+ Var **leftsubvar, Var **rightsubvar)
+{
+ Index varno = aliasvar->varno;
+ AttrNumber varattno = aliasvar->varattno;
+ RangeTblEntry *rte;
+ JoinExpr *jexpr;
+ Index leftrti,
+ rightrti;
+ AttrNumber leftattno,
+ rightattno;
+ RangeTblEntry *subrte;
+ Oid subtype;
+ int32 subtypmod;
+
+ Assert(aliasvar->varlevelsup == 0);
+ rte = rt_fetch(varno, root->rtable);
+ Assert(rte->rtekind == RTE_JOIN);
+
+ /*
+ * Find the RT indexes of the left and right children of the
+ * join node.
+ */
+ jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno);
+ if (jexpr == NULL ||
+ !IsA(jexpr, JoinExpr) ||
+ jexpr->rtindex != varno)
+ elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr");
+ if (IsA(jexpr->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+ else if (IsA(jexpr->larg, JoinExpr))
+ leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+ else
+ {
+ elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
+ }
+ if (IsA(jexpr->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+ else if (IsA(jexpr->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+ else
+ {
+ elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
+ }
+
+ Assert(varattno > 0);
+ leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+ rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+ if (!(leftattno && rightattno))
+ elog(ERROR, "build_join_alias_subvars: non-merger variable");
+
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ *leftsubvar = makeVar(leftrti,
+ leftattno,
+ subtype,
+ subtypmod,
+ 0);
+
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ *rightsubvar = makeVar(rightrti,
+ rightattno,
+ subtype,
+ subtypmod,
+ 0);
+}
+
+/*
+ * Find jointree item matching the specified RT index
+ */
+static Node *
+find_jointree_item(Node *jtnode, int rtindex)
+{
+ if (jtnode == NULL)
+ return NULL;
+ if (IsA(jtnode, RangeTblRef))
+ {
+ if (((RangeTblRef *) jtnode)->rtindex == rtindex)
+ return jtnode;
+ }
+ else if (IsA(jtnode, FromExpr))
+ {
+ FromExpr *f = (FromExpr *) jtnode;
+ List *l;
+
+ foreach(l, f->fromlist)
+ {
+ jtnode = find_jointree_item(lfirst(l), rtindex);
+ if (jtnode)
+ return jtnode;
+ }
+ }
+ else if (IsA(jtnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) jtnode;
+
+ if (j->rtindex == rtindex)
+ return jtnode;
+ jtnode = find_jointree_item(j->larg, rtindex);
+ if (jtnode)
+ return jtnode;
+ jtnode = find_jointree_item(j->rarg, rtindex);
+ if (jtnode)
+ return jtnode;
+ }
+ else
+ elog(ERROR, "find_jointree_item: unexpected node type %d",
+ nodeTag(jtnode));
+ return NULL;
+}