Update of /cvsroot/monetdb/pathfinder/compiler/algebra
In directory sc8-pr-cvs16:/tmp/cvs-serv26652/algebra

Modified Files:
        algebra.c algebra_cse.c algopt.c logical.c physical.c 
        planner.c 
Log Message:
Thetajoin operator:

    Introduced a thetajoin operator in the logical algebra (and the physical
    algebra and the MIL generation). This new thetajoin can handle a list of
    conjunctive predicates where each predicate represents an (in-)equality
    condition.

Thetajoin introduction:

    A thetajoin operator is introduced either by a small pattern
    (select-comparison-cross) in opt_general.brg or by a new introduction
    phase in intro_thetajoin.c. The latter introduction phase uses a
    selection as a basis to find the *correct* equi-join and transforms
    this equi-join into a thetajoin (for more details please look into
    the code).

Thetajoin optimization:

    Similar to the MVD optimization phase that pushes cross product
    operators up in the DAG structure this checkin provides a new
    optimization phase that pushes thetajoin operators up in the DAG
    (opt_thetajoin.c). These rewrites result in thetajoin operators
    that contain multiple predicates. (E.g. the transformed equi-joins
    eventually hit the selection that triggered the rewrite and thus form
    a new scope-dependent value-based join -- see XMark Q3 and Q4.)

