*** sql/item_func.cc Mon Mar 19 20:59:20 2007 --- ../mysql-5.0.bk-sub-aggr/sql/item_func.cc Tue Mar 27 20:25:34 2007 *************** *** 163,174 **** --- 163,184 ---- return TRUE; /* purecov: inspected */ item= *arg; if (allowed_arg_cols) { + /* + allowed_arg_cols could have been set by the function, rather than + being fetched from the 1st column. + */ if (item->check_cols(allowed_arg_cols)) return 1; + /* + Don't compare the item with itself + Currently all args to a function must have the same structure + */ + if (arg != args && item->check_cols_deeply(*args)) + return 1; } else { /* we have to fetch allowed_arg_cols from first argument */ DBUG_ASSERT(arg == args); // it is first argument *** sql/item.h Thu Mar 22 08:21:02 2007 --- ../mysql-5.0.bk-sub-aggr/sql/item.h Tue Mar 27 18:39:46 2007 *************** *** 811,820 **** --- 811,824 ---- // Row emulation virtual uint cols() { return 1; } virtual Item* element_index(uint i) { return this; } virtual Item** addr(uint i) { return 0; } virtual bool check_cols(uint c); + #ifndef DBUG_OFF + bool in_check_cols_deeply; + #endif + bool check_cols_deeply(Item* item); // It is not row => null inside is impossible virtual bool null_inside() { return 0; } // used in row subselects to get value of elements virtual void bring_value() {} *** sql/item_cmpfunc.cc Thu Mar 22 19:48:37 2007 --- ../mysql-5.0.bk-sub-aggr/sql/item_cmpfunc.cc Tue Mar 27 21:05:22 2007 *************** *** 2548,2557 **** --- 2548,2558 ---- int cmp_item_row::cmp(Item *arg) { arg->null_value= 0; + DBUG_ASSERT(n == arg->cols()); if (arg->cols() != n) { my_error(ER_OPERAND_COLUMNS, MYF(0), n); return 1; } *************** *** 2571,2580 **** --- 2572,2591 ---- int cmp_item_row::compare(cmp_item *c) { cmp_item_row *l_cmp= (cmp_item_row *) c; + /* + This also asserts that c is a row, because rows have min 2 cols + if n >= 2 then c->cols >= 2 + */ + DBUG_ASSERT(n == c->cols()); + if (c->cols() != n) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), n); + return 1; + } for (uint i=0; i < n; i++) { int res; if ((res= comparators[i]->compare(l_cmp->comparators[i]))) return res; *************** *** 2766,2775 **** --- 2777,2789 ---- if (!args[i]->null_value) // Skip NULL values j++; else have_null= 1; } + /* If an error inside array->set occured, array->sort could fail */ + if (thd->net.report_error) + return; if ((array->used_count=j)) array->sort(); } } else *** sql/item_cmpfunc.h Thu Mar 8 17:29:58 2007 --- ../mysql-5.0.bk-sub-aggr/sql/item_cmpfunc.h Tue Mar 27 18:05:39 2007 *************** *** 905,916 **** --- 905,926 ---- virtual int cmp(Item *item)= 0; // for optimized IN with row virtual int compare(cmp_item *item)= 0; static cmp_item* get_comparator(Item_result type, CHARSET_INFO *cs); virtual cmp_item *make_same()= 0; + virtual uint cols() + { + return 1; + } virtual void store_value_by_template(cmp_item *tmpl, Item *item) { + DBUG_ASSERT(tmpl->cols() == item->cols()); + if (tmpl->cols() != item->cols()) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), tmpl->cols()); + return; + } store_value(item); } }; class cmp_item_string :public cmp_item *************** *** 1016,1025 **** --- 1026,1039 ---- ~cmp_item_row(); void store_value(Item *item); int cmp(Item *arg); int compare(cmp_item *arg); cmp_item *make_same(); + uint cols() + { + return n; + } void store_value_by_template(cmp_item *tmpl, Item *); }; class in_row :public in_vector *** sql/item.cc Thu Mar 22 12:37:24 2007 --- ../mysql-5.0.bk-sub-aggr/sql/item.cc Tue Mar 27 19:06:41 2007 *************** *** 339,348 **** --- 339,351 ---- Item::Item(): rsize(0), name(0), orig_name(0), name_length(0), fixed(0), is_autogenerated_name(TRUE), collation(&my_charset_bin, DERIVATION_COERCIBLE) { + #ifndef DBUG_OFF + in_check_cols_deeply= 0; + #endif marker= 0; maybe_null=null_value=with_sum_func=unsigned_flag=0; decimals= 0; max_length= 0; with_subselect= 0; cmp_context= (Item_result)-1; *************** *** 385,394 **** --- 388,400 ---- with_sum_func(item->with_sum_func), fixed(item->fixed), collation(item->collation), cmp_context(item->cmp_context) { + #ifndef DBUG_OFF + in_check_cols_deeply= 0; + #endif next= thd->free_list; // Put in free list thd->free_list= this; } *************** *** 644,653 **** --- 650,686 ---- return 1; } return 0; } + bool Item::check_cols_deeply(Item* item) + { + #ifndef DBUG_OFF + DBUG_ASSERT(! in_check_cols_deeply); + in_check_cols_deeply= 1; + #endif + if(cols() != item->cols()) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), item->cols()); + return 1; + } + for (int c= 0; c < cols(); c++) + { + Item* i= element_index(c); + if (i != this && i->check_cols_deeply(item->element_index(c))) + { + #ifndef DBUG_OFF + in_check_cols_deeply= 0; + #endif + return 1; + } + } + #ifndef DBUG_OFF + in_check_cols_deeply= 0; + #endif + return 0; + } void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) { if (!length) { *** mysql-test/t/row.test Tue Mar 27 18:16:34 2007 --- ../mysql-5.0.bk-sub-aggr/mysql-test/t/row.test Tue Mar 27 20:45:42 2007 *************** *** 139,141 **** --- 139,184 ---- SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1); DROP TABLE t1,t2; + + # + # BUG #27484 + # row() in "IN()" + # + + #nested row() in "IN()" could cause a crash, + #if a row was compared to a different type or a row of differnet length + + # qsort / compare + -- error 1241 + select row(1,row(2,3)) IN (row(1,row(2,3)) ,row(1,1)); + -- error 1241 + select row(1,row(2,3)) IN (row(1,row(2,3)) ,row(1,1), row(1,row(2,3))); + -- error 1241 + select row(1,row(2,3)) IN (row(1,row(2,3)) ,row(1,row(2,2,2))); + -- error 1241 + select row(1,row(2,3,4)) IN (row(1,row(2,3,4)) ,row(1,row(2,2))); + + # the following were not affected, but should be tested + -- error 1241 + select row(1,row(2,3)) IN (row(1,1), row(1,row(2,3))); + -- error 1241 + select row(1,row(2,3)) IN (row(1,row(2,2,2)), row(1,row(2,3))); + -- error 1241 + select row(1,row(2,3,4)) IN (row(1,row(2,2)), row(1,row(2,3,4))); + + + #using subselect in nested row() and IN(), invalid statements were accepted with no error + --error 1241 + select row(1,row(2,3)) IN (row(1,row(2,3)), (select 1,1)); + --error 1241 + select row(1,row(2,3)) IN (row(1,row(2,3)), (select 1,1), row(1,row(2,4))); + --error 1241 + select row(1,row(2,3)) IN ((select 1,1), row(1,row(2,3))); + + #debug asserts happened with nested row() / run the below with a debug server + + -- error 1241 + select row(2,1) IN (row(21,2), row(row(1,1,3),0) ); + -- error 1241 + select row(2,1) IN (row(row(1,1,3),0), row(21,2)); + *** mysql-test/r/row.result Tue Mar 27 18:16:33 2007 --- ../mysql-5.0.bk-sub-aggr/mysql-test/r/row.result Tue Mar 27 20:46:54 2007 *************** *** 306,308 **** --- 306,332 ---- 1 1 1 2 1 1 2 1 2 1 DROP TABLE t1,t2; + select row(1,row(2,3)) IN (row(1,row(2,3)) ,row(1,1)); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3)) IN (row(1,row(2,3)) ,row(1,1), row(1,row(2,3))); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3)) IN (row(1,row(2,3)) ,row(1,row(2,2,2))); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3,4)) IN (row(1,row(2,3,4)) ,row(1,row(2,2))); + ERROR 21000: Operand should contain 3 column(s) + select row(1,row(2,3)) IN (row(1,1), row(1,row(2,3))); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3)) IN (row(1,row(2,2,2)), row(1,row(2,3))); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3,4)) IN (row(1,row(2,2)), row(1,row(2,3,4))); + ERROR 21000: Operand should contain 3 column(s) + select row(1,row(2,3)) IN (row(1,row(2,3)), (select 1,1)); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3)) IN (row(1,row(2,3)), (select 1,1), row(1,row(2,4))); + ERROR 21000: Operand should contain 2 column(s) + select row(1,row(2,3)) IN ((select 1,1), row(1,row(2,3))); + ERROR 21000: Operand should contain 2 column(s) + select row(2,1) IN (row(21,2), row(row(1,1,3),0) ); + ERROR 21000: Operand should contain 1 column(s) + select row(2,1) IN (row(row(1,1,3),0), row(21,2)); + ERROR 21000: Operand should contain 1 column(s)