Changeset: 2f53edef5058 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=2f53edef5058
Added Files:
sql/test/out2in/Tests/All
sql/test/out2in/Tests/out2in.sql
sql/test/out2in/Tests/out2in.stable.err
sql/test/out2in/Tests/out2in.stable.out
Modified Files:
sql/backends/monet5/sql_upgrades.c
sql/common/sql_types.c
sql/server/rel_exp.c
sql/server/rel_optimizer.c
sql/server/rel_out2inner_join.txt
sql/test/Tests/50ways.stable.out
sql/test/osm/Tests/drop_constraint_bug.stable.out
sql/test/subquery/Tests/correlated.stable.out
sql/test/subquery/Tests/subquery5.stable.out
Branch: default
Log Message:
Merge with default.
diffs (truncated from 608 to 300 lines):
diff --git a/sql/backends/monet5/sql_upgrades.c
b/sql/backends/monet5/sql_upgrades.c
--- a/sql/backends/monet5/sql_upgrades.c
+++ b/sql/backends/monet5/sql_upgrades.c
@@ -2789,7 +2789,7 @@ sql_update_semantics(Client c)
{
char* update_query =
"update sys.functions set semantics = false where type <> 6 and func
not ilike '%CREATE FUNCTION%' and name in
('length','octet_length','>','>=','<','<=','min','max','sql_min','sql_max','least','greatest','sum','prod','mod','and',\n"
-
"'or','xor','not','sql_mul','sql_div','sql_sub','sql_add','bit_and','bit_or','bit_xor','bit_not','left_shift','right_shift','abs','sign','scale_up','scale_down','round','power','floor','ceil','ceiling','sin','cos','tan','asin',\n"
+
"'xor','not','sql_mul','sql_div','sql_sub','sql_add','bit_and','bit_or','bit_xor','bit_not','left_shift','right_shift','abs','sign','scale_up','scale_down','round','power','floor','ceil','ceiling','sin','cos','tan','asin',\n"
"'acos','atan','sinh','cot','cosh','tanh','sqrt','exp','log','ln','log10','log2','pi','curdate','current_date','curtime','current_time','current_timestamp','localtime','localtimestamp','local_timezone','century','decade','year',\n"
"'quarter','month','day','dayofyear','weekofyear','dayofweek','dayofmonth','week','hour','minute','second','strings','locate','charindex','splitpart','substring','substr','truncate','concat','ascii','code','right','left','upper',\n"
"'ucase','lower','lcase','trim','ltrim','rtrim','lpad','rpad','insert','replace','repeat','space','char_length','character_length','soundex','qgramnormalize');";
diff --git a/sql/common/sql_types.c b/sql/common/sql_types.c
--- a/sql/common/sql_types.c
+++ b/sql/common/sql_types.c
@@ -1616,7 +1616,7 @@ sqltypeinit( sql_allocator *sa)
sql_create_analytic(sa, "listagg", "sql", "str_group_concat",
SCALE_NONE, STR, 2, STR, STR);
sql_create_func(sa, "and", "calc", "and", FALSE, FALSE, SCALE_FIX, 0,
BIT, 2, BIT, BIT);
- sql_create_func(sa, "or", "calc", "or", FALSE, FALSE, SCALE_FIX, 0,
BIT, 2, BIT, BIT);
+ sql_create_func(sa, "or", "calc", "or", TRUE, FALSE, SCALE_FIX, 0,
BIT, 2, BIT, BIT);
sql_create_func(sa, "xor", "calc", "xor", FALSE, FALSE, SCALE_FIX, 0,
BIT, 2, BIT, BIT);
sql_create_func(sa, "not", "calc", "not", FALSE, FALSE, SCALE_FIX, 0,
BIT, 1, BIT);
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
@@ -326,6 +326,7 @@ exp_op( sql_allocator *sa, list *l, sql_
e->card = CARD_ATOM; /* unop returns a single atom */
e->l = l;
e->f = f;
+ e->semantics = f->func->semantics;
fres = exp_subtype(e);
/* corner case if the output of the function is void, set the type to
one of the inputs */
@@ -1693,10 +1694,10 @@ exp_is_cmp_exp_is_false(mvc *sql, sql_ex
* Other cases in is-semantics are unspecified.
*/
if (e->flag == cmp_equal && !e->anti) {
- return (exp_is_null(sql, l) && exp_is_null(sql, r));
+ return ((exp_is_null(sql, l) && exp_is_not_null(sql, r)) ||
(exp_is_not_null(sql, l) && exp_is_null(sql, r)));
}
if (((e->flag == cmp_notequal) && !e->anti) || ((e->flag == cmp_equal) &&
e->anti) ) {
- return ((exp_is_null(sql, l) && exp_is_not_null(sql, r))) ||
((exp_is_not_null(sql, l) && exp_is_null(sql, r)));
+ return ((exp_is_null(sql, l) && exp_is_null(sql, r)) ||
(exp_is_not_null(sql, l) && exp_is_not_null(sql, r)));
}
return false;
@@ -1736,7 +1737,29 @@ exp_regular_cmp_exp_is_false(mvc *sql, s
static inline bool
exp_or_exp_is_false(mvc *sql, sql_exp* e) {
assert(e->type == e_cmp && e->flag == cmp_or);
- return exp_is_false(sql, e->l) && exp_is_false(sql, e->r);
+
+ list* left = e->l;
+ list* right = e->r;
+
+ bool left_is_false = false;
+ for(node* n = left->h; n; n=n->next) {
+ if (exp_is_false(sql, n->data)) {
+ left_is_false=true;
+ break;
+ }
+ }
+
+ if (!left_is_false) {
+ return false;
+ }
+
+ for(node* n = right->h; n; n=n->next) {
+ if (exp_is_false(sql, n->data)) {
+ return true;
+ }
+ }
+
+ return false;
}
static inline bool
@@ -1817,6 +1840,19 @@ exp_is_null(mvc *sql, sql_exp *e )
case e_convert:
return exp_is_null(sql, e->l);
case e_func:
+ if (!e->semantics && e->l) {
+ /* This is a call to a function with no-nil semantics.
+ * If one of the parameters is null the expression
itself is null
+ */
+ list* l = e->l;
+ for(node* n = l->h; n; n=n->next) {
+ sql_exp* p = n->data;
+ if (exp_is_null(sql, p)) {
+ return true;
+ }
+ }
+ }
+ return 0;
case e_aggr:
case e_column:
case e_cmp:
diff --git a/sql/server/rel_optimizer.c b/sql/server/rel_optimizer.c
--- a/sql/server/rel_optimizer.c
+++ b/sql/server/rel_optimizer.c
@@ -9037,6 +9037,157 @@ rel_merge_table_rewrite(mvc *sql, sql_re
return rel;
}
+static bool is_non_trivial_select_applied_to_outer_join(sql_rel *rel) {
+ return is_select(rel->op) && rel->exps && is_outerjoin(((sql_rel*)
rel->l)->op);
+}
+
+extern list *list_append_before(list *l, node *n, void *data);
+
+static void replace_column_references_with_nulls_2(mvc *sql, list* crefs,
sql_exp* e);
+
+static void
+replace_column_references_with_nulls_1(mvc *sql, list* crefs, list* exps) {
+ for(node* n = exps->h; n; n=n->next) {
+ sql_exp* e = n->data;
+ replace_column_references_with_nulls_2(sql, crefs, e);
+ }
+}
+
+static void
+replace_column_references_with_nulls_2(mvc *sql, list* crefs, sql_exp* e) {
+ if (e == NULL) {
+ return;
+ }
+
+ switch (e->type) {
+ case e_column:
+ for(node* n = crefs->h; n; n=n->next) {
+ sql_exp* c = n->data;
+
+ if (exp_match(e, c)) {
+ e->type = e_atom;
+ e->l = atom_general(sql->sa, &e->tpe, NULL);
+ e->r = e->f = NULL;
+ break;
+ }
+ }
+ break;
+ case e_cmp:
+ switch (e->flag) {
+ case cmp_gt:
+ case cmp_gte:
+ case cmp_lte:
+ case cmp_lt:
+ case cmp_equal:
+ case cmp_notequal:
+ {
+ sql_exp* l = e->l;
+ sql_exp* r = e->r;
+ sql_exp* f = e->f;
+
+ replace_column_references_with_nulls_2(sql, crefs, l);
+ replace_column_references_with_nulls_2(sql, crefs, r);
+ replace_column_references_with_nulls_2(sql, crefs, f);
+ break;
+ }
+ case cmp_or:
+ {
+ list* l = e->l;
+ list* r = e->r;
+ replace_column_references_with_nulls_1(sql, crefs, l);
+ replace_column_references_with_nulls_1(sql, crefs, r);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case e_func:
+ {
+ list* l = e->l;
+ replace_column_references_with_nulls_1(sql, crefs, l);
+ break;
+ }
+ case e_convert:
+ {
+ sql_exp* l = e->l;
+ replace_column_references_with_nulls_2(sql, crefs, l);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static sql_rel *
+out2inner(mvc *sql, sql_rel* sel, sql_rel* join, sql_rel* inner_join_side, int
*changes, operator_type new_type) {
+
+ list* select_predicates = exps_copy(sql, sel->exps);
+
+ if (!is_base(inner_join_side->op) &&
!is_simple_project(inner_join_side->op)) {
+ // Nothing to do here.
+ return sel;
+ }
+
+ list* inner_join_column_references = inner_join_side->exps;
+
+ for(node* n = select_predicates->h; n; n=n->next) {
+ sql_exp* e = n->data;
+ replace_column_references_with_nulls_2(sql,
inner_join_column_references, e);
+
+ if (exp_is_false(sql, e)) {
+ join->op = new_type;
+ (*changes)++;
+ break;
+ }
+ }
+
+ return sel;
+}
+
+static sql_rel *
+rel_out2inner(mvc *sql, sql_rel *rel, int *changes) {
+
+ if (!is_non_trivial_select_applied_to_outer_join(rel)) {
+ // Nothing to do here.
+ return rel;
+ }
+
+ sql_rel* join = (sql_rel*) rel->l;
+
+ if (rel_is_ref(join)) {
+ /* Do not alter a multi-referenced join relation.
+ * This is problematic (e.g. in the case of the plan of a merge
statement)
+ * basically because there are no guarantees on the other
container relations.
+ * In particular there is no guarantee that the other
referencing relations are
+ * select relations with null-rejacting predicates on the inner
join side.
+ */
+ return rel;
+ }
+
+ sql_rel* inner_join_side;
+ if (is_left(join->op)) {
+ inner_join_side = join->r;
+ return out2inner(sql, rel, join, inner_join_side, changes, op_join);
+ }
+ else if (is_right(join->op)) {
+ inner_join_side = join->l;
+ return out2inner(sql, rel, join, inner_join_side, changes, op_join);
+ }
+ else /*full outer join*/ {
+ // First check if left side can degenerate from full outer join to
just right outer join.
+ inner_join_side = join->r;
+ rel = out2inner(sql, rel, join, inner_join_side, changes, op_right);
+ /* Now test if the right side can degenerate to
+ * a normal inner join or a left outer join
+ * depending on the result of previous call to out2inner.
+ */
+
+ inner_join_side = join->l;
+ return out2inner(sql, rel, join, inner_join_side, changes,
is_right(join->op)? op_join: op_left);
+ }
+}
+
static sql_rel*
exp_skip_output_parts(sql_rel *rel)
{
@@ -9247,6 +9398,7 @@ optimize_rel(mvc *sql, sql_rel *rel, int
if (gp.cnt[op_join] || gp.cnt[op_left] || gp.cnt[op_right] ||
gp.cnt[op_full] || gp.cnt[op_semi] || gp.cnt[op_anti]) {
rel = rel_remove_empty_join(sql, rel, &changes);
+ rel = rel_visitor_topdown(sql, rel, &rel_out2inner, &changes);
rel = rel_visitor_bottomup(sql, rel, &rel_join2semijoin,
&changes);
if (!gp.cnt[op_update])
rel = rel_join_order(sql, rel);
@@ -9328,6 +9480,7 @@ optimize_rel(mvc *sql, sql_rel *rel, int
rel = rel_visitor_topdown(sql, rel, &rel_merge_table_rewrite,
&changes);
if (level <= 0 && mvc_debug_on(sql,8))
rel = rel_visitor_topdown(sql, rel, &rel_add_dicts, &changes);
+
*g_changes = changes;
return rel;
}
diff --git a/sql/server/rel_out2inner_join.txt
b/sql/server/rel_out2inner_join.txt
--- a/sql/server/rel_out2inner_join.txt
+++ b/sql/server/rel_out2inner_join.txt
@@ -5,8 +5,8 @@ Say we have two single integer column ta
create table foo (i int);
insert into foo values (10), (40), (20), (5);
-create table bar (i int);
-insert into bar values (30), (20), (50), (40);
+create table bar (i int, j int);
+insert into bar values (30, 300), (20, 200), (50, 500), (40, 400);
select * from foo;
+------+
@@ -28,7 +28,7 @@ select * from bar;
+------+
We compute the following outer join
-select * from foo left join bar on foo.i = bar.i where bar.i is not null;
+select foo.i, bar.i from foo left join bar on foo.i = bar.i where bar.j is not
null;
+------+------+
| i | i |
+======+======+
diff --git a/sql/test/Tests/50ways.stable.out b/sql/test/Tests/50ways.stable.out
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list