Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions mysql-test/main/literal_boolean_cond_folding.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#
# MDEV-37713/MDEV-37714: Test correctness of boolean folding
#
SELECT TRUE AND TRUE;
TRUE AND TRUE
1
SELECT TRUE AND FALSE;
TRUE AND FALSE
0
SELECT FALSE AND TRUE;
FALSE AND TRUE
0
SELECT FALSE AND FALSE;
FALSE AND FALSE
0
SELECT TRUE OR TRUE;
TRUE OR TRUE
1
SELECT TRUE OR FALSE;
TRUE OR FALSE
1
SELECT TRUE OR TRUE;
TRUE OR TRUE
1
SELECT FALSE OR FALSE;
FALSE OR FALSE
0
SELECT TRUE AND NULL;
TRUE AND NULL
NULL
SELECT NULL AND TRUE;
NULL AND TRUE
NULL
SELECT FALSE AND NULL;
FALSE AND NULL
0
SELECT NULL AND FALSE;
NULL AND FALSE
0
SELECT TRUE OR NULL;
TRUE OR NULL
1
SELECT NULL OR TRUE;
NULL OR TRUE
1
SELECT FALSE OR NULL;
FALSE OR NULL
NULL
SELECT NULL OR FALSE;
NULL OR FALSE
NULL
SELECT NULL AND NULL;
NULL AND NULL
NULL
SELECT NULL OR NULL;
NULL OR NULL
NULL
SELECT NOT NULL OR TRUE, NOT NULL AND FALSE;
NOT NULL OR TRUE NOT NULL AND FALSE
1 0
SELECT NULL AND TRUE, NULL OR FALSE;
NULL AND TRUE NULL OR FALSE
NULL NULL
PREPARE s FROM 'SELECT NOT NULL OR TRUE, NOT NULL AND FALSE';
EXECUTE s;
NOT NULL OR TRUE NOT NULL AND FALSE
1 0
EXECUTE s;
NOT NULL OR TRUE NOT NULL AND FALSE
1 0
DEALLOCATE PREPARE s;
CREATE TABLE t1(c0 INT);
CREATE TABLE t2(c0 INT);
INSERT INTO t1 VALUES(1),(2),(3);
INSERT INTO t2 VALUES(4),(5),(6);
SELECT (SELECT MIN(c0) FROM t2)<0 OR true;
(SELECT MIN(c0) FROM t2)<0 OR true
1
SELECT ((SELECT MIN(c0) FROM t2)<0 AND false) OR (SELECT MAX(c0) FROM t1)>0 AS f;
f
1
ANALYZE SELECT (SELECT MIN(c0) FROM t2)<0 OR true;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used
ANALYZE SELECT ((SELECT MIN(c0) FROM t2)<0 AND false) OR (SELECT MAX(c0) FROM t1)>0;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used
3 SUBQUERY t1 ALL NULL NULL NULL NULL 3 3.00 100.00 100.00
CREATE VIEW v1 AS SELECT (SELECT MIN(c0) FROM t2)<0 OR true FROM t1;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select (select min(`t2`.`c0`) from `t2`) < 0 or 1 AS `(SELECT MIN(c0) FROM t2)<0 OR true` from `t1` latin1 latin1_swedish_ci
DROP VIEW v1;
DROP TABLE t1,t2;
# End of 13.1 tests
55 changes: 55 additions & 0 deletions mysql-test/main/literal_boolean_cond_folding.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--echo #
--echo # MDEV-37713/MDEV-37714: Test correctness of boolean folding
--echo #

SELECT TRUE AND TRUE;
SELECT TRUE AND FALSE;
SELECT FALSE AND TRUE;
SELECT FALSE AND FALSE;
SELECT TRUE OR TRUE;
SELECT TRUE OR FALSE;
SELECT TRUE OR TRUE;
SELECT FALSE OR FALSE;

SELECT TRUE AND NULL;
SELECT NULL AND TRUE;
SELECT FALSE AND NULL;
SELECT NULL AND FALSE;
SELECT TRUE OR NULL;
SELECT NULL OR TRUE;
SELECT FALSE OR NULL;
SELECT NULL OR FALSE;

SELECT NULL AND NULL;
SELECT NULL OR NULL;

SELECT NOT NULL OR TRUE, NOT NULL AND FALSE;
SELECT NULL AND TRUE, NULL OR FALSE;

