From: Jorgen Loland Date: November 30 2012 6:44am Subject: bzr push into mysql-trunk branch (jorgen.loland:5127 to 5128) WL#5894 List-Archive: http://lists.mysql.com/commits/145413 Message-Id: <20121130064404.26880.67838.5128@atum21.no.oracle.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 5128 Jorgen Loland 2012-11-29 WL#5894: Unit-test SEL_ARG Batch #1, which contains: * Introduction of utility functions to be used when adding unit tests for SEL_ARG. * First batch of unit tests * Some cleanup of gunit mock classes as proposed by Didrik. modified: sql/opt_range.cc unittest/gunit/fake_table.h unittest/gunit/field-t.cc unittest/gunit/field_date-t.cc unittest/gunit/field_datetime-t.cc unittest/gunit/field_long-t.cc unittest/gunit/field_newdecimal-t.cc unittest/gunit/item-t.cc unittest/gunit/mock_field_timestamp.h unittest/gunit/opt_range-t.cc unittest/gunit/test_utils.h 5127 Venkatesh Duggirala 2012-11-30 [merge] BUG#15888454: SLAVE CRASHES WHEN DML REQUIRES CONVERSION & TABLE HAS LESS COLUMNS THAN MASTER Null merge from mysql-5.6 === modified file 'sql/opt_range.cc' --- a/sql/opt_range.cc 2012-11-23 11:17:38 +0000 +++ b/sql/opt_range.cc 2012-11-29 15:23:48 +0000 @@ -904,6 +904,11 @@ static inline void dbug_print_tree(const SEL_TREE *tree, const RANGE_OPT_PARAM *param); +static inline void print_tree(String *out, + const char *tree_name, + SEL_TREE *tree, + const RANGE_OPT_PARAM *param); + void append_range(String *out, const KEY_PART_INFO *key_parts, const uchar *min_key, const uchar *max_key, @@ -13621,8 +13626,6 @@ static void trace_range_all_keyparts(Opt #endif //OPTIMIZER_TRACE -#ifndef DBUG_OFF - /** Traverse an R-B tree of range conditions and append all ranges for this keypart and consecutive keyparts to a String. See description @@ -13701,8 +13704,6 @@ static void print_range_all_keyparts(Str } } -#endif // DBUG_OFF - /** Print the ranges in a SEL_TREE to debug log. @@ -13715,53 +13716,107 @@ static inline void dbug_print_tree(const const RANGE_OPT_PARAM *param) { #ifndef DBUG_OFF + print_tree(NULL, tree_name, tree, param); +#endif +} + +static inline void print_tree(String *out, + const char *tree_name, + SEL_TREE *tree, + const RANGE_OPT_PARAM *param) +{ if (!param->using_real_indexes) { - DBUG_PRINT("info", - ("sel_tree: " - "%s uses a partitioned index and cannot be printed", - tree_name)); + if (out) + { + out->append(tree_name); + out->append(" uses a partitioned index and cannot be printed"); + } + else + DBUG_PRINT("info", + ("sel_tree: " + "%s uses a partitioned index and cannot be printed", + tree_name)); return; } if (!tree) { - DBUG_PRINT("info", ("sel_tree: %s is NULL", tree_name)); + if (out) + { + out->append(tree_name); + out->append(" is NULL"); + } + else + DBUG_PRINT("info", ("sel_tree: %s is NULL", tree_name)); return; } if (tree->type == SEL_TREE::IMPOSSIBLE) { - DBUG_PRINT("info", ("sel_tree: %s is IMPOSSIBLE", tree_name)); + if (out) + { + out->append(tree_name); + out->append(" is IMPOSSIBLE"); + } + else + DBUG_PRINT("info", ("sel_tree: %s is IMPOSSIBLE", tree_name)); return; } if (tree->type == SEL_TREE::ALWAYS) { - DBUG_PRINT("info", ("sel_tree: %s is ALWAYS", tree_name)); + if (out) + { + out->append(tree_name); + out->append(" is ALWAYS"); + } + else + DBUG_PRINT("info", ("sel_tree: %s is ALWAYS", tree_name)); return; } if (tree->type == SEL_TREE::MAYBE) { - DBUG_PRINT("info", ("sel_tree: %s is MAYBE", tree_name)); + if (out) + { + out->append(tree_name); + out->append(" is MAYBE"); + } + else + DBUG_PRINT("info", ("sel_tree: %s is MAYBE", tree_name)); return; } if (!tree->merges.is_empty()) { - DBUG_PRINT("info", - ("sel_tree: " - "%s contains the following merges", tree_name)); + if (out) + { + out->append(tree_name); + out->append(" contains the following merges\n"); + } + else + DBUG_PRINT("info", + ("sel_tree: " + "%s contains the following merges", tree_name)); List_iterator it(tree->merges); - int i= 0; + int i= 1; for (SEL_IMERGE *el= it++; el; el= it++, i++) { + if (out) + { + out->append(tree_name); + out->append(" --- alternative "); + out->append(i); + out->append(" ---\n"); + } + else + DBUG_PRINT("info", ("sel_tree: --- alternative %d ---",i)); for (SEL_TREE** current= el->trees; current != el->trees_next; current++) - dbug_print_tree(" merge_tree", *current, param); + print_tree(out, " merge_tree", *current, param); } } @@ -13794,11 +13849,24 @@ static inline void dbug_print_tree(const print_range_all_keyparts(&range_result, &range_so_far, tree->keys[i], key_part); - DBUG_PRINT("info", - ("sel_tree: %s->keys[%d(real_keynr: %d)]: %s", - tree_name, i, real_key_nr, range_result.ptr())); + if (out) + { + char istr[22]; + if (i>0) + out->append("\n"); + + out->append(tree_name); + out->append(" keys["); + out->append(llstr(i, istr)); + out->append("]: "); + out->append(range_result.ptr()); + } + else + DBUG_PRINT("info", + ("sel_tree: %p, type=%d, %s->keys[%u(%u)]: %s", + tree->keys[i], tree->keys[i]->type, tree_name, i, + real_key_nr, range_result.ptr())); } -#endif } /***************************************************************************** === modified file 'unittest/gunit/fake_table.h' --- a/unittest/gunit/fake_table.h 2012-09-07 14:07:26 +0000 +++ b/unittest/gunit/fake_table.h 2012-11-29 15:23:48 +0000 @@ -23,6 +23,8 @@ */ class Fake_TABLE_SHARE : public TABLE_SHARE { + uint32 all_set_buf; + public: Fake_TABLE_SHARE(uint number_of_columns) { @@ -34,6 +36,9 @@ public: db_low_byte_first= true; path.str= const_cast(fakepath); path.length= strlen(path.str); + + EXPECT_EQ(0, bitmap_init(&all_set, &all_set_buf, fields, false)); + bitmap_set_above(&all_set, 0, 1); } ~Fake_TABLE_SHARE() {} }; @@ -44,10 +49,18 @@ public: */ class Fake_TABLE: public TABLE { + // make room for 8 indexes (mysql permits 64) + static const int max_keys= 8; + KEY m_keys[max_keys]; + // make room for up to 8 keyparts per index + KEY_PART_INFO m_key_part_infos[max_keys][8]; + Fake_TABLE_SHARE table_share; MY_BITMAP write_set_struct; uint32 write_set_buf; - Field *field_array[32]; + MY_BITMAP read_set_struct; + uint32 read_set_buf; + Field *m_field_array[32]; void inititalize() { @@ -55,11 +68,12 @@ class Fake_TABLE: public TABLE file= NULL; in_use= current_thd; null_row= '\0'; + read_set= &read_set_struct; write_set= &write_set_struct; - read_set= NULL; next_number_field= NULL; // No autoinc column EXPECT_EQ(0, bitmap_init(write_set, &write_set_buf, s->fields, false)); + EXPECT_EQ(0, bitmap_init(read_set, &read_set_buf, s->fields, false)); static const char *table_name= "Fake"; for (uint i= 0; i < s->fields; ++i) @@ -70,19 +84,36 @@ class Fake_TABLE: public TABLE } const_table= true; maybe_null= 0; + this->TABLE::map= 1; /* ID bit of table */ + this->TABLE::key_info= &m_keys[0]; + for (int i= 0; i < max_keys; i++) + key_info[i].key_part= m_key_part_infos[i]; } public: + Fake_TABLE(List fields) + : table_share(fields.elements) + { + field= m_field_array; + + List_iterator it(fields); + int nbr_fields= 0; + for (Field *cur_field= it++; cur_field; cur_field= it++) + field[nbr_fields++]= cur_field; + + inititalize(); + } + Fake_TABLE(Field *column1) : table_share(1) { - field= field_array; + field= m_field_array; field[0]= column1; inititalize(); } Fake_TABLE(Field *column1, Field *column2) : table_share(2) { - field= field_array; + field= m_field_array; field[0]= column1; field[1]= column2; inititalize(); === modified file 'unittest/gunit/field-t.cc' --- a/unittest/gunit/field-t.cc 2012-10-30 14:40:02 +0000 +++ b/unittest/gunit/field-t.cc 2012-11-29 15:23:48 +0000 @@ -390,6 +390,7 @@ TEST_F(FieldTest, CopyFieldSet) Field_set *f_from= create_field_set(&tl4); bitmap_set_all(f_from->table->write_set); + bitmap_set_all(f_from->table->read_set); uchar from_fieldval= static_cast(typeset); f_from->ptr= &from_fieldval; === modified file 'unittest/gunit/field_date-t.cc' --- a/unittest/gunit/field_date-t.cc 2012-05-07 12:05:48 +0000 +++ b/unittest/gunit/field_date-t.cc 2012-11-29 15:23:48 +0000 @@ -90,6 +90,7 @@ public: } void make_writable() { bitmap_set_bit(table->write_set, field_index); } + void make_readable() { bitmap_set_bit(table->read_set, field_index); } }; @@ -99,6 +100,7 @@ TEST_F(FieldDateTest, StoreLegalStringVa Fake_TABLE table(&field_date); table.in_use= thd(); field_date.make_writable(); + field_date.make_readable(); { SCOPED_TRACE(""); @@ -123,6 +125,7 @@ TEST_F(FieldDateTest, StoreIllegalString Fake_TABLE table(&field_date); table.in_use= thd(); field_date.make_writable(); + field_date.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // Truncates time @@ -178,6 +181,7 @@ TEST_F(FieldDateTest, StoreZeroDateSqlMo Fake_TABLE table(&field_date); table.in_use= thd(); field_date.make_writable(); + field_date.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; for (int i= 0; i < no_modes; i++) @@ -225,6 +229,7 @@ TEST_F(FieldDateTest, StoreZeroDateSqlMo Fake_TABLE table(&field_date); table.in_use= thd(); field_date.make_writable(); + field_date.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // With "MODE_NO_ZERO_DATE" set - Errors if date is all null @@ -286,6 +291,7 @@ TEST_F(FieldDateTest, StoreZeroDateSqlMo Fake_TABLE table(&field_date); table.in_use= thd(); field_date.make_writable(); + field_date.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // With "MODE_NO_ZERO_IN_DATE" set - Entire date zero is ok === modified file 'unittest/gunit/field_datetime-t.cc' --- a/unittest/gunit/field_datetime-t.cc 2012-05-07 12:05:48 +0000 +++ b/unittest/gunit/field_datetime-t.cc 2012-11-29 15:23:48 +0000 @@ -69,6 +69,7 @@ public: } void make_writable() { bitmap_set_bit(table->write_set, field_index); } + void make_readable() { bitmap_set_bit(table->read_set, field_index); } }; @@ -82,6 +83,7 @@ TEST_F(FieldDatetimeTest, StoreLegalStri Fake_TABLE table(&field_dt); table.in_use= thd(); field_dt.make_writable(); + field_dt.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; { @@ -108,6 +110,7 @@ TEST_F(FieldDatetimeTest, StoreIllegalSt Fake_TABLE table(&field_dt); table.in_use= thd(); field_dt.make_writable(); + field_dt.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // Bad year @@ -191,6 +194,7 @@ TEST_F(FieldDatetimeTest, StoreZeroDateS Fake_TABLE table(&field_dt); table.in_use= thd(); field_dt.make_writable(); + field_dt.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; for (int i= 0; i < no_modes; i++) @@ -252,6 +256,7 @@ TEST_F(FieldDatetimeTest, StoreZeroDateS Fake_TABLE table(&field_dt); table.in_use= thd(); field_dt.make_writable(); + field_dt.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // With "MODE_NO_ZERO_DATE" set - Errors if date is all null @@ -313,6 +318,7 @@ TEST_F(FieldDatetimeTest, StoreZeroDateS Fake_TABLE table(&field_dt); table.in_use= thd(); field_dt.make_writable(); + field_dt.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // With "MODE_NO_ZERO_IN_DATE" set - Entire date zero is ok === modified file 'unittest/gunit/field_long-t.cc' --- a/unittest/gunit/field_long-t.cc 2012-06-24 07:07:58 +0000 +++ b/unittest/gunit/field_long-t.cc 2012-11-29 15:23:48 +0000 @@ -66,6 +66,7 @@ public: } void make_writable() { bitmap_set_bit(table->write_set, field_index); } + void make_readable() { bitmap_set_bit(table->read_set, field_index); } }; @@ -105,6 +106,7 @@ TEST_F(FieldLongTest, StoreLegalIntValue Fake_TABLE table(&field_long); table.in_use= thd(); field_long.make_writable(); + field_long.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; SCOPED_TRACE(""); test_store_long(&field_long, 0, 0, 0, TYPE_OK); @@ -145,6 +147,7 @@ TEST_F(FieldLongTest, StoreOutOfRangeInt Fake_TABLE table(&field_long); table.in_use= thd(); field_long.make_writable(); + field_long.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; @@ -188,6 +191,7 @@ TEST_F(FieldLongTest, StoreLegalStringVa Fake_TABLE table(&field_long); table.in_use= thd(); field_long.make_writable(); + field_long.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; const char min_int[]= "-2147483648"; @@ -242,6 +246,7 @@ TEST_F(FieldLongTest, StoreIllegalString Fake_TABLE table(&field_long); table.in_use= thd(); field_long.make_writable(); + field_long.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; const char max_int_plus1[]= "2147483648"; @@ -330,6 +335,7 @@ TEST_F(FieldLongTest, StoreNullValue) Fake_TABLE table(&field_long); table.in_use= thd(); field_long.make_writable(); + field_long.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; type_conversion_status err; === modified file 'unittest/gunit/field_newdecimal-t.cc' --- a/unittest/gunit/field_newdecimal-t.cc 2012-05-07 12:05:48 +0000 +++ b/unittest/gunit/field_newdecimal-t.cc 2012-11-29 15:23:48 +0000 @@ -72,6 +72,7 @@ public: } void make_writable() { bitmap_set_bit(table->write_set, field_index); } + void make_readable() { bitmap_set_bit(table->read_set, field_index); } void test_store_string(const char *store_value, const int length, const char *expected_string_result, @@ -87,6 +88,7 @@ public: Mock_error_handler error_handler(table->in_use, expected_error_no); type_conversion_status err= store(store_value, length, &my_charset_latin1); val_str(&str, &unused); + EXPECT_STREQ(expected_string_result, str.ptr()); EXPECT_EQ(expected_int_result, val_int()); EXPECT_EQ(expected_real_result, val_real()); @@ -106,6 +108,7 @@ TEST_F(FieldNewDecimalTest, StoreLegalSt Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; { @@ -128,6 +131,7 @@ TEST_F(FieldNewDecimalTest, StoreIllegal Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; // Truncated (precision beyond 3 decimals is lost) @@ -206,6 +210,7 @@ TEST_F(FieldNewDecimalTest, storeInterna Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; my_decimal d10_01; @@ -261,6 +266,7 @@ TEST_F(FieldNewDecimalTest, storeInterna Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; my_decimal dTooHigh; @@ -300,6 +306,7 @@ TEST_F(FieldNewDecimalTest, storeInterna Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; my_decimal d10_01; @@ -370,6 +377,7 @@ TEST_F(FieldNewDecimalTest, storeInterna Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; my_decimal d10_01; @@ -438,6 +446,7 @@ TEST_F(FieldNewDecimalTest, storeInterna Fake_TABLE table(&field_dec); table.in_use= thd(); field_dec.make_writable(); + field_dec.make_readable(); thd()->count_cuted_fields= CHECK_FIELD_WARN; my_decimal d10_01; === modified file 'unittest/gunit/item-t.cc' --- a/unittest/gunit/item-t.cc 2012-11-23 09:02:13 +0000 +++ b/unittest/gunit/item-t.cc 2012-11-29 15:23:48 +0000 @@ -134,6 +134,7 @@ TEST_F(ItemTest, ItemEqual) { // Bug#13720201 VALGRIND: VARIOUS BLOCKS OF BYTES DEFINITELY LOST Mock_field_timestamp mft; + mft.make_readable(); // foo is longer than STRING_BUFFER_USUAL_SIZE used by cmp_item_sort_string. const char foo[]= "0123456789012345678901234567890123456789" @@ -142,6 +143,7 @@ TEST_F(ItemTest, ItemEqual) Item_equal *item_equal= new Item_equal(new Item_string(STRING_WITH_LEN(foo), &my_charset_bin), new Item_field(&mft)); + EXPECT_FALSE(item_equal->fix_fields(thd(), NULL)); EXPECT_EQ(0, item_equal->val_int()); } === modified file 'unittest/gunit/mock_field_timestamp.h' --- a/unittest/gunit/mock_field_timestamp.h 2012-01-31 15:16:16 +0000 +++ b/unittest/gunit/mock_field_timestamp.h 2012-11-29 15:23:48 +0000 @@ -73,6 +73,7 @@ public: /* Averts ASSERT_COLUMN_MARKED_FOR_WRITE assertion. */ void make_writable() { bitmap_set_bit(table->write_set, field_index); } + void make_readable() { bitmap_set_bit(table->read_set, field_index); } void store_timestamp(const timeval *tm) { === modified file 'unittest/gunit/opt_range-t.cc' --- a/unittest/gunit/opt_range-t.cc 2012-05-07 12:05:48 +0000 +++ b/unittest/gunit/opt_range-t.cc 2012-11-29 15:23:48 +0000 @@ -17,6 +17,10 @@ #include "my_config.h" #include +#include + +#include "handler-t.h" +#include "fake_table.h" #include "test_utils.h" #include "opt_range.cc" @@ -24,35 +28,191 @@ namespace { using my_testing::Server_initializer; +using my_testing::delete_container_pointers; +using ::testing::Return; +using ::testing::NiceMock; +using ::testing::_; + +class Fake_RANGE_OPT_PARAM : public RANGE_OPT_PARAM +{ + KEY_PART m_key_parts[64]; + Mem_root_array m_kpis; + +public: + + Fake_RANGE_OPT_PARAM(THD *thd_arg, MEM_ROOT *alloc_arg, TABLE *table_arg) + : m_kpis(alloc_arg) + { + m_kpis.reserve(64); + + thd= thd_arg; + mem_root= alloc_arg; + current_table= 1<<0; + table= table_arg; + + alloced_sel_args= 0; + using_real_indexes= true; + key_parts= m_key_parts; + key_parts_end= m_key_parts; + keys= 0; + } + + void add_key(List fields_in_index) + { + List_iterator it(fields_in_index); + int cur_kp= 0; + + table->key_info[keys].actual_key_parts= 0; + for (Field *cur_field= it++; cur_field; cur_field= it++, cur_kp++) + { + KEY_PART_INFO *kpi= m_kpis.end(); // Points past the end. + m_kpis.push_back(KEY_PART_INFO()); // kpi now points to a new element + kpi->init_from_field(cur_field); + + key_parts_end->key= keys; + key_parts_end->part= cur_kp; + key_parts_end->length= kpi->store_length; + key_parts_end->store_length= kpi->store_length; + key_parts_end->field= kpi->field; + key_parts_end->null_bit= kpi->null_bit; + key_parts_end->image_type = Field::itRAW; + + key_parts_end++; + table->key_info[keys].key_part[cur_kp]= *kpi; + table->key_info[keys].actual_key_parts++; + } + table->key_info[keys].user_defined_key_parts= + table->key_info[keys].actual_key_parts; + real_keynr[keys]= keys; + keys++; + } + + ~Fake_RANGE_OPT_PARAM() + { + for (uint i= 0; i < keys; i++) + { + table->key_info[i].actual_key_parts= 0; + table->key_info[i].user_defined_key_parts= 0; + } + } + +}; + +class Mock_field_long : public Field_long +{ +private: + Fake_TABLE *m_fake_tbl; + +public: + Mock_field_long(THD *thd, Item *item, const char *name, bool create_table) + : Field_long(0, // ptr_arg + 8, // len_arg + NULL, // null_ptr_arg + 0, // null_bit_arg + Field::NONE, // unireg_check_arg + name ? name : "field_name", // field_name_arg + false, // zero_arg + false) // unsigned_arg + { + if (create_table) + m_fake_tbl= new Fake_TABLE(this); + else + m_fake_tbl= NULL; + + this->ptr= (uchar*) alloc_root((thd->mem_root), KEY_LENGTH); + if (item) + item->save_in_field_no_warnings(this, true); + } + + ~Mock_field_long() + { + delete m_fake_tbl; + m_fake_tbl= NULL; + } + + // #bytes to store the value - see Field_long::key_lenght() + static const int KEY_LENGTH= 4; +}; + class SelArgTest : public ::testing::Test { protected: - SelArgTest() + SelArgTest() : m_ftable(NULL), m_opt_param(NULL), m_mock_handler(NULL) { - memset(&m_opt_param, 0, sizeof(m_opt_param)); } virtual void SetUp() { initializer.SetUp(); - m_opt_param.thd= thd(); - m_opt_param.mem_root= &m_alloc; - m_opt_param.current_table= 1<<0; init_sql_alloc(&m_alloc, thd()->variables.range_alloc_block_size, 0); } virtual void TearDown() { + delete_container_pointers(m_table_fields); + delete m_mock_handler; + delete m_opt_param; + delete m_ftable; + initializer.TearDown(); free_root(&m_alloc, MYF(0)); } THD *thd() { return initializer.thd(); } + void create_table(List field_names_arg) + { + List_iterator fld_name_it(field_names_arg); + for (char **cur_name= fld_name_it++; *cur_name; cur_name= fld_name_it++) + m_field_list.push_back(new Mock_field_long(thd(), NULL, + *cur_name, false)); + + create_table_common(); + } + + void create_table(const char *field_name) + { + m_field_list.push_back(new Mock_field_long(thd(), NULL, + field_name, false)); + create_table_common(); + } + + void create_table(const char *field_name1, const char *field_name2) + { + m_field_list.push_back(new Mock_field_long(thd(), NULL, + field_name1, false)); + m_field_list.push_back(new Mock_field_long(thd(), NULL, + field_name2, false)); + create_table_common(); + } + Server_initializer initializer; MEM_ROOT m_alloc; - RANGE_OPT_PARAM m_opt_param; + + List m_field_list; + Fake_TABLE *m_ftable; + Fake_RANGE_OPT_PARAM *m_opt_param; + Mock_HANDLER *m_mock_handler; + std::vector m_table_fields; + +private: + void create_table_common() + { + m_ftable= new Fake_TABLE(m_field_list); + m_opt_param= new Fake_RANGE_OPT_PARAM(thd(), &m_alloc, m_ftable); + handlerton *hton= NULL; + m_mock_handler= + new NiceMock(hton, m_ftable->get_share()); + m_ftable->set_handler(m_mock_handler); + + List_iterator it(m_field_list); + for (Field *cur_field= it++; cur_field; cur_field= it++) + m_table_fields.push_back(static_cast(cur_field)); + + ON_CALL(*m_mock_handler, index_flags(_, _, true)) + .WillByDefault(Return(HA_READ_RANGE)); + } }; /* @@ -91,64 +251,6 @@ const SEL_TREE *null_tree= NULL; const SEL_ARG *null_arg= NULL; -class Mock_field_long : public Field_long -{ -public: - Mock_field_long(THD *thd, Item *item) - : Field_long(0, // ptr_arg - 8, // len_arg - NULL, // null_ptr_arg - 0, // null_bit_arg - Field::NONE, // unireg_check_arg - "field_name", // field_name_arg - false, // zero_arg - false) // unsigned_arg - { - m_table_name= "mock_table"; - memset(&m_share, 0, sizeof(m_share)); - const char *foo= "mock_db"; - m_share.db.str= const_cast(foo); - m_share.db.length= strlen(m_share.db.str); - - bitmap_init(&share_allset, 0, sizeof(my_bitmap_map), 0); - bitmap_set_above(&share_allset, 0, 1); //all bits 1 - m_share.all_set= share_allset; - - memset(&m_table, 0, sizeof(m_table)); - m_table.s= &m_share; - - bitmap_init(&tbl_readset, 0, sizeof(my_bitmap_map), 0); - m_table.read_set= &tbl_readset; - - bitmap_init(&tbl_writeset, 0, sizeof(my_bitmap_map), 0); - m_table.write_set= &tbl_writeset; - - m_table.map= 1<<0; - m_table.in_use= thd; - this->table_name= &m_table_name; - this->table= &m_table; - this->ptr= (uchar*) alloc_root((thd->mem_root), KEY_LENGTH); - if (item) - item->save_in_field_no_warnings(this, true); - } - ~Mock_field_long() - { - bitmap_free(&share_allset); - bitmap_free(&tbl_readset); - bitmap_free(&tbl_writeset); - } - - // #bytes to store the value - see Field_long::key_lenght() - static const int KEY_LENGTH= 4; - const char *m_table_name; - TABLE_SHARE m_share; - TABLE m_table; - MY_BITMAP share_allset; - MY_BITMAP tbl_readset; - MY_BITMAP tbl_writeset; -}; - - static void print_selarg_ranges(String *s, SEL_ARG *sel_arg, const KEY_PART_INFO *kpi) { @@ -170,30 +272,216 @@ static void print_selarg_ranges(String * TEST_F(SelArgTest, SimpleCond) { - EXPECT_NE(null_tree, get_mm_tree(&m_opt_param, new Item_int(42))); + Fake_RANGE_OPT_PARAM opt_param(thd(), &m_alloc, NULL); + EXPECT_NE(null_tree, get_mm_tree(&opt_param, new Item_int(42))); } /* - TODO: Here we try to build a range, but a lot of mocking remains - before it works as intended. Currently get_mm_tree() returns NULL - because m_opt_param.key_parts and m_opt_param.key_parts_end have not - been setup. + Exercise range optimizer without adding indexes */ -TEST_F(SelArgTest, EqualCond) +TEST_F(SelArgTest, EqualCondNoIndexes) { - Mock_field_long field_long(thd(), NULL); - m_opt_param.table= &field_long.m_table; - SEL_TREE *tree= get_mm_tree(&m_opt_param, + Mock_field_long field_long(thd(), NULL, NULL, true); + Fake_RANGE_OPT_PARAM opt_param(thd(), &m_alloc, field_long.table); + SEL_TREE *tree= get_mm_tree(&opt_param, new Item_equal(new Item_int(42), new Item_field(&field_long))); EXPECT_EQ(null_tree, tree); } +/* + Exercise range optimizer with single column index +*/ +TEST_F(SelArgTest, GetMMTreeSingleColIndex) +{ + create_table(NULL); + + Mock_field_long *field_long= m_table_fields[0]; + + List index_list; + index_list.push_back(field_long); + m_opt_param->add_key(index_list); + + char buff[512]; + String range_string(buff, sizeof(buff), system_charset_info); + range_string.set_charset(system_charset_info); + + // Expected result of next test: + const char expected[]= "result keys[0]: (42 <= field_name <= 42)"; + SEL_TREE *tree= get_mm_tree(m_opt_param, + new Item_equal(new Item_int(42), + new Item_field(field_long))); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected, range_string.c_ptr()); + + + // Expected result of next test: + const char expected2[]= + "result keys[0]: (42 <= field_name <= 42) OR (43 <= field_name <= 43)"; + tree= get_mm_tree(m_opt_param, + new Item_cond_or(new Item_equal(new Item_int(42), + new Item_field(field_long)), + new Item_equal(new Item_int(43), + new Item_field(field_long))) + ); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected2, range_string.c_ptr()); + + + // Expected result of next test: + const char expected3[]= + "result keys[0]: " + "(1 <= field_name <= 1) OR (2 <= field_name <= 2) OR " + "(3 <= field_name <= 3) OR (4 <= field_name <= 4) OR " + "(5 <= field_name <= 5) OR (6 <= field_name <= 6) OR " + "(7 <= field_name <= 7) OR (8 <= field_name <= 8)"; + List or_list1; + or_list1.push_back(new Item_equal(new Item_int(1), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(2), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(3), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(4), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(5), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(6), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(7), + new Item_field(field_long))); + or_list1.push_back(new Item_equal(new Item_int(8), + new Item_field(field_long))); + tree= get_mm_tree(m_opt_param, new Item_cond_or(or_list1)); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected3, range_string.c_ptr()); + + + // Expected result of next test: + const char expected4[]= "result keys[0]: (7 <= field_name <= 7)"; + Item_equal *eq7= new Item_equal(new Item_int(7), + new Item_field(field_long)); + tree= get_mm_tree(m_opt_param, + new Item_cond_and(new Item_cond_or(or_list1), eq7)); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected4, range_string.c_ptr()); + + + // Expected result of next test: + const char expected5[]= + "result keys[0]: " + "(1 <= field_name <= 1) OR (3 <= field_name <= 3) OR " + "(5 <= field_name <= 5) OR (7 <= field_name <= 7)"; + List or_list2; + or_list2.push_back(new Item_equal(new Item_int(1), + new Item_field(field_long))); + or_list2.push_back(new Item_equal(new Item_int(3), + new Item_field(field_long))); + or_list2.push_back(new Item_equal(new Item_int(5), + new Item_field(field_long))); + or_list2.push_back(new Item_equal(new Item_int(7), + new Item_field(field_long))); + or_list2.push_back(new Item_equal(new Item_int(9), + new Item_field(field_long))); + tree= get_mm_tree(m_opt_param, + new Item_cond_and(new Item_cond_or(or_list1), + new Item_cond_or(or_list2))); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected5, range_string.c_ptr()); +} + + +/* + Exercise range optimizer with multiple column index +*/ +TEST_F(SelArgTest, GetMMTreeMultipleSingleColIndex) +{ + create_table(NULL); + + Mock_field_long *field_long= m_table_fields[0]; + + List index_list; + index_list.push_back(field_long); + m_opt_param->add_key(index_list); + m_opt_param->add_key(index_list); + + char buff[512]; + String range_string(buff, sizeof(buff), system_charset_info); + range_string.set_charset(system_charset_info); + + // Expected result of next test: + const char expected[]= + "result keys[0]: (42 <= field_name <= 42)\n" + "result keys[1]: (42 <= field_name <= 42)"; + SEL_TREE *tree= get_mm_tree(m_opt_param, + new Item_equal(new Item_int(42), + new Item_field(field_long))); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected, range_string.c_ptr()); +} + + +/* + Exercise range optimizer with multiple single column indexes +*/ +TEST_F(SelArgTest, GetMMTreeSingleMultiColIndex) +{ + create_table("field_1", "field_2"); + + Mock_field_long *field_long1= m_table_fields[0]; + Mock_field_long *field_long2= m_table_fields[1]; + + List index_list; + index_list.push_back(field_long1); + index_list.push_back(field_long2); + m_opt_param->add_key(index_list); + + char buff[512]; + String range_string(buff, sizeof(buff), system_charset_info); + range_string.set_charset(system_charset_info); + + // Expected result of next test: + const char expected[]= "result keys[0]: (42 <= field_1 <= 42)"; + SEL_TREE *tree= get_mm_tree(m_opt_param, + new Item_equal(new Item_int(42), + new Item_field(field_long1))); + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected, range_string.c_ptr()); + + + // Expected result of next test: + const char expected3[]= "result keys[0]: " + "(42 <= field_1 <= 42 AND 10 <= field_2 <= 10)"; + + tree= get_mm_tree(m_opt_param, + new + Item_cond_and(new Item_equal(new Item_int(42), + new Item_field(field_long1)), + new Item_equal(new Item_int(10), + new Item_field(field_long2))) + ); + + range_string.length(0); + print_tree(&range_string, "result", tree, m_opt_param); + EXPECT_STREQ(expected3, range_string.c_ptr()); +} + + +/* + Create SelArg with various single valued predicate +*/ TEST_F(SelArgTest, SelArgOnevalue) { - Mock_field_long field_long7(thd(), new Item_int(7)); + Mock_field_long field_long7(thd(), new Item_int(7), NULL, true); KEY_PART_INFO kpi; kpi.init_from_field(&field_long7); @@ -234,10 +522,13 @@ TEST_F(SelArgTest, SelArgOnevalue) } +/* + Create SelArg with a between predicate +*/ TEST_F(SelArgTest, SelArgBetween) { - Mock_field_long field_long3(thd(), new Item_int(3)); - Mock_field_long field_long5(thd(), new Item_int(5)); + Mock_field_long field_long3(thd(), new Item_int(3), NULL, true); + Mock_field_long field_long5(thd(), new Item_int(5), NULL, true); KEY_PART_INFO kpi; kpi.init_from_field(&field_long3); @@ -288,10 +579,13 @@ TEST_F(SelArgTest, SelArgBetween) EXPECT_STREQ(expected6, range_string.c_ptr()); } +/* + Test SelArg::CopyMax +*/ TEST_F(SelArgTest, CopyMax) { - Mock_field_long field_long3(thd(), new Item_int(3)); - Mock_field_long field_long5(thd(), new Item_int(5)); + Mock_field_long field_long3(thd(), new Item_int(3), NULL, true); + Mock_field_long field_long5(thd(), new Item_int(5), NULL, true); KEY_PART_INFO kpi; kpi.init_from_field(&field_long3); @@ -358,10 +652,13 @@ TEST_F(SelArgTest, CopyMax) EXPECT_STREQ(expected5, range_string.c_ptr()); } +/* + Test SelArg::CopyMin +*/ TEST_F(SelArgTest, CopyMin) { - Mock_field_long field_long3(thd(), new Item_int(3)); - Mock_field_long field_long5(thd(), new Item_int(5)); + Mock_field_long field_long3(thd(), new Item_int(3), NULL, true); + Mock_field_long field_long5(thd(), new Item_int(5), NULL, true); KEY_PART_INFO kpi; kpi.init_from_field(&field_long3); @@ -429,10 +726,13 @@ TEST_F(SelArgTest, CopyMin) } +/* + Test SelArg::KeyOr +*/ TEST_F(SelArgTest, KeyOr1) { - Mock_field_long field_long3(thd(), new Item_int(3)); - Mock_field_long field_long4(thd(), new Item_int(4)); + Mock_field_long field_long3(thd(), new Item_int(3), NULL, true); + Mock_field_long field_long4(thd(), new Item_int(4), NULL, true); KEY_PART_INFO kpi; kpi.init_from_field(&field_long3); === modified file 'unittest/gunit/test_utils.h' --- a/unittest/gunit/test_utils.h 2012-10-25 13:34:04 +0000 +++ b/unittest/gunit/test_utils.h 2012-11-29 15:23:48 +0000 @@ -23,6 +23,18 @@ namespace my_testing { +template +void delete_container_pointers(Container_type &container) +{ + typename Container_type::iterator it1= container.begin(); + typename Container_type::iterator it2= container.end(); + for (; it1 != it2; ++it1) + { + delete (*it1); + } + container.clear(); +} + void setup_server_for_unit_tests(); void teardown_server_for_unit_tests(); No bundle (reason: useless for push emails).