Additional changes:

    opt_general.brg
     * Extended thetajoin pattern to cope with inequality comparisons
     * Rewrite the pattern: semijoin (distinct (Rel), Rel) into
       distinct (semijoin (Rel, Rel) as the semijoin hopefully reduces
       the cardinality more than the distinct operator. (This speeds
       up XMark Q12 by a factor of 2.5.)

    intro_proxy.c
     * Added a preceding phase that removes all semijoin operators.
       (Using only simply checks more proxies are now detected.)
     * Removed unnest optimization phase (thetajoin optimization
       does the same in a more general scenario).

    physical.c
     * Toyed around with the cost model
     * Fixed 'PFord_order_dir_at' bug

    planner.c
     * Generated more ordered alternatives (even if an ordering is
       not required) after an operator is mapped to physical algebra.
       In some situations this avoids in a later step planning the same
       sort operator multiple times. (We only introduce orderings for
       iter-like columns and sort at most two columns. Otherwise the
       plan space explodes for some sample queries.)
     * Extended list of possible orderings for the rownum operator



Index: algopt.c
===================================================================
RCS file: /cvsroot/monetdb/pathfinder/compiler/algebra/algopt.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- algopt.c    28 Mar 2007 15:06:49 -0000      1.15
+++ algopt.c    7 May 2007 10:17:05 -0000       1.16
@@ -34,6 +34,7 @@
 /* always include pathfinder.h first! */
 #include "pathfinder.h"
 #include <assert.h>
+#include <stdio.h>
 
 #include "algopt.h"
 #include "map_names.h"
@@ -41,6 +42,7 @@
 #include "timer.h"
 #include "algebra_cse.h"
 #include "la_proxy.h"
+#include "la_thetajoin.h"
 
 #define MAP_ORI_NAMES(phase)                                                \
         if (unq_names) {                                                    \
@@ -94,7 +96,14 @@
     bool proxies_involved = false;
     char *args = PFstate.opt_alg;
 
+#define DEBUG_OPT_STR 0
+#if DEBUG_OPT_STR
+    fprintf (stderr, "-o");
+#endif
     while (*args) {
+#if DEBUG_OPT_STR
+        fputc (*args, stderr);
+#endif
         switch (*args) {
             case 'A': /* disabled */
                 /*
@@ -242,6 +251,20 @@
                            PFtimer_str (tm));
                 break;
 
+            case 'T':
+                MAP_ORI_NAMES("thetajoin optimization")
+                    
+                tm = PFtimer_start ();
+                
+                root = PFintro_thetajoins (root);
+                root = PFalgopt_thetajoin (root);
+                
+                tm = PFtimer_stop (tm);
+                if (timing)
+                    PFlog ("   thetajoin optimization:\t    %s",
+                           PFtimer_str (tm));
+                break;
+
             case 'V':
                 MAP_ORI_NAMES("required value optimization")
                 REMOVE_PROXIES("required value optimization")
@@ -374,6 +397,9 @@
         args++;
         root = PFla_cse (root);
     }
+#if DEBUG_OPT_STR
+    fputc ('\n', stderr);
+#endif
 
     if (unq_names)
         PFinfo (OOPS_WARNING,

Index: algebra.c
===================================================================
RCS file: /cvsroot/monetdb/pathfinder/compiler/algebra/algebra.c,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -d -r1.51 -r1.52
--- algebra.c   11 Apr 2007 14:17:42 -0000      1.51
+++ algebra.c   7 May 2007 10:17:04 -0000       1.52
@@ -899,4 +899,13 @@
     return NULL;
 }
 
+/**
+ * Construct a predicate.
+ */
+PFalg_sel_t PFalg_sel (PFalg_comp_t comp,
+                       PFalg_att_t left,
+                       PFalg_att_t right) {
+    return (PFalg_sel_t) { .comp = comp, .left = left, .right = right };
+}
+
 /* vim:set shiftwidth=4 expandtab: */

Index: physical.c
===================================================================
RCS file: /cvsroot/monetdb/pathfinder/compiler/algebra/physical.c,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -d -r1.32 -r1.33
--- physical.c  15 Mar 2007 14:12:55 -0000      1.32
+++ physical.c  7 May 2007 10:17:06 -0000       1.33
@@ -57,7 +57,7 @@
 #define DEFAULT_COST INIT_COST
 #define AGGR_COST 50
 #define JOIN_COST 100
-#define SORT_COST 100
+#define SORT_COST 700
 
 /**
  * Create an algebra operator (leaf) node.
@@ -650,7 +650,7 @@
     ret->orderings = PFord_set ();
 
     /* ---- EqJoin: costs ---- */
-    ret->cost = 1.5 * JOIN_COST + n1->cost + n2->cost;
+    ret->cost = 2.5 * JOIN_COST + n1->cost + n2->cost;
 
     return ret;
 }
@@ -700,6 +700,160 @@
 }
 
 /**
+ * ThetaJoin: Theta-Join. Does not provide any ordering guarantees.
+ */
+PFpa_op_t *
+PFpa_thetajoin (const PFpa_op_t *n1, const PFpa_op_t *n2,
+                unsigned int count, PFalg_sel_t *pred)
+{
+    unsigned int i;
+    PFpa_op_t  *ret;
+
+    /* verify that the join attributes are all available */
+    for (i = 0; i < count; i++)
+        if (!contains_att (n1->schema, pred[i].left) ||
+            !contains_att (n2->schema, pred[i].right))
+            break;
+
+    /* did we find all attributes? */
+    if (i < count)
+        PFoops (OOPS_FATAL,
+                "attribute `%s' referenced in theta-join not found", 
+                contains_att (n2->schema, pred[i].right)
+                ? PFatt_str(pred[i].left) : PFatt_str(pred[i].right));
+    
+    ret = wire2 (pa_thetajoin, n1, n2);
+
+    ret->sem.thetajoin.count = count;
+    ret->sem.thetajoin.pred  = PFmalloc (count * sizeof (PFalg_sel_t));
+    for (i = 0; i < count; i++)
+        ret->sem.thetajoin.pred[i] = pred[i];
+
+    /* allocate memory for the result schema */
+    ret->schema.count = n1->schema.count + n2->schema.count;
+    ret->schema.items
+        = PFmalloc (ret->schema.count * sizeof (*(ret->schema.items)));
+
+    /* copy schema from n1 */
+    for (unsigned int i = 0; i < n1->schema.count; i++)
+        ret->schema.items[i] = n1->schema.items[i];
+
+    /* copy schema from n2 */
+    for (unsigned int i = 0; i < n2->schema.count; i++)
+        ret->schema.items[n1->schema.count + i] = n2->schema.items[i];
+
+    /* ---- ThetaJoin: orderings ---- */
+    ret->orderings = PFord_set ();
+
+    /* ---- ThetaJoin: costs ---- */
+
+    /* make it a little bit more expensive than the normal equi-join
+       and more expensive than the specialized variant that require
+       two sorted columns up front. */
+    ret->cost = 4 * JOIN_COST + 2 * SORT_COST + n1->cost + n2->cost;
+
+    return ret;
+}
+
+/**
+ * ThetaJoin: Theta-Join. Returns two columns (from n1 and n2)
+ *            with duplicates eliminated. Preserves the order 
+ *            of the left argument.
+ */
+PFpa_op_t *
+PFpa_unq2_thetajoin (PFalg_comp_t comp, PFalg_att_t left, PFalg_att_t right,
+                     PFalg_att_t ldist, PFalg_att_t rdist,
+                     const PFpa_op_t *n1, const PFpa_op_t *n2)
+{
+    PFpa_op_t  *ret = wire2 (pa_unq2_thetajoin, n1, n2);
+
+    assert (contains_att (n1->schema, left) &&
+            contains_att (n2->schema, right) &&
+            contains_att (n1->schema, ldist) &&
+            contains_att (n2->schema, rdist));
+    
+    ret->sem.unq_thetajoin.comp  = comp;
+    ret->sem.unq_thetajoin.left  = left;
+    ret->sem.unq_thetajoin.right = right;
+    ret->sem.unq_thetajoin.ldist = ldist;
+    ret->sem.unq_thetajoin.rdist = rdist;
+
+    /* allocate memory for the result schema */
+    ret->schema.count = 2;
+    ret->schema.items = PFmalloc (2 * sizeof (*(ret->schema.items)));
+    ret->schema.items[0].name = ldist;
+    ret->schema.items[0].type = aat_nat;
+    ret->schema.items[1].name = rdist;
+    ret->schema.items[1].type = aat_nat;
+
+    /* ---- ThetaJoin: orderings ---- */
+
+    /* The result is ordered by first the left distinct column
+       and then by the right distinct column. */
+
+    ret->orderings = PFord_set ();
+    PFord_set_add (ret->orderings,
+                   PFord_refine (PFordering (),
+                                 ldist,
+                                 DIR_ASC));
+    PFord_set_add (ret->orderings,
+                   PFord_refine (PFord_refine (PFordering (),
+                                               ldist,
+                                               DIR_ASC),
+                                 rdist,
+                                 DIR_ASC));
+
+    /* ---- ThetaJoin: costs ---- */
+    ret->cost = 3 * JOIN_COST + n1->cost + n2->cost;
+
+    return ret;
+}
+
+/**
+ * ThetaJoin: Theta-Join. Returns a single column with duplicates
+ *            eliminated. Preserves the order of the left argument.
+ */
+PFpa_op_t *
+PFpa_unq1_thetajoin (PFalg_comp_t comp, PFalg_att_t left, PFalg_att_t right,
+                     PFalg_att_t ldist, PFalg_att_t rdist,
+                     const PFpa_op_t *n1, const PFpa_op_t *n2)
+{
+    PFpa_op_t  *ret = wire2 (pa_unq1_thetajoin, n1, n2);
+
+    assert (contains_att (n1->schema, left) &&
+            contains_att (n2->schema, right) &&
+            contains_att (n1->schema, ldist) &&
+            contains_att (n2->schema, rdist));
+    
+    ret->sem.unq_thetajoin.comp  = comp;
+    ret->sem.unq_thetajoin.left  = left;
+    ret->sem.unq_thetajoin.right = right;
+    ret->sem.unq_thetajoin.ldist = ldist;
+    ret->sem.unq_thetajoin.rdist = rdist;
+
+    /* allocate memory for the result schema */
+    ret->schema.count = 1;
+    ret->schema.items = PFmalloc (sizeof (*(ret->schema.items)));
+    ret->schema.items[0].name = ldist;
+    ret->schema.items[0].type = aat_nat;
+
+    /* ---- ThetaJoin: orderings ---- */
+
+    /* The result is ordered by the left distinct column */
+
+    ret->orderings = PFord_set ();
+    PFord_set_add (ret->orderings,
+                   PFord_refine (PFordering (),
+                                 ldist,
+                                 DIR_ASC));
+
+    /* ---- ThetaJoin: costs ---- */
+    ret->cost = 3 * JOIN_COST + n1->cost + n2->cost;
+
+    return ret;
+}
+
+/**
  * Project: Column selection and renaming.
  *
  * Note that projection does @b not eliminate duplicates. If you
@@ -1224,7 +1378,12 @@
     PFord_set_add (ret->orderings, required);
 
     /* ---- StandardSort: costs ---- */
-    ret->cost = PFord_count (required) * SORT_COST + n->cost;
+    ret->cost = 3 * SORT_COST /* (1) */
+              + PFord_count (required) * SORT_COST
+              + n->cost;
+    /* (1): make sorting more expensive as all path steps.
+            This ensures that path steps sort internally
+            which is probably more efficient. */
 
     for (unsigned int i = 0; i < PFord_count (required); i++)
         if (PFord_order_dir_at (required, i) == DIR_DESC)
@@ -1261,9 +1420,13 @@
     PFord_set_add (ret->orderings, required);
 
     /* ---- StandardSort: costs ---- */
-    ret->cost = PFord_count (required) * SORT_COST 
+    ret->cost = 3 * SORT_COST /* (1) */
+              + PFord_count (required) * SORT_COST 
               - PFord_count (existing) * SORT_COST
               + n->cost;
+    /* (1): make sorting more expensive as all path steps.
+            This ensures that path steps sort internally
+            which is probably more efficient. */
 
     for (unsigned int i = 0; i < PFord_count (required); i++)
         if (PFord_order_dir_at (required, i) == DIR_DESC)
@@ -1785,11 +1948,19 @@
     /* ---- MergeRowNum: orderings ---- */
     for (unsigned int i = 0; i < PFord_set_count (n->orderings); i++) {
         PFord_set_add (ret->orderings, PFord_set_at (n->orderings, i));
+        
+        /* if we have already an ordering we can also add the new
+           generated column at the end. It won't break the ordering. */
+        PFord_set_add (ret->orderings, 
+                       PFord_refine (
+                           PFord_set_at (n->orderings, i),
+                           new_att, DIR_ASC));
+        
         /* get the direction of the partitioning column */
         if (part &&
             PFord_order_col_at (PFord_set_at (n->orderings, i), 0)
             == part) {
-            if (PFord_order_col_at (PFord_set_at (n->orderings, i), 0)
+            if (PFord_order_dir_at (PFord_set_at (n->orderings, i), 0)
                 == DIR_ASC)
                 dir_asc = true;
             else
@@ -2019,11 +2190,11 @@
 
         if (PFord_implies (n->sem.scjoin.out, iter_item)) {
             /* output has iter|item ordering */
-            n->cost = 3 * SORT_COST + ctx_cost;
+            n->cost = 3 * SORT_COST;
         }
         else {
             /* output has item|iter ordering */
-            n->cost = 2 * SORT_COST + ctx_cost;
+            n->cost = 2 * SORT_COST;
         }
 
     }
@@ -2032,13 +2203,13 @@
 
         if (PFord_implies (n->sem.scjoin.out, iter_item)) {
             /* output has iter|item ordering */
-            n->cost = 1 * SORT_COST + ctx_cost;
+            n->cost = 1 * SORT_COST;
         }
         else {
             /* output has item|iter ordering */
 
             /* should be cheapest */
-            n->cost = 0 * SORT_COST + ctx_cost;
+            n->cost = 0 * SORT_COST;
         }
     }
 

Index: planner.c
===================================================================
RCS file: /cvsroot/monetdb/pathfinder/compiler/algebra/planner.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- planner.c   15 Mar 2007 14:12:55 -0000      1.34
+++ planner.c   7 May 2007 10:17:07 -0000       1.35
@@ -173,7 +173,22 @@
     PFarray_concat (list, plans);
 }
 
+/**
+ * Helper function: Determine type of attribute @a att in schema
+ * @a schema. Used by plan_disjunion() and plan_thetajoin().
+ */
+static PFalg_simple_type_t
+type_of (PFalg_schema_t schema, PFalg_att_t att)
+{
+    for (unsigned int i = 0; i < schema.count; i++)
+        if (schema.items[i].name == att)
+            return schema.items[i].type;
 
+    PFoops (OOPS_FATAL, "unable to find attribute %s in schema",
+            PFatt_str (att));
+
+    return 0;  /* pacify compilers */
+}
 
 
 /**
@@ -379,8 +394,8 @@
      * LeftJoin may be more expensive, but gives us some ordering
      * guarantees.
      */
-    add_plan (ret, leftjoin (att1, att2, a, b));
     add_plan (ret, leftjoin (att2, att1, b, a));
+    add_plan (ret, leftjoin (att1, att2, a, b));
 
 #if 0
     PFalg_att_t    att_a = NULL;
@@ -449,9 +464,6 @@
             join_worker (ret, n->sem.eqjoin.att1, n->sem.eqjoin.att2,
                               *(plan_t **) PFarray_at (L(n)->plans, r),
                               *(plan_t **) PFarray_at (R(n)->plans, s));
-            join_worker (ret, n->sem.eqjoin.att2, n->sem.eqjoin.att1,
-                              *(plan_t **) PFarray_at (R(n)->plans, s),
-                              *(plan_t **) PFarray_at (L(n)->plans, r));
         }
 
     return ret;
@@ -463,7 +475,7 @@
 static PFplanlist_t *
 plan_semijoin (const PFla_op_t *n)
 {
-    PFplanlist_t  *ret       = new_planlist ();
+    PFplanlist_t  *ret = new_planlist ();
 
     assert (n); assert (n->kind == la_semijoin);
     assert (L(n)); assert (L(n)->plans);
@@ -483,6 +495,220 @@
 }
 
 /**
+ * Generate physical plans for theta-join.
+ */
+static PFplanlist_t *
+plan_thetajoin (const PFla_op_t *n)
+{
+    PFplanlist_t  *ret = new_planlist ();
+    
+    PFalg_simple_type_t cur_type;
+
+    assert (n); assert (n->kind == la_thetajoin);
+    assert (L(n)); assert (L(n)->plans);
+    assert (R(n)); assert (R(n)->plans);
+
+    /* combine each plan in R with each plan in S */
+    for (unsigned int r = 0; r < PFarray_last (L(n)->plans); r++)
+        for (unsigned int s = 0; s < PFarray_last (R(n)->plans); s++) {
+            add_plan (ret, 
+                      thetajoin (*(plan_t **) PFarray_at (L(n)->plans, r),
+                                 *(plan_t **) PFarray_at (R(n)->plans, s),
+                                 n->sem.thetajoin.count,
+                                 n->sem.thetajoin.pred));
+        }
+
+    cur_type = type_of (n->schema, n->sem.thetajoin.pred[0].left);
+    /* If we have only a single equi-join predicate we can also plan
+       an equi-join in addition. */
+    if (n->sem.thetajoin.count == 1 &&
+        n->sem.thetajoin.pred[0].comp == alg_comp_eq &&
+        monomorphic (cur_type) &&
+        cur_type != aat_anode &&
+        cur_type != aat_pnode)
+        /* combine each plan in R with each plan in S */
+        for (unsigned int r = 0; r < PFarray_last (L(n)->plans); r++)
+            for (unsigned int s = 0; s < PFarray_last (R(n)->plans); s++)
+                join_worker (ret, 
+                             n->sem.thetajoin.pred[0].left,
+                             n->sem.thetajoin.pred[0].right,
+                             *(plan_t **) PFarray_at (L(n)->plans, r),
+                             *(plan_t **) PFarray_at (R(n)->plans, s));
+
+    return ret;
+}
+
+/**
+ * Generate physical plans for dependent
+ * theta-join that additionally removes duplicates.
+ */
+static PFplanlist_t *
+plan_dep_unique_thetajoin (const PFla_op_t *n)
+{
+    PFplanlist_t  *ret     = new_planlist (),
+                  *lsorted = new_planlist (),
+                  *rsorted = new_planlist ();
+
+    PFalg_simple_type_t cur_type;
+
+    /* check all conditions again */
+    assert (n);     assert (n->kind     == la_distinct);
+    assert (L(n));  assert (L(n)->kind  == la_project);
+    assert (LL(n)); assert (LL(n)->kind == la_thetajoin);
+    assert (L(LL(n))); assert (L(LL(n))->plans);
+    assert (R(LL(n))); assert (R(LL(n))->plans);
+    assert (L(n)->schema.count == 1);
+    assert (LL(n)->sem.thetajoin.count == 2);
+    assert (LL(n)->sem.thetajoin.pred[0].comp == alg_comp_eq);
+    assert (L(n)->sem.proj.items[0].old == 
+            LL(n)->sem.thetajoin.pred[0].left ||
+            L(n)->sem.proj.items[0].old ==
+            LL(n)->sem.thetajoin.pred[0].right);
+
+    /* check for nat type in the first predicate */
+    if (type_of (LL(n)->schema,
+                 LL(n)->sem.thetajoin.pred[0].left) != aat_nat ||
+        type_of (LL(n)->schema,
+                 LL(n)->sem.thetajoin.pred[0].right) != aat_nat)
+        return ret;
+   
+    if ((cur_type = type_of (LL(n)->schema,
+                             LL(n)->sem.thetajoin.pred[1].left)) != 
+        type_of (LL(n)->schema,
+                 LL(n)->sem.thetajoin.pred[1].right) ||
+        !monomorphic (cur_type) ||
+        cur_type & aat_node)
+        return ret;
+            
+    /* make sure the left input is sorted by the left sort criterion */
+    for (unsigned int i = 0; i < PFarray_last (L(LL(n))->plans); i++)
+        add_plans (lsorted,
+                   ensure_ordering (
+                       *(plan_t **) PFarray_at (L(LL(n))->plans, i),
+                       sortby (LL(n)->sem.thetajoin.pred[0].left)));
+        
+    /* make sure the right input is sorted by the right sort criterion */
+    for (unsigned int i = 0; i < PFarray_last (R(LL(n))->plans); i++)
+        add_plans (rsorted,
+                   ensure_ordering (
+                       *(plan_t **) PFarray_at (R(LL(n))->plans, i),
+                       sortby (LL(n)->sem.thetajoin.pred[0].right)));
+    
+    /* combine each plan in R with each plan in S */
+    for (unsigned int l = 0; l < PFarray_last (lsorted); l++)
+        for (unsigned int r = 0; r < PFarray_last (rsorted); r++) {
+            add_plan (ret, 
+                      /* add the renaming projection afterwards */
+                      project (
+                          unq1_tjoin (
+                              /* from our compilation we know that
+                                 the first predicate is the dependent
+                                 one which is also used for removing
+                                 duplicate tuples. */
+                              LL(n)->sem.thetajoin.pred[1].comp,
+                              LL(n)->sem.thetajoin.pred[1].left,
+                              LL(n)->sem.thetajoin.pred[1].right,
+                              LL(n)->sem.thetajoin.pred[0].left,
+                              LL(n)->sem.thetajoin.pred[0].right,
+                              *(plan_t **) PFarray_at (lsorted, l),
+                              *(plan_t **) PFarray_at (rsorted, r)),
+                          1,
+                          L(n)->sem.proj.items));
+        }
+
+    return ret;
+}
+
+/**
+ * Generate physical plans for theta-join
+ * that additionally removes duplicates.
+ */
+static PFplanlist_t *
+plan_unique_thetajoin (const PFla_op_t *n)
+{
+    PFplanlist_t  *ret = new_planlist ();
+    PFalg_att_t    ldist, rdist;
+
+    PFalg_simple_type_t cur_type;
+
+    /* check all conditions again */
+    assert (n);     assert (n->kind     == la_distinct);
+    assert (L(n));  assert (L(n)->kind  == la_project);
+    assert (LL(n)); assert (LL(n)->kind == la_thetajoin);
+    assert (L(LL(n))); assert (L(LL(n))->plans);
+    assert (R(LL(n))); assert (R(LL(n))->plans);
+    assert (L(n)->schema.count == 2);
+    assert (LL(n)->sem.thetajoin.count == 1);
+    assert ((PFprop_ocol (L(LL(n)), L(n)->sem.proj.items[0].old) &&
+             PFprop_ocol (R(LL(n)), L(n)->sem.proj.items[1].old)) ^
+            (PFprop_ocol (L(LL(n)), L(n)->sem.proj.items[1].old) &&
+             PFprop_ocol (R(LL(n)), L(n)->sem.proj.items[0].old)));
+
+    if (PFprop_ocol (L(LL(n)), L(n)->sem.proj.items[0].old)) {
+        ldist = L(n)->sem.proj.items[0].old;
+        rdist = L(n)->sem.proj.items[1].old;
+    } else {
+        ldist = L(n)->sem.proj.items[1].old;
+        rdist = L(n)->sem.proj.items[0].old;
+    }
+    /* check for nat type in the distinct check */
+    if (type_of (LL(n)->schema, ldist) != aat_nat ||
+        type_of (LL(n)->schema, rdist) != aat_nat)
+        return ret;
+        
+    if ((cur_type = type_of (LL(n)->schema,
+                             LL(n)->sem.thetajoin.pred[0].left)) != 
+        type_of (LL(n)->schema,
+                 LL(n)->sem.thetajoin.pred[0].right) ||
+        !monomorphic (cur_type) ||
+        cur_type & aat_node)
+        return ret;
+            
+    for (unsigned int l = 0; l < PFarray_last (L(LL(n))->plans); l++)
+        for (unsigned int r = 0; r < PFarray_last (R(LL(n))->plans); r++) {
+            add_plan (ret, 
+                      /* add the renaming projection afterwards */
+                      project (
+                          unq2_tjoin (
+                              /* from our compilation we know that
+                                 the first predicate is the dependent
+                                 one which is also used for removing
+                                 duplicate tuples. */
+                              LL(n)->sem.thetajoin.pred[0].comp,
+                              LL(n)->sem.thetajoin.pred[0].left,
+                              LL(n)->sem.thetajoin.pred[0].right,
+                              ldist,
+                              rdist,
+                              *(plan_t **) PFarray_at (L(LL(n))->plans, l),
+                              *(plan_t **) PFarray_at (R(LL(n))->plans, r)),
+                          2,
+                          L(n)->sem.proj.items));
+            
+            /* for an equi-join we can also plan with switched sides */
+            if (LL(n)->sem.thetajoin.pred[0].comp == alg_comp_eq)
+                add_plan (ret, 
+                          /* add the renaming projection afterwards */
+                          project (
+                              unq2_tjoin (
+                                  /* from our compilation we know that
+                                     the first predicate is the dependent
+                                     one which is also used for removing
+                                     duplicate tuples. */
+                                  alg_comp_eq,
+                                  LL(n)->sem.thetajoin.pred[0].right,
+                                  LL(n)->sem.thetajoin.pred[0].left,
+                                  rdist,
+                                  ldist,
+                                  *(plan_t **) PFarray_at (R(LL(n))->plans, r),
+                                  *(plan_t **) PFarray_at (L(LL(n))->plans, 
l)),
+                              2,
+                              L(n)->sem.proj.items));
+        }
+
+    return ret;
+}
+
+/**
  * Generate physical plan for the logical `project' operator.
  *
  * There's just one physical operator for `project' that we
@@ -532,23 +758,6 @@
 }
 
 /**
- * Helper function: Determine type of attribute @a att in schema
- * @a schema. Used by plan_disjunion().
- */
-static PFalg_simple_type_t
-type_of (PFalg_schema_t schema, PFalg_att_t att)
-{
-    for (unsigned int i = 0; i < schema.count; i++)
-        if (schema.items[i].name == att)
-            return schema.items[i].type;
-
-    PFoops (OOPS_FATAL, "unable to find attribute %s in schema",
-            PFatt_str (att));
-
-    return 0;  /* pacify compilers */
-}
-
-/**
  * Generate physical plans for disjoint union.
  *
  * Available implementations are MergeUnion and AppendUnion.
@@ -954,7 +1163,7 @@
 {
     PFplanlist_t *ret    = new_planlist ();
     PFplanlist_t *sorted = new_planlist ();
-    PFord_ordering_t ord_asc, ord_desc;
+    PFord_ordering_t ord_asc, ord_desc, ord_wo_part;
 
     assert (n); assert (n->kind == la_rownum);
     assert (L(n)); assert (L(n)->plans);
@@ -962,8 +1171,9 @@
     /*
      * Build up the ordering that we require for MergeRowNumber
      */
-    ord_asc  = PFordering ();
-    ord_desc = PFordering ();
+    ord_asc     = PFordering ();
+    ord_desc    = PFordering ();
+    ord_wo_part = PFordering ();
 
     /* the partitioning attribute must be the primary ordering */
     if (n->sem.rownum.part) {
@@ -975,14 +1185,18 @@
     for (unsigned int i = 0;
          i < PFord_count (n->sem.rownum.sortby);
          i++) {
-        ord_asc = PFord_refine (
-                      ord_asc,
-                      PFord_order_col_at (n->sem.rownum.sortby, i),
-                      PFord_order_dir_at (n->sem.rownum.sortby, i));
-        ord_desc = PFord_refine (
-                       ord_desc,
-                       PFord_order_col_at (n->sem.rownum.sortby, i),
-                       PFord_order_dir_at (n->sem.rownum.sortby, i));
+        ord_asc     = PFord_refine (
+                          ord_asc,
+                          PFord_order_col_at (n->sem.rownum.sortby, i),
+                          PFord_order_dir_at (n->sem.rownum.sortby, i));
+        ord_desc    = PFord_refine (
+                          ord_desc,
+                          PFord_order_col_at (n->sem.rownum.sortby, i),
+                          PFord_order_dir_at (n->sem.rownum.sortby, i));
+        ord_wo_part = PFord_refine (
+                          ord_wo_part,
+                          PFord_order_col_at (n->sem.rownum.sortby, i),
+                          PFord_order_dir_at (n->sem.rownum.sortby, i));
     }
 
     /* ensure correct input ordering for MergeRowNumber */
@@ -993,6 +1207,9 @@
         add_plans (sorted,
                    ensure_ordering (
                        *(plan_t **) PFarray_at (L(n)->plans, i), ord_desc));
+        add_plans (sorted,
+                   ensure_ordering (
+                       *(plan_t **) PFarray_at (L(n)->plans, i), ord_wo_part));
     }
 
     /* throw out those plans that are too expensive */
@@ -1000,10 +1217,10 @@
 
     /* for each remaining plan, generate a MergeRowNumber operator */
     for (unsigned int i = 0; i < PFarray_last (sorted); i++)
-        for (unsigned int j = 0; j < PFarray_last (L(n)->plans); j++)
-            add_plan (ret,
-                      number (*(plan_t **) PFarray_at (sorted, i),
-                              n->sem.rownum.attname, n->sem.rownum.part));
+        add_plan (ret,
+                  number (*(plan_t **) PFarray_at (sorted, i),
+                          n->sem.rownum.attname,
+                          n->sem.rownum.part));
 
     return ret;
 }
