From df32342102cd21425b2ead121af6336378f83a5f Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 30 Oct 2010 15:14:36 -0700 Subject: [PATCH] Fixed LP bug #668290. Prohibited to use hash join algorithm BNLH if join attributes need non-binary collations. It has to be done because BNLH does not support join for such attributes yet. Later this limitations will be lifted. Changed default collations for the schemes of some test cases to preserve the old execution plans. --- mysql-test/include/world_schema.inc | 6 +-- mysql-test/include/world_schema1.inc | 6 +-- mysql-test/r/join_cache.result | 67 +++++++++++++++++++++++----- mysql-test/t/join_cache.test | 51 +++++++++++++++++++-- sql/field.h | 11 +++++ sql/sql_select.cc | 37 ++++++++++++++- sql/sql_select.h | 1 + 7 files changed, 158 insertions(+), 21 deletions(-) diff --git a/mysql-test/include/world_schema.inc b/mysql-test/include/world_schema.inc index c683faf0114..bd7b768e83b 100644 --- a/mysql-test/include/world_schema.inc +++ b/mysql-test/include/world_schema.inc @@ -6,7 +6,7 @@ CREATE TABLE Country ( Capital int(11) default NULL, PRIMARY KEY (Code), UNIQUE INDEX (Name) -); +) COLLATE latin1_bin; CREATE TABLE City ( ID int(11) NOT NULL auto_increment, Name char(35) NOT NULL default '', @@ -15,11 +15,11 @@ CREATE TABLE City ( PRIMARY KEY (ID), INDEX (Population), INDEX (Country) -); +) COLLATE latin1_bin; CREATE TABLE CountryLanguage ( Country char(3) NOT NULL default '', Language char(30) NOT NULL default '', Percentage float(3,1) NOT NULL default '0.0', PRIMARY KEY (Country, Language), INDEX (Percentage) -); +) COLLATE latin1_bin; diff --git a/mysql-test/include/world_schema1.inc b/mysql-test/include/world_schema1.inc index 172e79a7c8c..48448b5c1d8 100644 --- a/mysql-test/include/world_schema1.inc +++ b/mysql-test/include/world_schema1.inc @@ -4,15 +4,15 @@ CREATE TABLE Country ( SurfaceArea float(10,2) NOT NULL default '0.00', Population int(11) NOT NULL default '0', Capital int(11) default NULL -); +) COLLATE latin1_bin; CREATE TABLE City ( ID int(11) NOT NULL, Name char(35) NOT NULL default '', Country char(3) NOT NULL default '', Population int(11) NOT NULL default '0' -); +) COLLATE latin1_bin; CREATE TABLE CountryLanguage ( Country char(3) NOT NULL default '', Language char(30) NOT NULL default '', Percentage float(3,1) NOT NULL default '0.0' -); +) COLLATE latin1_bin; diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result index e0543fd085b..3c352386b8a 100644 --- a/mysql-test/r/join_cache.result +++ b/mysql-test/r/join_cache.result @@ -12,18 +12,18 @@ Name char(52) NOT NULL default '', SurfaceArea float(10,2) NOT NULL default '0.00', Population int(11) NOT NULL default '0', Capital int(11) default NULL -); +) COLLATE latin1_bin; CREATE TABLE City ( ID int(11) NOT NULL, Name char(35) NOT NULL default '', Country char(3) NOT NULL default '', Population int(11) NOT NULL default '0' -); +) COLLATE latin1_bin; CREATE TABLE CountryLanguage ( Country char(3) NOT NULL default '', Language char(30) NOT NULL default '', Percentage float(3,1) NOT NULL default '0.0' -); +) COLLATE latin1_bin; SELECT COUNT(*) FROM Country; COUNT(*) 239 @@ -810,7 +810,7 @@ Population int(11) NOT NULL default '0', Capital int(11) default NULL, PRIMARY KEY (Code), UNIQUE INDEX (Name) -); +) COLLATE latin1_bin; CREATE TABLE City ( ID int(11) NOT NULL auto_increment, Name char(35) NOT NULL default '', @@ -819,14 +819,14 @@ Population int(11) NOT NULL default '0', PRIMARY KEY (ID), INDEX (Population), INDEX (Country) -); +) COLLATE latin1_bin; CREATE TABLE CountryLanguage ( Country char(3) NOT NULL default '', Language char(30) NOT NULL default '', Percentage float(3,1) NOT NULL default '0.0', PRIMARY KEY (Country, Language), INDEX (Percentage) -); +) COLLATE latin1_bin; show variables like 'join_buffer_size'; Variable_name Value join_buffer_size 131072 @@ -5469,7 +5469,7 @@ SET SESSION join_buffer_size=DEFAULT; CREATE TABLE t1 ( pk int NOT NULL, i int NOT NULL, v varchar(1) NOT NULL, PRIMARY KEY (pk), INDEX idx1(i), INDEX idx2 (v,i) -); +) COLLATE latin1_bin; INSERT INTO t1 VALUES (10,8,'v'), (11,8,'f'), (12,5,'v'), (13,8,'s'), (14,8,'a'), (15,6,'p'), (16,7,'z'), (17,2,'a'), (18,5,'h'), (19,7,'h'), @@ -5477,16 +5477,16 @@ INSERT INTO t1 VALUES CREATE TABLE t2 ( pk int NOT NULL, i int NOT NULL, v varchar(1) NOT NULL, PRIMARY KEY (pk), INDEX idx1(i), INDEX idx2(v,i) -); +) COLLATE latin1_bin; INSERT INTO t2 VALUES (10,8,'v'), (11,8,'f'), (12,5,'v'), (13,8,'s'), (14,8,'a'), (15,6,'p'), (16,7,'z'), (17,2,'a'), (18,5,'h'), (19,7,'h'), (20,2,'v'), (21,9,'v'), (22,142,'b'), (23,3,'y'), (24,0,'v'), (25,3,'m'), (26,5,'z'), (27,9,'n'), (28,1,'d'), (29,107,'a'); CREATE TABLE t3 ( -pk int NOT NULL, i int(11) NOT NULL, v varchar(1) NOT NULL, +pk int NOT NULL, i int NOT NULL, v varchar(1) NOT NULL, PRIMARY KEY (pk), INDEX idx1(i), INDEX idx2(v,i) -); +) COLLATE latin1_bin; INSERT INTO t3 VALUES (1,9,'x'), (2,5,'g'), (3,1,'o'), (4,0,'g'), (5,1,'v'), (6,190,'m'), (7,6,'x'), (8,3,'c'), (9,4,'z'), (10,3,'i'), @@ -5542,4 +5542,51 @@ v p DROP TABLE t1,t2,t3; SET SESSION join_cache_level=DEFAULT; +# +# Bug #668290: hash join with non-binary collations +# +CREATE TABLE t1 ( +i int DEFAULT NULL, +cl varchar(10) CHARACTER SET latin1 DEFAULT NULL, +cu varchar(10) CHARACTER SET utf8 DEFAULT NULL, +INDEX cl (cl), +INDEX cu (cu) +); +INSERT INTO t1 VALUES +(650903552,'cmxffkpsel','z'), (535298048,'tvtjrcmxff','y'), +(1626865664,'when','for'), (39649280,'rcvljitvtj','ercvljitvt'), +(792068096,'ttercvljit','jttercvlji'); +INSERT INTO t1 SELECT * FROM t1; +CREATE TABLE t2 ( +cu varchar(10) CHARACTER SET utf8 DEFAULT NULL, +i int DEFAULT NULL, +cl varchar(10) CHARACTER SET latin1 DEFAULT NULL, +INDEX cu (cu), +INDEX cl (cl) +); +INSERT INTO t2 VALUES +('g',7,'like'), ('fujttercvl',6,'y'), +('s',2,'e'), ('didn\'t',0,'v'), + ('gvdrodpedk',8,'chogvdrodp'), ('jichogvdro',7,'will'); +EXPLAIN +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 6 +1 SIMPLE t1 ref cu cu 33 func 2 Using where; Using index +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; +i +6 +6 +SET SESSION join_cache_level = 4; +EXPLAIN +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 6 +1 SIMPLE t1 ref cu cu 33 func 2 Using where; Using index +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; +i +6 +6 +SET SESSION join_cache_level = DEFAULT; +DROP TABLE t1,t2; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test index 010abed3c2b..48a2cd1d4a8 100644 --- a/mysql-test/t/join_cache.test +++ b/mysql-test/t/join_cache.test @@ -2192,7 +2192,7 @@ SET SESSION join_buffer_size=DEFAULT; CREATE TABLE t1 ( pk int NOT NULL, i int NOT NULL, v varchar(1) NOT NULL, PRIMARY KEY (pk), INDEX idx1(i), INDEX idx2 (v,i) -); +) COLLATE latin1_bin; INSERT INTO t1 VALUES (10,8,'v'), (11,8,'f'), (12,5,'v'), (13,8,'s'), (14,8,'a'), (15,6,'p'), (16,7,'z'), (17,2,'a'), (18,5,'h'), (19,7,'h'), @@ -2201,7 +2201,7 @@ INSERT INTO t1 VALUES CREATE TABLE t2 ( pk int NOT NULL, i int NOT NULL, v varchar(1) NOT NULL, PRIMARY KEY (pk), INDEX idx1(i), INDEX idx2(v,i) -); +) COLLATE latin1_bin; INSERT INTO t2 VALUES (10,8,'v'), (11,8,'f'), (12,5,'v'), (13,8,'s'), (14,8,'a'), (15,6,'p'), (16,7,'z'), (17,2,'a'), (18,5,'h'), (19,7,'h'), @@ -2209,9 +2209,9 @@ INSERT INTO t2 VALUES (25,3,'m'), (26,5,'z'), (27,9,'n'), (28,1,'d'), (29,107,'a'); CREATE TABLE t3 ( - pk int NOT NULL, i int(11) NOT NULL, v varchar(1) NOT NULL, + pk int NOT NULL, i int NOT NULL, v varchar(1) NOT NULL, PRIMARY KEY (pk), INDEX idx1(i), INDEX idx2(v,i) -); +) COLLATE latin1_bin; INSERT INTO t3 VALUES (1,9,'x'), (2,5,'g'), (3,1,'o'), (4,0,'g'), (5,1,'v'), (6,190,'m'), (7,6,'x'), (8,3,'c'), (9,4,'z'), (10,3,'i'), @@ -2243,5 +2243,48 @@ DROP TABLE t1,t2,t3; SET SESSION join_cache_level=DEFAULT; +--echo # +--echo # Bug #668290: hash join with non-binary collations +--echo # + +CREATE TABLE t1 ( + i int DEFAULT NULL, + cl varchar(10) CHARACTER SET latin1 DEFAULT NULL, + cu varchar(10) CHARACTER SET utf8 DEFAULT NULL, + INDEX cl (cl), + INDEX cu (cu) +); +INSERT INTO t1 VALUES + (650903552,'cmxffkpsel','z'), (535298048,'tvtjrcmxff','y'), + (1626865664,'when','for'), (39649280,'rcvljitvtj','ercvljitvt'), + (792068096,'ttercvljit','jttercvlji'); +INSERT INTO t1 SELECT * FROM t1; + +CREATE TABLE t2 ( + cu varchar(10) CHARACTER SET utf8 DEFAULT NULL, + i int DEFAULT NULL, + cl varchar(10) CHARACTER SET latin1 DEFAULT NULL, + INDEX cu (cu), + INDEX cl (cl) +); +INSERT INTO t2 VALUES + ('g',7,'like'), ('fujttercvl',6,'y'), + ('s',2,'e'), ('didn\'t',0,'v'), + ('gvdrodpedk',8,'chogvdrodp'), ('jichogvdro',7,'will'); + +EXPLAIN +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; + +SET SESSION join_cache_level = 4; + +EXPLAIN +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; +SELECT t2.i FROM t1,t2 WHERE t1.cu = t2.cl ; + +SET SESSION join_cache_level = DEFAULT; + +DROP TABLE t1,t2; + # this must be the last command in the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/field.h b/sql/field.h index ca400caac59..a5f3ae6ba13 100644 --- a/sql/field.h +++ b/sql/field.h @@ -585,6 +585,10 @@ public: } /* Hash value */ virtual void hash(ulong *nr, ulong *nr2); + + /* Check whether the field can be used as a join attribute in hash join */ + virtual bool hash_join_is_possible() { return TRUE; } + friend bool reopen_table(THD *,struct st_table *,bool); friend int cre_myisam(char * name, register TABLE *form, uint options, ulonglong auto_increment_value); @@ -760,6 +764,12 @@ public: my_decimal *val_decimal(my_decimal *); virtual bool str_needs_quotes() { return TRUE; } uint is_equal(Create_field *new_field); + + bool hash_join_is_possible() + { + /* TODO: support hash joins for non-binary collations */ + return (flags & BINARY_FLAG); + } }; @@ -1904,6 +1914,7 @@ public: uint size_of() const { return sizeof(*this); } int reset(void) { return !maybe_null() || Field_blob::reset(); } geometry_type get_geometry_type() { return geom_type; }; + bool hash_join_is_possible() { return FALSE; } }; #endif /*HAVE_SPATIAL*/ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 95ba424ee97..f4a86baa253 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5946,6 +5946,39 @@ void JOIN_TAB::calc_used_field_length(bool max_fl) } +/** + @brief + Check whether hash join algorithm can be used to join this table + + @details + This function finds out whether the ref items that have been chosen + by the planner to access this table can be used for hash join algorithms. + The answer depends on a certain property of the the fields of the + joined tables on which the hash join key is built. If hash join is + allowed for all these fields the answer is positive. + + @note + The function is supposed to be called now only after the function + get_best_combination has been called. + + @retval TRUE it's possible to use hash join to join this table + @retval FALSE otherwise +*/ + +bool JOIN_TAB::hash_join_is_possible() +{ + if (type != JT_REF && type != JT_EQ_REF) + return FALSE; + KEY *keyinfo= &table->key_info[ref.key]; + for (uint i= 0; i < ref.key_parts; i++) + { + if (!keyinfo->key_part[i].field->hash_join_is_possible()) + return FALSE; + } + return TRUE; +} + + static uint cache_record_length(JOIN *join,uint idx) { @@ -7649,8 +7682,10 @@ uint check_join_cache_usage(JOIN_TAB *tab, &bufsz, &flags, &cost); if ((cache_level <=4 && !no_hashed_cache) || no_bka_cache || - (flags & HA_MRR_NO_ASSOCIATION) && cache_level <=6) + ((flags & HA_MRR_NO_ASSOCIATION) && cache_level <=6)) { + if (!tab->hash_join_is_possible()) + goto no_join_cache; if (cache_level == 3) prev_cache= 0; if ((tab->cache= new JOIN_CACHE_BNLH(join, tab, prev_cache)) && diff --git a/sql/sql_select.h b/sql/sql_select.h index a57b5116bea..65e27466294 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -391,6 +391,7 @@ typedef struct st_join_table { return max_used_fieldlength; } double get_partial_join_cardinality() { return partial_join_cardinality; } + bool hash_join_is_possible(); } JOIN_TAB;