PREPARE s FROM 'SELECT NOT NULL OR TRUE, NOT NULL AND FALSE';
EXECUTE s;
EXECUTE s;
DEALLOCATE PREPARE s;

CREATE TABLE t1(c0 INT);
CREATE TABLE t2(c0 INT);
INSERT INTO t1 VALUES(1),(2),(3);
INSERT INTO t2 VALUES(4),(5),(6);

SELECT (SELECT MIN(c0) FROM t2)<0 OR true;
SELECT ((SELECT MIN(c0) FROM t2)<0 AND false) OR (SELECT MAX(c0) FROM t1)>0 AS f;

# Folds to constant true, t2 subselect is eliminated
ANALYZE SELECT (SELECT MIN(c0) FROM t2)<0 OR true;
Comment thread
jaeheonshim marked this conversation as resolved.

# (... AND false) folds to false and is dropped; only the t1 subselect remains
ANALYZE SELECT ((SELECT MIN(c0) FROM t2)<0 AND false) OR (SELECT MAX(c0) FROM t1)>0;

# Check that views are not malformed by the folding
CREATE VIEW v1 AS SELECT (SELECT MIN(c0) FROM t2)<0 OR true FROM t1;
SHOW CREATE VIEW v1;

DROP VIEW v1;
DROP TABLE t1,t2;

--echo # End of 13.1 tests
57 changes: 57 additions & 0 deletions sql/item_cmpfunc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5736,6 +5736,63 @@ bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel)
return true;
}

/**
Recursively simplifies AND/OR expressions containing boolean literals.

@note
This can potentially allow skipping evaluation of some non-literal
parts of the entire expression.

E.g. a query like
SELECT (SELECT MIN(c0) FROM t2)<0 OR true;
will be simplified to
SELECT true;
which allows skipping the evaluation of SELECT MIN(c0) FROM t2.
*/
Item *Item_cond::simplify_cond(THD *thd)
Comment thread
jaeheonshim marked this conversation as resolved.
{
List_iterator<Item> li(list);
Item *child;

if (check_stack_overrun(thd, STACK_MIN_SIZE, NULL))
return this;

while ((child= li++))
{
if (child->type() == Item::COND_ITEM)
{
Item *new_child= static_cast<Item_cond *>(child)->simplify_cond(thd);
if (new_child != child)
li.replace(new_child);
}
}
bool is_and= functype() == Item_func::COND_AND_FUNC;
bool is_or= functype() == Item_func::COND_OR_FUNC;
if (is_and || is_or)
{
List_iterator<Item> li2(list);
while ((child= li2++))
{
Item *real= child->real_item();
if (real->is_bool_literal())
{
if (real->val_bool() == is_or)
return new (thd->mem_root) Item_bool(thd, is_or);
else
li2.remove();
}
}
if (list.is_empty())
Comment thread
grooverdan marked this conversation as resolved.
Comment thread
jaeheonshim marked this conversation as resolved.
return new (thd->mem_root) Item_bool(thd, is_and);
if (list.elements == 1)
{
Item *item= list.head();
list.empty();
return item;
}
}
return this;
}

void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding)
{
Expand Down
1 change: 1 addition & 0 deletions sql/item_cmpfunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -3450,6 +3450,7 @@ class Item_cond :public Item_bool_func
Item *deep_copy(THD *thd) const override;
bool excl_dep_on_table(table_map tab_map) override;
bool excl_dep_on_grouping_fields(st_select_lex *sel) override;
Item *simplify_cond(THD *thd);

private:
void merge_sub_condition(List_iterator<Item>& li);
Expand Down
20 changes: 20 additions & 0 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,26 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
}
}

if (thd->lex->current_select->first_cond_optimization &&
!thd->lex->is_view_context_analysis())
{
Query_arena_stmt on_stmt_arena(thd);
List_iterator<Item> li(select_lex->item_list);
Item *item;
while ((item= li++))
{
if (item->type() == Item::COND_ITEM)
{
Item *new_item= static_cast<Item_cond *>(item)->simplify_cond(thd);
if (new_item != item)
{
new_item->share_name_with(item);
li.replace(new_item);
}
}
}
}

if (setup_fields(thd, ref_ptrs, fields_list, select_lex->item_list_usage,
&all_fields, &select_lex->pre_fix, 1))
DBUG_RETURN(-1);
Expand Down