@@ -2183,12 +2400,74 @@
         case la_cross:          plans = plan_cross (n);        break;
         case la_eqjoin:         plans = plan_eqjoin (n);       break;
         case la_semijoin:       plans = plan_semijoin (n);     break;
-        case la_project:        plans = plan_project (n);      break;
+        case la_thetajoin:      plans = plan_thetajoin (n);    break;
+        case la_project:
+            plans = plan_project (n);
+                                
+            /* in addition try to implement the combination 
+               of thetajoin and distinct more efficiently */
+            if (L(n)->kind == la_thetajoin &&
+                PFprop_set (n->prop)) {
+                /* check for dependent thetajoins where the
+                   distinct column is also a join column */
+                if (n->schema.count == 1 &&
+                    L(n)->sem.thetajoin.count == 2 &&
+                    L(n)->sem.thetajoin.pred[0].comp == alg_comp_eq &&
+                    (n->sem.proj.items[0].old == 
+                     L(n)->sem.thetajoin.pred[0].left ||
+                     n->sem.proj.items[0].old ==
+                     L(n)->sem.thetajoin.pred[0].right)) {
+                    add_plans (plans, plan_dep_unique_thetajoin (
+                                          PFla_distinct (n)));
+                /* check for independent thetajoins where the
+                   two distinct column come from both the left
+                   and the right children of the thetajoin */
+                } else if (n->schema.count == 2 &&
+                    L(n)->sem.thetajoin.count == 1 &&
+                    (PFprop_ocol (L(L(n)), n->sem.proj.items[0].old) &&
+                     PFprop_ocol (R(L(n)), n->sem.proj.items[1].old)) ^
+                    (PFprop_ocol (L(L(n)), n->sem.proj.items[1].old) &&
+                     PFprop_ocol (R(L(n)), n->sem.proj.items[0].old))) {
+                    add_plans (plans, plan_unique_thetajoin (
+                                          PFla_distinct (n)));
+                }
+            }
+            break;
+
         case la_select:         plans = plan_select (n);       break;
         case la_disjunion:      plans = plan_disjunion (n);    break;
         case la_intersect:      plans = plan_intersect (n);    break;
         case la_difference:     plans = plan_difference (n);   break;
