Changeset: c2cf29ceb889 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/c2cf29ceb889
Modified Files:
        sql/server/rel_optimize_sel.c
        sql/server/rel_unnest.c
        sql/test/BugTracker-2015/Tests/crash.Bug-3736.test
Branch: unnest2
Log Message:

add some extra optimization for new unnesting


diffs (truncated from 389 to 300 lines):

diff --git a/sql/server/rel_optimize_sel.c b/sql/server/rel_optimize_sel.c
--- a/sql/server/rel_optimize_sel.c
+++ b/sql/server/rel_optimize_sel.c
@@ -120,10 +120,53 @@ bind_split_select(visitor *v, global_pro
  * join (L, Distinct Project(join(L,P) [ p.key == l.lkey]) [p.key]) [ p.key == 
l.lkey]
  * =>
  * join(L, P) [p.key==l.lkey]
+ *
+ * join(project(Ref 1 distinc) [ x as xx ], groupby( join( project(Ref 1 
distinct) [ x as yy ], ZZ ) ) [ yy ] [ yy , ...]) [ xx == yy ]
+ * =>
+ * project(groupby( join( project(Ref 1 distinct) [ x as yy ], ZZ ) ) [ yy ] [ 
yy, .. ] ) [ yy as xx, .. ]
  */
 static sql_rel *
 rel_remove_redundant_join_(visitor *v, sql_rel *rel)
 {
+       if (rel->op == op_join && !list_empty(rel->exps)) {
+               sql_rel *l = rel->l, *gb = rel->r;
+               if (l->l && is_project(l->op) && is_groupby(gb->op)) {
+                       sql_rel *inner = l->l;
+                       sql_rel *gbj = gb->l;
+                       if (rel_is_ref(inner) && is_project(inner->op) && 
(need_distinct(inner) || is_groupby(inner->op)) &&
+                           gbj->op == op_join && list_length(inner->r) == 
list_length(rel->exps)) {
+                               sql_rel *gbjl = gbj->l, *gbjr = gbj->r;
+                               if ((gbjl->l && is_project(gbjl->op) && inner 
== gbjl->l) ||
+                                       (gbjr->l && is_project(gbjr->op) && 
inner == gbjr->l)) {
+                                       sql_rel *nrel = rel_project(v->sql->sa, 
rel_dup(gb), rel_projections(v->sql, gb, NULL, 0, 1));
+                                       /* sofar expect aligned gbe and je */
+                                       bool found = true;
+                                       for (node *n = nrel->exps->h, *m = 
rel->exps->h; n && m && found; n = n->next, m = m->next) {
+                                               sql_exp *gbe = n->data;
+                                               sql_exp *je = m->data;
+                                               if (!is_compare(je->type) || 
!is_semantics(je) || je->flag != cmp_equal) {
+                                                       found = false;
+                                                       break;
+                                               }
+                                               sql_exp *lje = je->l, *rje = 
je->r;
+                                               if (lje->nid == 
gbe->alias.label) {
+                                                       gbe->alias = rje->alias;
+                                               } else if (rje->nid == 
gbe->alias.label) {
+                                                       gbe->alias = lje->alias;
+                                               } else {
+                                                       found = false;
+                                               }
+                                       }
+                                       if (found) {
+                                               rel_destroy(v->sql, rel);
+                                               return nrel;
+                                       } else {
+                                               rel_destroy(v->sql, nrel);
+                                       }
+                               }
+                       }
+               }
+       }
        if ((is_join(rel->op) || is_semi(rel->op)) && !list_empty(rel->exps)) {
                sql_rel *l = rel->l, *r = rel->r, *b, *p = NULL, *j;
 
@@ -176,7 +219,7 @@ run_optimizer
 bind_remove_redundant_join(visitor *v, global_props *gp)
 {
        int flag = v->sql->sql_optimizer;
-       return gp->opt_cycle == 0 && gp->opt_level == 1 && (gp->cnt[op_left] || 
gp->cnt[op_right]
+       return gp->opt_cycle <= 1 && gp->opt_level == 1 && (gp->cnt[op_left] || 
gp->cnt[op_right]
                   || gp->cnt[op_full] || gp->cnt[op_join] || gp->cnt[op_semi] 
|| gp->cnt[op_anti]) &&
                   (flag & remove_redundant_join) ? rel_remove_redundant_join : 
NULL;
 }
@@ -1327,8 +1370,174 @@ out2inner(visitor *v, sql_rel* sel, sql_
        return sel;
 }
 
-static inline sql_rel *
-rel_out2inner(visitor *v, sql_rel *rel) {
+static bool
+exps_uses_any(list *exps, list *l)
+{
+       bool uses_any = false;
+
+       if (list_empty(exps) || list_empty(l))
+               return false;
+       for (node *n = l->h; n && !uses_any; n = n->next) {
+               sql_exp *e = n->data;
+               uses_any |= list_exps_uses_exp(exps, e) != NULL;
+       }
+       return uses_any;
+}
+
+static bool has_semantics(sql_exp *e);
+
+static bool
+exps_semantics(list *l)
+{
+       if (list_empty(l))
+               return false;
+       for(node *n = l->h; n; n = n->next) {
+               sql_exp *e = n->data;
+               if (has_semantics(e))
+                       return true;
+       }
+       return false;
+}
+
+static bool
+all_semantics(list *l) /* or const */
+{
+       if (list_empty(l))
+               return false;
+       for(node *n = l->h; n; n = n->next) {
+               sql_exp *e = n->data;
+               if (!exp_is_atom(e) && !has_semantics(e))
+                       return false;
+       }
+       return true;
+}
+
+static bool
+any_semantics(list *l) /* or const */
+{
+       if (list_empty(l))
+               return false;
+       for(node *n = l->h; n; n = n->next) {
+               sql_exp *e = n->data;
+               if (exp_is_atom(e) || has_semantics(e))
+                       return true;
+       }
+       return false;
+}
+
+static bool
+has_semantics(sql_exp *e)
+{
+       if (is_semantics(e))
+               return true;
+       switch(e->type) {
+       case e_cmp:
+               if (e->flag == cmp_con) {
+                       return all_semantics(e->l);
+               } else if (e->flag == cmp_dis) {
+                       return any_semantics(e->l);
+               } else if (e->flag == cmp_in || e->flag == cmp_notin) {
+                       return has_semantics(e->l) || any_semantics(e->r);
+               } else if (e->flag == cmp_filter) {
+                       return any_semantics(e->l) || any_semantics(e->r);
+               } else {
+                       bool semantics = has_semantics(e->l) || 
has_semantics(e->r);
+                       if (e->f)
+                               return true;
+                               //semantics |= has_semantics(e->f);
+                       return semantics;
+               }
+       case e_atom:
+               return false;
+       case e_column:
+               return false;
+       case e_aggr:
+       case e_func:
+               if (e->type == e_func) {
+                       sql_subfunc *f = e->f;
+                       if (f->func->semantics)
+                               return true;
+                       if (!f->func->s && (
+                                               strcmp(f->func->base.name, 
"nullif") == 0 ||
+                                               strcmp(f->func->base.name, 
"coalesce") == 0 ||
+                                               strcmp(f->func->base.name, 
"casewhen") == 0 ||
+                                               strcmp(f->func->base.name, 
"case") == 0 ||
+                                               strcmp(f->func->base.name, 
"ifthenelse") == 0))
+                                       return true;
+                       return exps_semantics(e->l);
+               }
+               return e->semantics;
+       case e_convert:
+               return has_semantics(e->l);
+       case e_psm:
+               return false;
+       }
+       return false;
+}
+
+static sql_rel *
+rel_outer2inner(visitor *v, list *exps, sql_rel *rel)
+{
+       if (!rel || rel_is_ref(rel))
+               return rel;
+       if (rel->op == op_join /*is_join(rel->op) later nested outers as well 
*/ || is_select(rel->op)) {
+               rel->l = rel_outer2inner(v, exps, rel->l);
+               if (is_join(rel->op))
+                       rel->r = rel_outer2inner(v, exps, rel->r);
+       } else if (is_simple_project(rel->op)) { /* handle projection 
expressions */
+               /* merge/collect all exps which don't have 'is' semantics */
+               list *nexps = sa_list(v->sql->sa);
+               for(node *n = rel->exps->h; n; n = n->next) {
+                       sql_exp *e = n->data;
+                       if (!has_semantics(e) && list_exps_uses_exp(exps, e))
+                               append(nexps, e);
+               }
+               rel->l = rel_outer2inner(v, nexps, rel->l);
+       } else if (is_left(rel->op)) { /* first just left outers */
+               list *rexps = rel_projections(v->sql, rel->r, NULL, 0, 1);
+               /* exps from right hand */
+               if (exps_uses_any(exps, rexps)) {
+                       rel->op = op_join;
+                       v->changes++;
+               }
+       }
+       return rel;
+}
+
+static bool
+rel_has_leftouter(sql_rel *rel)
+{
+       if (!rel || rel_is_ref(rel))
+               return false;
+       if (rel->op == op_join)
+               return rel_has_leftouter(rel->l) || rel_has_leftouter(rel->r);
+       if (is_simple_project(rel->op) || is_select(rel->op))
+               return rel_has_leftouter(rel->l);
+       if (is_left(rel->op))
+               return true;
+       return false;
+}
+
+/*
+ * Rewrite outer joins into inner joins, when results of the outer join are 
later used in
+ * predicates filtering the none matching rows of the outer join.
+ *
+ * select/join(.. project(..  outer_join () )[ use exps of none-matching-side] 
) [ exp using (possibly indirect)
+ * attribute of none-matching sided
+ */
+static sql_rel *
+rel_out2inner(visitor *v, sql_rel *rel)
+{
+       if (rel && (is_select(rel->op) || rel->op == op_join) && 
!list_empty(rel->exps) && rel_has_leftouter(rel)) {
+               /* collect all exps which don't have 'is' semantics */
+               list *exps = sa_list(v->sql->sa);
+               for (node *n = rel->exps->h; n; n = n->next) {
+                       sql_exp *e = n->data;
+                       if (!has_semantics(e))
+                               append(exps, e);
+               }
+               rel = rel_outer2inner(v, exps, rel);
+       }
 
        if (!is_non_trivial_select_applied_to_outer_join(rel)) {
                // Nothing to do here.
@@ -1370,20 +1579,6 @@ rel_out2inner(visitor *v, sql_rel *rel) 
        }
 }
 
-static bool
-exps_uses_any(list *exps, list *l)
-{
-       bool uses_any = false;
-
-       if (list_empty(exps) || list_empty(l))
-               return false;
-       for (node *n = l->h; n && !uses_any; n = n->next) {
-               sql_exp *e = n->data;
-               uses_any |= list_exps_uses_exp(exps, e) != NULL;
-       }
-       return uses_any;
-}
-
 /* TODO At the moment I have to disable the new join2semi because the join 
order optimizer doesn't take semi-joins into account,
 so plans get deteriorated if more joins are optimized into semi-joins. Later I 
will review the join order with semi-joins and hopefully,
 I will be able to re-enable the new join2semi. */
diff --git a/sql/server/rel_unnest.c b/sql/server/rel_unnest.c
--- a/sql/server/rel_unnest.c
+++ b/sql/server/rel_unnest.c
@@ -3977,6 +3977,29 @@ accessing_merge(list *acc, list *nacc)
        return acc;
 }
 
+static sql_rel *
+rel_smallest(sql_rel *r, list *outer_refs)
+{
+       if (r->op != op_join || !list_empty(r->exps))
+               return r;
+       sql_rel *rl = r->l;
+       sql_rel *rr = r->r;
+       int llen = list_length(outer_refs), rlen = llen;
+       for(node *n = outer_refs->h; n; n = n->next) {
+               sql_exp *e = n->data;
+               if (rel_find_exp(rl, e)) {
+                       llen--;
+               } else {
+                       rlen--;
+               }
+       }
+       if (llen && rlen)
+               return r;
+       if (llen)
+               return rel_smallest(rr, outer_refs);
+       return rel_smallest(rl, outer_refs);
+}
+
 static bool
 rel_djoin_elim(visitor *v, sql_rel *prel, sql_rel *rel, struct unnesting 
*parent, list *parent_accessing, list *refs)
 {
@@ -4007,7 +4030,8 @@ rel_djoin_elim(visitor *v, sql_rel *prel
        assert(p);
        struct access_info *ai = p?p->value.pval:NULL;
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to