diff options
author | igor@olga.mysql.com <> | 2007-01-25 18:44:35 -0800 |
---|---|---|
committer | igor@olga.mysql.com <> | 2007-01-25 18:44:35 -0800 |
commit | 36df33d80abb4ed946c7feee6b20205619ba3534 (patch) | |
tree | 8a121243129b7782eb39a40dd3442eb15198b028 | |
parent | 9659c11b6eee9e3ddf7d18b40d9afbc00e310bf2 (diff) | |
download | mariadb-git-36df33d80abb4ed946c7feee6b20205619ba3534.tar.gz |
Fixed bug #24653.
The bug report has demonstrated the following two problems.
1. If an ORDER/GROUP BY list includes a constant expression being
optimized away and, at the same time, containing single-row
subselects that return more that one row, no error is reported.
Strictly speaking the standard allows to ignore error in this case.
Yet, now a corresponding fatal error is reported in this case.
2. If a query requires sorting by expressions containing single-row
subselects that, however, return more than one row, then the execution
of the query may cause a server crash.
To fix this some code has been added that blocks execution of a subselect
item in case of a fatal error in the method Item_subselect::exec.
-rw-r--r-- | mysql-test/r/subselect.result | 74 | ||||
-rw-r--r-- | mysql-test/t/subselect.test | 59 | ||||
-rw-r--r-- | sql/filesort.cc | 6 | ||||
-rw-r--r-- | sql/item.cc | 1 | ||||
-rw-r--r-- | sql/item.h | 3 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 7 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 3 | ||||
-rw-r--r-- | sql/item_func.cc | 1 | ||||
-rw-r--r-- | sql/item_subselect.cc | 4 | ||||
-rw-r--r-- | sql/sql_select.cc | 21 |
10 files changed, 174 insertions, 5 deletions
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index a4ea8e21d61..a339a139687 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3026,3 +3026,77 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE DROP TABLE t1; +CREATE TABLE t1 (a int); +INSERT INTO t1 VALUES (2), (4), (1), (3); +CREATE TABLE t2 (b int, c int); +INSERT INTO t2 VALUES +(2,1), (1,3), (2,1), (4,4), (2,2), (1,4); +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2 ); +a +2 +4 +1 +3 +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1); +ERROR 21000: Subquery returns more than 1 row +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2), a; +a +1 +2 +3 +4 +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1), a; +ERROR 21000: Subquery returns more than 1 row +SELECT b, MAX(c) FROM t2 GROUP BY b, (SELECT c FROM t2 WHERE b > 2); +b MAX(c) +1 4 +2 2 +4 4 +SELECT b, MAX(c) FROM t2 GROUP BY b, (SELECT c FROM t2 WHERE b > 1); +ERROR 21000: Subquery returns more than 1 row +SELECT a FROM t1 GROUP BY a +HAVING IFNULL((SELECT b FROM t2 WHERE b > 2), +(SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; +a +1 +2 +3 +4 +SELECT a FROM t1 GROUP BY a +HAVING IFNULL((SELECT b FROM t2 WHERE b > 1), +(SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; +ERROR 21000: Subquery returns more than 1 row +SELECT a FROM t1 GROUP BY a +HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), +(SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; +a +4 +SELECT a FROM t1 GROUP BY a +HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), +(SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)) > 3; +ERROR 21000: Subquery returns more than 1 row +SELECT a FROM t1 +ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 2), +(SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); +a +2 +4 +1 +3 +SELECT a FROM t1 +ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 1), +(SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); +ERROR 21000: Subquery returns more than 1 row +SELECT a FROM t1 +ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), +(SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); +a +2 +1 +3 +4 +SELECT a FROM t1 +ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), +(SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); +ERROR 21000: Subquery returns more than 1 row +DROP TABLE t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index f816551e51f..67a18e7a30f 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1993,4 +1993,63 @@ SELECT a FROM t1 WHERE (SELECT 1 FROM DUAL WHERE 1=0) IS NULL; EXPLAIN SELECT a FROM t1 WHERE (SELECT 1 FROM DUAL WHERE 1=0) IS NULL; DROP TABLE t1; + +# +# Bug 24653: sorting by expressions containing subselects +# that return more than one row +# + +CREATE TABLE t1 (a int); +INSERT INTO t1 VALUES (2), (4), (1), (3); + +CREATE TABLE t2 (b int, c int); +INSERT INTO t2 VALUES + (2,1), (1,3), (2,1), (4,4), (2,2), (1,4); + +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2 ); +--error 1242 +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1); +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2), a; +--error 1242 +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1), a; + +SELECT b, MAX(c) FROM t2 GROUP BY b, (SELECT c FROM t2 WHERE b > 2); +--error 1242 +SELECT b, MAX(c) FROM t2 GROUP BY b, (SELECT c FROM t2 WHERE b > 1); + + +SELECT a FROM t1 GROUP BY a + HAVING IFNULL((SELECT b FROM t2 WHERE b > 2), + (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; +--error 1242 +SELECT a FROM t1 GROUP BY a + HAVING IFNULL((SELECT b FROM t2 WHERE b > 1), + (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; + +SELECT a FROM t1 GROUP BY a + HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), + (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; +--error 1242 +SELECT a FROM t1 GROUP BY a + HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), + (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)) > 3; + +SELECT a FROM t1 + ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 2), + (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); +--error 1242 +SELECT a FROM t1 + ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 1), + (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); + +SELECT a FROM t1 + ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), + (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); +--error 1242 +SELECT a FROM t1 + ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), + (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); + +DROP TABLE t1,t2; + # End of 4.1 tests diff --git a/sql/filesort.cc b/sql/filesort.cc index 63a8515020b..38a49e24263 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -387,7 +387,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH]; my_off_t record; TABLE *sort_form; - volatile my_bool *killed= ¤t_thd->killed; + THD *thd= current_thd; + volatile my_bool *killed= &thd->killed; handler *file; DBUG_ENTER("find_all_keys"); DBUG_PRINT("info",("using: %s",(select?select->quick?"ranges":"where":"every row"))); @@ -474,6 +475,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, } else file->unlock_row(); + /* It does not make sense to read more keys in case of a fatal error */ + if (thd->net.report_error) + DBUG_RETURN(HA_POS_ERROR); } (void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */ if (!next_pos) diff --git a/sql/item.cc b/sql/item.cc index 1e8c70c6616..7d110ccdc25 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -47,6 +47,7 @@ Item::Item(): collation.set(&my_charset_bin, DERIVATION_COERCIBLE); name= 0; decimals= 0; max_length= 0; + with_subselect= 0; /* Put item in free list so that we can free all items at end */ THD *thd= current_thd; diff --git a/sql/item.h b/sql/item.h index ad8bea663f1..f2136c4997a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -142,6 +142,9 @@ public: my_bool with_sum_func; my_bool fixed; /* If item fixed with fix_fields */ DTCollation collation; + my_bool with_subselect; /* If this item is a subselect or some + of its arguments is or contains a + subselect */ // alloc & destruct is done as start of select using sql_alloc Item(); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 859b4e0ecc1..91546c2282c 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2139,6 +2139,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) and_tables_cache&= tmp_table_map; const_item_cache&= item->const_item(); with_sum_func= with_sum_func || item->with_sum_func; + with_subselect|= item->with_subselect; if (item->maybe_null) maybe_null=1; } @@ -2351,7 +2352,7 @@ longlong Item_func_isnull::val_int() Handle optimization if the argument can't be null This has to be here because of the test in update_used_tables(). */ - if (!used_tables_cache) + if (!used_tables_cache && !with_subselect) return cached_value; return args[0]->is_null() ? 1: 0; } @@ -2360,7 +2361,7 @@ longlong Item_is_not_null_test::val_int() { DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_is_not_null_test::val_int"); - if (!used_tables_cache) + if (!used_tables_cache && !with_subselect) { owner->was_null|= (!cached_value); DBUG_PRINT("info", ("cached :%d", cached_value)); @@ -2387,7 +2388,7 @@ void Item_is_not_null_test::update_used_tables() else { args[0]->update_used_tables(); - if (!(used_tables_cache=args[0]->used_tables())) + if (!(used_tables_cache=args[0]->used_tables()) && !with_subselect) { /* Remember if the value is always NULL or never NULL */ cached_value= (longlong) !args[0]->is_null(); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 0e157fd412c..4635a301c31 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -843,7 +843,8 @@ public: else { args[0]->update_used_tables(); - if ((const_item_cache= !(used_tables_cache= args[0]->used_tables()))) + if ((const_item_cache= !(used_tables_cache= args[0]->used_tables())) && + !with_subselect) { /* Remember if the value is always NULL or never NULL */ cached_value= (longlong) args[0]->is_null(); diff --git a/sql/item_func.cc b/sql/item_func.cc index 461998477b5..92ba7520642 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -177,6 +177,7 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) used_tables_cache|= item->used_tables(); not_null_tables_cache|= item->not_null_tables(); const_item_cache&= item->const_item(); + with_subselect|= item->with_subselect; } } fix_length_and_dec(); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 20a092d7607..cdbcde8b56b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -39,6 +39,7 @@ Item_subselect::Item_subselect(): engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1), engine_changed(0), changed(0) { + with_subselect= 1; reset(); /* item value is NULL if select_subselect not changed this value @@ -201,6 +202,9 @@ bool Item_subselect::exec() mem root */ thd->mem_root= &thd->main_mem_root; + if (thd->net.report_error) + /* Do not execute subselect in case of a fatal error */ + return 1; res= engine->exec(); thd->mem_root= old_root; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0254e8f56dc..97fa6bb7809 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -644,6 +644,13 @@ JOIN::optimize() { ORDER *org_order= order; order=remove_const(this, order,conds,1, &simple_order); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from remove_const")); + DBUG_RETURN(1); + } + /* If we are using ORDER BY NULL or ORDER BY const_expression, return result in any order (even if we are using a GROUP BY) @@ -747,6 +754,12 @@ JOIN::optimize() group_list= remove_const(this, (old_group_list= group_list), conds, rollup.state == ROLLUP::STATE_NONE, &simple_group); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from remove_const")); + DBUG_RETURN(1); + } if (old_group_list && !group_list) select_distinct= 0; } @@ -763,6 +776,12 @@ JOIN::optimize() { group_list= procedure->group= remove_const(this, procedure->group, conds, 1, &simple_group); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from remove_const")); + DBUG_RETURN(1); + } calc_group_buffer(this, group_list); } @@ -4428,6 +4447,8 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, *simple_order=0; // Must do a temp table to sort else if (!(order_tables & not_const_tables)) { + if (order->item[0]->with_subselect) + order->item[0]->val_str(&order->item[0]->str_value); DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); continue; // skip const item } |