-        case la_distinct:       plans = plan_distinct (n);     break;
+        case la_distinct:
+            plans = plan_distinct (n);
+                                
+            /* in addition try to implement the combination 
+               of thetajoin and distinct more efficiently */
+            if (L(n)->kind == la_project &&
+                LL(n)->kind == la_thetajoin) {
+                /* check for dependent thetajoins where the
+                   distinct column is also a join column */
+                if (n->schema.count == 1 &&
+                    LL(n)->sem.thetajoin.count == 2 &&
+                    LL(n)->sem.thetajoin.pred[0].comp == alg_comp_eq &&
+                    (L(n)->sem.proj.items[0].old == 
+                     LL(n)->sem.thetajoin.pred[0].left ||
+                     L(n)->sem.proj.items[0].old ==
+                     LL(n)->sem.thetajoin.pred[0].right)) {
+                    add_plans (plans, plan_dep_unique_thetajoin (n));
+                /* check for independent thetajoins where the
+                   two distinct column come from both the left
+                   and the right children of the thetajoin */
+                } else if (n->schema.count == 2 &&
+                    LL(n)->sem.thetajoin.count == 1 &&
+                    (PFprop_ocol (L(LL(n)), L(n)->sem.proj.items[0].old) &&
+                     PFprop_ocol (R(LL(n)), L(n)->sem.proj.items[1].old)) ^
+                    (PFprop_ocol (L(LL(n)), L(n)->sem.proj.items[1].old) &&
+                     PFprop_ocol (R(LL(n)), L(n)->sem.proj.items[0].old))) {
+                    add_plans (plans, plan_unique_thetajoin (n));
+                }
+            }
+            break;
 
         case la_fun_1to1:       plans = plan_fun_1to1 (n);     break;
         case la_num_eq:
