Changeset: a42f1d0afe55 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=a42f1d0afe55
Modified Files:
        sql/include/sql_relation.h
        sql/server/rel_exp.c
        sql/server/rel_select.c
        sql/server/rel_unnest.c
Branch: window-tunning
Log Message:

Handle changes in window functions in the SQL layer by adding/updating 
partition expression while unnesting under subqueries. This also unnests window 
functions with more than 1 argument correctly.


diffs (199 lines):

diff --git a/sql/include/sql_relation.h b/sql/include/sql_relation.h
--- a/sql/include/sql_relation.h
+++ b/sql/include/sql_relation.h
@@ -52,6 +52,7 @@ typedef struct expression {
         nulls_last:1,  /* return null after all other rows */
         zero_if_empty:1,       /* in case of partial aggregator computation, 
some aggregators need to return 0 instead of NULL */
         distinct:1,
+        frames:1, /* needed for window functions with frame support, ie 
aggregates and last_value, first_value and nth_value functions */
 
         semantics:1,   /* is vs = semantics (nil = nil vs unknown != unknown), 
ranges with or without nil, aggregation with or without nil */
         need_no_nil:1,
@@ -227,6 +228,10 @@ typedef enum operator_type {
 #define set_nulls_first(e)     ((e)->nulls_last=0)
 #define set_direction(e, dir)  ((e)->ascending = (dir&1), (e)->nulls_last = 
(dir&2)?1:0)
 
+#define supports_frames(e)              ((e)->frames)
+#define set_frame_support(e)    ((e)->frames = 1)
+#define reset_supports_frames(e) ((e)->frames = 0)
+
 #define is_anti(e)             ((e)->anti)
 #define set_anti(e)            (e)->anti = 1
 #define is_semantics(e)        ((e)->semantics)
diff --git a/sql/server/rel_exp.c b/sql/server/rel_exp.c
--- a/sql/server/rel_exp.c
+++ b/sql/server/rel_exp.c
@@ -663,6 +663,8 @@ exp_propagate(sql_allocator *sa, sql_exp
                set_semantics(ne);
        if (is_ascending(oe))
                set_ascending(ne);
+       if (supports_frames(oe))
+               set_frame_support(ne);
        if (nulls_last(oe))
                set_nulls_last(ne);
        if (need_distinct(oe))
diff --git a/sql/server/rel_select.c b/sql/server/rel_select.c
--- a/sql/server/rel_select.c
+++ b/sql/server/rel_select.c
@@ -4912,6 +4912,8 @@ rel_rankop(sql_query *query, sql_rel **r
                        list_append(args, start && eend ? exp_copy(sql, pe) : 
pe);
        }
        call = exp_rank_op(sql->sa, list_empty(args) ? NULL : args, gbe, obe, 
wf);
+       if (supports_frames)
+               set_frame_support(call);
        *rel = p;
        return call;
 }
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
@@ -605,47 +605,79 @@ exp_rewrite(mvc *sql, sql_rel *rel, sql_
        e->l = exps_rewrite(sql, rel, e->l, ad);
        sf = e->f;
        /* window functions need to be run per freevars */
-       if (sf->func->type == F_ANALYTIC && list_length(sf->func->ops) > 2) {
+       if (sf->func->type == F_ANALYTIC && strcmp(sf->func->base.name, 
"window_bound") != 0 && strcmp(sf->func->base.name, "diff") != 0 && ad) {
                sql_subtype *bt = sql_bind_localtype("bit");
-               node *d;
-               list *rankopargs = e->l;
-               /* window_bound has partition/orderby as first argument (before 
normal expressions), others as second (and have a boolean placeholder) */
-               int is_wb = (strcmp(sf->func->base.name, "window_bound") == 0);
-               int is_ntile = (strcmp(sf->func->base.name, "ntile") == 0);
-               node *n = 
(is_wb)?rankopargs->h:(is_ntile)?rankopargs->h->next->next:rankopargs->h->next;
-               sql_exp *pe = n->data;
-
-               /* if pe is window_bound function skip */
-               if (pe->type == e_func) {
-                       sf = pe->f;
-                       if (strcmp(sf->func->base.name, "window_bound") == 0)
-                               return e;
-               }
-               /* find partition expression in rankfunc */
-               /* diff function */
-               if (exp_is_atom(pe) || (is_wb && (pe->type != e_func || 
strcmp(sf->func->base.name, "diff") != 0)))
+               list *rankopargs = e->l, *gbe = ((list*)e->r)->h->data;
+               sql_exp *pe = list_empty(gbe) ? NULL : (sql_exp*)gbe->t->data;
+               bool has_pe = pe != NULL;
+
+               if (!pe || pe->type != e_func || strcmp(((sql_subfunc 
*)pe->f)->func->base.name, "diff") != 0)
                        pe = NULL;
-               else
-                       is_wb = 0;
-               if (ad)
-               for(d=ad->h; d; d=d->next) {
+
+               for(node *d = ad->h; d; d=d->next) {
                        sql_subfunc *df;
-                       sql_exp *e = d->data;
+                       sql_exp *de = d->data;
                        list *args = sa_list(sql->sa);
                        if (pe) {
-                               df = sql_bind_func(sql->sa, NULL, "diff", bt, 
exp_subtype(e), F_ANALYTIC);
+                               df = sql_bind_func(sql->sa, NULL, "diff", bt, 
exp_subtype(de), F_ANALYTIC);
                                append(args, pe);
                        } else {
-                               df = sql_bind_func(sql->sa, NULL, "diff", 
exp_subtype(e), NULL, F_ANALYTIC);
+                               df = sql_bind_func(sql->sa, NULL, "diff", 
exp_subtype(de), NULL, F_ANALYTIC);
                        }
                        assert(df);
-                       append(args, e);
+                       append(args, de);
                        pe = exp_op(sql->sa, args, df);
                }
-               if (is_wb)
-                       e->l = list_prepend(rankopargs, pe);
-               else
-                       n->data = pe;
+
+               if (!supports_frames(e)) {
+                       for (node *n = rankopargs->h; n ; n = n->next) {
+                               if (n->next == rankopargs->t) {
+                                       n->data = pe;
+                                       break;
+                               }
+                       }
+               } else if (!strcmp(sf->func->base.name, "nth_value") || 
!strcmp(sf->func->base.name, "first_value") || !strcmp(sf->func->base.name, 
"last_value")) {
+                       sql_exp *window1 = list_fetch(rankopargs, 
list_length(sf->func->ops)), *window2 = list_fetch(rankopargs, 
list_length(sf->func->ops) + 1);
+                       list *lw1 = window1->l, *lw2 = window2->l; /* the value 
functions require bound functions always */
+
+                       if (has_pe) {
+                               assert(list_length(window1->l) == 6);
+                               lw1->h->data = exp_copy(sql, pe);
+                               lw2->h->data = exp_copy(sql, pe);
+                       } else {
+                               window1->l = list_prepend(lw1, exp_copy(sql, 
pe));
+                               window2->l = list_prepend(lw2, exp_copy(sql, 
pe));
+                       }
+               } else { /* aggregate case */
+                       bool found_window_bound = false;
+                       for (node *n = rankopargs->h; n && !found_window_bound 
; n = n->next) {
+                               sql_exp *e = n->data;
+                               found_window_bound |= e->type == e_func && 
!strcmp(((sql_subfunc *)e->f)->func->base.name, "window_bound");
+                       }
+
+                       if (found_window_bound) { /* complex case, requires 
bound function call */
+                               sql_exp *window1 = list_fetch(rankopargs, 
list_length(sf->func->ops) + 1), *window2 = list_fetch(rankopargs, 
list_length(sf->func->ops) + 2);
+                               list *lw1 = window1->l, *lw2 = window2->l;
+                               assert(list_length(window1->l) == 
list_length(window2->l));
+
+                               if (has_pe) {
+                                       assert(list_length(window1->l) == 6);
+                                       lw1->h->data = exp_copy(sql, pe);
+                                       lw2->h->data = exp_copy(sql, pe);
+                               } else {
+                                       assert(list_length(window1->l) == 5);
+                                       window1->l = list_prepend(lw1, 
exp_copy(sql, pe));
+                                       window2->l = list_prepend(lw2, 
exp_copy(sql, pe));
+                                       list_append(e->l, exp_copy(sql, pe));
+                               }
+                       } else { /* trivial case, there's no need to compute 
bounds */
+                               if (has_pe) {
+                                       rankopargs->t->data = pe;
+                               } else {
+                                       e->l = list_append(e->l, pe);
+                               }
+                       }
+               }
        }
        return e;
 }
@@ -1676,6 +1708,8 @@ exp_reset_card_and_freevar(visitor *v, s
        (void)v;
        (void)depth;
 
+       if (e->type == e_func && e->r) /* mark as normal (analytic) function 
now */
+               e->r = NULL;
        reset_freevar(e); /* unnesting is done, we can remove the freevar flag 
*/
        if (!rel->l)
                return e;
@@ -2066,7 +2100,7 @@ rewrite_rank(visitor *v, sql_rel *rel, s
        sql_rel *rell = NULL;
        int needed = 0;
 
-       if (!is_simple_project(rel->op) || e->type != e_func || !e->r /* e->r 
means window function */)
+       if (!is_simple_project(rel->op) || e->type != e_func || 
list_length(e->r) < 2 /* e->r means window function */)
                return e;
 
        (void)depth;
@@ -2140,8 +2174,8 @@ rewrite_rank(visitor *v, sql_rel *rel, s
                rell->r = gbe;
                rel->l = rell;
 
-               /* mark as normal (analytic) function now */
-               e->r = NULL;
+               /* remove obe argument, so this function won't be called again 
on this expression */
+               list_remove_node(r, r->t);
 
                /* add project with rank */
                rell = rel->l = rel_project(v->sql->sa, rel->l, 
rel_projections(v->sql, rell->l, NULL, 1, 1));
@@ -2151,8 +2185,8 @@ rewrite_rank(visitor *v, sql_rel *rel, s
                append(rell->exps, e);
                e = exp_ref(v->sql, e);
        } else {
-               /* mark as normal (analytic) function now */
-               e->r = NULL;
+               /* remove obe argument, so this function won't be called again 
on this expression */
+               list_remove_node(r, r->t);
        }
        return e;
 }
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to