@@ -2349,6 +2628,68 @@
     assert (plans);
     assert (PFarray_last (plans) > 0);
 
+    /* Introduce some more orders (on iter columns of length 1 or 2)
+       in the hope to share more sort operations. */ 
+    switch (n->kind) {
+        case la_element:
+        case la_attribute:
+        case la_textnode:
+        case la_docnode:
+        case la_comment:
+        case la_processi:
+        case la_merge_adjacent:
+        case la_fragment:
+        case la_frag_union:
+        case la_empty_frag:
+        case la_nil:
+        case la_trace_msg:
+        case la_trace_map:
+            break;
+            
+        default:
+        {
+            PFord_ordering_t ord = PFordering ();
+            PFord_set_t orderings = PFord_set ();
+            plan_t *p = (*(plan_t **) PFarray_at (plans, 0));
+            unsigned int plan_count = PFarray_last (plans);
+
+            for (unsigned int i = 0; i < p->schema.count; i++)
+                /* check for an iter-like column (to avoid generating
+                   a large amount of plans) */
+                if (PFalg_unq_name (p->schema.items[i].name, 0) ==
+                    PFalg_unq_name (att_iter, 0))
+                    ord = PFord_refine (ord, p->schema.items[i].name, DIR_ASC);
+
+            /* collect all possible orderings of length 1 and 2 */       
+            for (unsigned int i = 0; i < PFord_count (ord); i++) {
+                PFord_ordering_t  ordering = PFordering ();
+
+                ordering = PFord_refine (ordering,
+                                         PFord_order_col_at (ord, i),
+                                         DIR_ASC);
+                PFord_set_add (orderings, ordering);
+                
+                for (unsigned int j = 0; j < PFord_count (ord); j++)
+                    if (j != i)
+                        PFord_set_add (orderings,
+                                       PFord_refine (
+                                           ordering,
+                                           PFord_order_col_at (ord, j),
+                                           DIR_ASC));
+            }
+
+            /* add all plans satisfying the generated ordering
+               to the list of plans */
+            for (unsigned int plan = 0; plan < plan_count; plan++) {
+                p = (*(plan_t **) PFarray_at (plans, plan));
+                for (unsigned int i = 0; i < PFord_set_count (orderings); i++) 
{
+                    ord = PFord_set_at (orderings, i);
+                    add_plans (plans, ensure_ordering (p, ord));
+                }
+            }
+        }
+    }
+        
     /* Prune un-interesting plans. */
     plans = prune_plans (plans);
 

Index: algebra_cse.c
===================================================================
RCS file: /cvsroot/monetdb/pathfinder/compiler/algebra/algebra_cse.c,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -d -r1.49 -r1.50
--- algebra_cse.c       15 Mar 2007 14:12:55 -0000      1.49
+++ algebra_cse.c       7 May 2007 10:17:05 -0000       1.50
@@ -192,6 +192,22 @@
                     && a->sem.eqjoin.att2 == b->sem.eqjoin.att2);
             break;
 
+        case la_thetajoin:
+            if (a->sem.thetajoin.count != b->sem.thetajoin.count)
+                return false;
+            
+            for (unsigned int i = 0; i < a->sem.thetajoin.count; i++)
+                if ((a->sem.thetajoin.pred[i].comp != 
+                     b->sem.thetajoin.pred[i].comp) ||
+                    (a->sem.thetajoin.pred[i].left != 
+                     b->sem.thetajoin.pred[i].left) ||
+                    (a->sem.thetajoin.pred[i].right != 
+                     b->sem.thetajoin.pred[i].right))
+                    return false;
+                    
+            return true;
+            break;
+
         case la_eqjoin_unq:
             return (a->sem.eqjoin_unq.att1 == b->sem.eqjoin_unq.att1
                     && a->sem.eqjoin_unq.att2 == b->sem.eqjoin_unq.att2

Index: logical.c
===================================================================
RCS file: /cvsroot/monetdb/pathfinder/compiler/algebra/logical.c,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -d -r1.45 -r1.46
--- logical.c   16 Apr 2007 12:06:31 -0000      1.45
+++ logical.c   7 May 2007 10:17:06 -0000       1.46
@@ -712,6 +712,98 @@
 }
 
 /**
+ * Theta-join between two operator nodes.
+ *
+ * Assert that all columns listed in the predicate list are
+ * available.  @a n1 and @a n2 must not have duplicate attribute names.
+ * The schema of the result is (schema(@a n1) + schema(@a n2)).
+ */
+PFla_op_t *
+PFla_thetajoin (const PFla_op_t *n1, const PFla_op_t *n2,
+                unsigned int count, PFalg_sel_t *pred)
+{
+    PFla_op_t     *ret;
+    unsigned int   i, j;
+
+    assert (n1); assert (n2);
+
+    /* verify that the join attributes are all available */
+    for (i = 0; i < count; i++)
+        if (!PFprop_ocol (n1, pred[i].left) ||
+            !PFprop_ocol (n2, pred[i].right))
+            break;
+
+    /* did we find all attributes? */
+    if (i < count)
+        PFoops (OOPS_FATAL,
+                "attribute `%s' referenced in theta-join not found", 
+                PFprop_ocol (n2, pred[i].right)
+                ? PFatt_str(pred[i].left) : PFatt_str(pred[i].right));
+
+    /* build new theta-join node */
+    ret = la_op_wire2 (la_thetajoin, n1, n2);
+
+    /* insert semantic value (predicates) into the result */
+    ret->sem.thetajoin.count = count;
+    ret->sem.thetajoin.pred  = PFmalloc (count * sizeof (PFalg_sel_t));
+    for (i = 0; i < count; i++)
+        ret->sem.thetajoin.pred[i] = pred[i];
+
+    /* allocate memory for the result schema (schema(n1) + schema(n2)) */
+    ret->schema.count = n1->schema.count + n2->schema.count;
+    ret->schema.items
+        = PFmalloc (ret->schema.count * sizeof (*(ret->schema.items)));
+
+    /* copy schema from argument 'n1' */
+    for (i = 0; i < n1->schema.count; i++)
+        ret->schema.items[i] = n1->schema.items[i];
+
+    /* copy schema from argument 'n2', check for duplicate attribute names */
+    for (j = 0; j < n2->schema.count; j++) {
+
+        ret->schema.items[n1->schema.count + j] = n2->schema.items[j];
+
+#ifndef NDEBUG
+        for (i = 0; i < n1->schema.count; i++)
+            if (n1->schema.items[i].name == n2->schema.items[j].name)
+                PFoops (OOPS_FATAL,
+                        "duplicate attribute `%s' in theta-join",
+                        PFatt_str (n2->schema.items[j].name));
+#endif
+    }
+    
+    return ret;
+}
+
+/**
+ * Theta-join between two operator nodes.
+ * Special internal variant used during thetajoin optimization.
+ * 
+ * The optimizer will fill check for consistency and
+ * fill in the correct schema (which requires internal knowledge
+ * of @a data).
+ */
+PFla_op_t *
+PFla_thetajoin_opt_internal (const PFla_op_t *n1,
+                             const PFla_op_t *n2,
+                             PFarray_t *data)
+{
+    PFla_op_t     *ret;
+
+    assert (n1); assert (n2);
+
+    /* build new theta-join node */
+    ret = la_op_wire2 (la_thetajoin, n1, n2);
+
+    /* insert semantic value (predicates) into the result */
+    ret->sem.thetajoin_opt.pred = PFarray_copy (data);
+
+    /* allocate no schema -- this will be done by the optimization */
+    
+    return ret;
+}
+
+/**
  * Logical algebra projection/renaming.
  *
  * @param n     Argument for the projection operator.


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Monetdb-pf-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/monetdb-pf-checkins

Reply via email to