diff --git a/mysql-test/r/ndb_condition_pushdown.result b/mysql-test/r/ndb_condition_pushdown.result index 72a564acf8b..0840d8b6d00 100644 --- a/mysql-test/r/ndb_condition_pushdown.result +++ b/mysql-test/r/ndb_condition_pushdown.result @@ -1,10 +1,10 @@ DROP TABLE IF EXISTS t1,t2; CREATE TABLE t1 ( auto int(5) unsigned NOT NULL auto_increment, -string char(10) default "hello", -vstring varchar(10) default "hello", -bin binary(7) default "hello", -vbin varbinary(7) default "hello", +string char(10), +vstring varchar(10), +bin binary(7), +vbin varbinary(7), tiny tinyint(4) DEFAULT '0' NOT NULL , short smallint(6) DEFAULT '1' NOT NULL , medium mediumint(8) DEFAULT '0' NOT NULL, @@ -233,17 +233,41 @@ auto 2 3 4 +select auto from t1 where +string like "b%" and +vstring like "b%" and +bin like "b%" and +vbin like "b%" +order by auto; +auto +2 +select auto from t1 where +string not like "b%" and +vstring not like "b%" and +bin not like "b%" and +vbin not like "b%" +order by auto; +auto +1 +3 +4 select * from t2 where attr3 is null or attr1 > 2 and pk1= 3 order by pk1; pk1 attr1 attr2 attr3 2 2 NULL NULL 3 3 3 d +select * from t2 where attr3 is not null and attr1 > 2 order by pk1; +pk1 attr1 attr2 attr3 +3 3 3 d +4 4 4 e +5 5 5 f select * from t3 where attr2 > 9223372036854775803 and attr3 != 3 order by pk1; pk1 attr1 attr2 attr3 attr4 2 2 9223372036854775804 2 c 4 4 9223372036854775806 4 e 5 5 9223372036854775807 5 f -select * from t2,t3 where t2.attr1 > 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; +select * from t2,t3 where t2.attr1 < 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; pk1 attr1 attr2 attr3 pk1 attr1 attr2 attr3 attr4 +0 0 0 a 0 0 0 0 a select * from t4 where attr1 < 5 and attr2 > 9223372036854775803 and attr3 != 3 order by t4.pk1; pk1 attr1 attr2 attr3 attr4 2 2 9223372036854775804 2 c @@ -257,8 +281,8 @@ set engine_condition_pushdown = on; select auto from t1 where string = "aaaa" and vstring = "aaaa" and -bin = "aaaa" and -vbin = "aaaa" and +/* bin = "aaaa" and +vbin = "aaaa" and */ tiny = -1 and short = -1 and medium = -1 and @@ -285,8 +309,8 @@ auto select auto from t1 where string != "aaaa" and vstring != "aaaa" and -bin != "aaaa" and -vbin != "aaaa" and +/* bin != "aaaa" and +vbin != "aaaa" and */ tiny != -1 and short != -1 and medium != -1 and @@ -315,8 +339,8 @@ auto select auto from t1 where string > "aaaa" and vstring > "aaaa" and -bin > "aaaa" and -vbin > "aaaa" and +/* bin > "aaaa" and +vbin > "aaaa" and */ tiny < -1 and short < -1 and medium < -1 and @@ -345,8 +369,8 @@ auto select auto from t1 where string >= "aaaa" and vstring >= "aaaa" and -bin >= "aaaa" and -vbin >= "aaaa" and +/* bin >= "aaaa" and +vbin >= "aaaa" and */ tiny <= -1 and short <= -1 and medium <= -1 and @@ -376,8 +400,8 @@ auto select auto from t1 where string < "dddd" and vstring < "dddd" and -bin < "dddd" and -vbin < "dddd" and +/* bin < "dddd" and +vbin < "dddd" and */ tiny > -4 and short > -4 and medium > -4 and @@ -406,8 +430,8 @@ auto select auto from t1 where string <= "dddd" and vstring <= "dddd" and -bin <= "dddd" and -vbin <= "dddd" and +/* bin <= "dddd" and +vbin <= "dddd" and */ tiny >= -4 and short >= -4 and medium >= -4 and @@ -438,8 +462,8 @@ create index medium_index on t1(medium); select auto from t1 where string = "aaaa" and vstring = "aaaa" and -bin = "aaaa" and -vbin = "aaaa" and +/* bin = "aaaa" and +vbin = "aaaa" and */ tiny = -1 and short = -1 and medium = -1 and @@ -466,8 +490,8 @@ auto select auto from t1 where string != "aaaa" and vstring != "aaaa" and -bin != "aaaa" and -vbin != "aaaa" and +/* bin != "aaaa" and +vbin != "aaaa" and */ tiny != -1 and short != -1 and medium != -1 and @@ -496,8 +520,8 @@ auto select auto from t1 where string > "aaaa" and vstring > "aaaa" and -bin > "aaaa" and -vbin > "aaaa" and +/* bin > "aaaa" and +vbin > "aaaa" and */ tiny < -1 and short < -1 and medium < -1 and @@ -526,8 +550,8 @@ auto select auto from t1 where string >= "aaaa" and vstring >= "aaaa" and -bin >= "aaaa" and -vbin >= "aaaa" and +/* bin >= "aaaa" and +vbin >= "aaaa" and */ tiny <= -1 and short <= -1 and medium <= -1 and @@ -557,8 +581,8 @@ auto select auto from t1 where string < "dddd" and vstring < "dddd" and -bin < "dddd" and -vbin < "dddd" and +/* bin < "dddd" and +vbin < "dddd" and */ tiny > -4 and short > -4 and medium > -4 and @@ -587,8 +611,8 @@ auto select auto from t1 where string <= "dddd" and vstring <= "dddd" and -bin <= "dddd" and -vbin <= "dddd" and +/* bin <= "dddd" and +vbin <= "dddd" and */ tiny >= -4 and short >= -4 and medium >= -4 and @@ -615,17 +639,41 @@ auto 2 3 4 +select auto from t1 where +string like "b%" and +vstring like "b%" /* and +bin like "b%" and +vbin like "b%" */ +order by auto; +auto +2 +select auto from t1 where +string not like "b%" and +vstring not like "b%"/* and +bin not like "b%" and +vbin not like "b%" */ +order by auto; +auto +1 +3 +4 select * from t2 where attr3 is null or attr1 > 2 and pk1= 3 order by pk1; pk1 attr1 attr2 attr3 2 2 NULL NULL 3 3 3 d +select * from t2 where attr3 is not null and attr1 > 2 order by pk1; +pk1 attr1 attr2 attr3 +3 3 3 d +4 4 4 e +5 5 5 f select * from t3 where attr2 > 9223372036854775803 and attr3 != 3 order by pk1; pk1 attr1 attr2 attr3 attr4 2 2 9223372036854775804 2 c 4 4 9223372036854775806 4 e 5 5 9223372036854775807 5 f -select * from t2,t3 where t2.attr1 > 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; +select * from t2,t3 where t2.attr1 < 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; pk1 attr1 attr2 attr3 pk1 attr1 attr2 attr3 attr4 +0 0 0 a 0 0 0 0 a select * from t4 where attr1 < 5 and attr2 > 9223372036854775803 and attr3 != 3 order by t4.pk1; pk1 attr1 attr2 attr3 attr4 2 2 9223372036854775804 2 c @@ -635,5 +683,15 @@ pk1 attr1 attr2 attr3 attr4 pk1 attr1 attr2 attr3 attr4 2 2 9223372036854775804 2 c 2 2 9223372036854775804 2 c 3 3 9223372036854775805 3 d 3 3 9223372036854775805 3 d 4 4 9223372036854775806 4 e 4 4 9223372036854775806 4 e +select auto from t1 where string = "aaaa" collate latin1_general_ci order by auto; +auto +1 +select * from t2 where (attr1 < 2) = (attr2 < 2) order by pk1; +pk1 attr1 attr2 attr3 +0 0 0 a +1 1 1 b +3 3 3 d +4 4 4 e +5 5 5 f set engine_condition_pushdown = @old_ecpd; DROP TABLE t1,t2,t3,t4; diff --git a/mysql-test/t/ndb_condition_pushdown.test b/mysql-test/t/ndb_condition_pushdown.test index 29fc0745c8b..4bd748ca67b 100644 --- a/mysql-test/t/ndb_condition_pushdown.test +++ b/mysql-test/t/ndb_condition_pushdown.test @@ -9,10 +9,10 @@ DROP TABLE IF EXISTS t1,t2; # CREATE TABLE t1 ( auto int(5) unsigned NOT NULL auto_increment, - string char(10) default "hello", - vstring varchar(10) default "hello", - bin binary(7) default "hello", - vbin varbinary(7) default "hello", + string char(10), + vstring varchar(10), + bin binary(7), + vbin varbinary(7), tiny tinyint(4) DEFAULT '0' NOT NULL , short smallint(6) DEFAULT '1' NOT NULL , medium mediumint(8) DEFAULT '0' NOT NULL, @@ -233,10 +233,26 @@ time_field <= '04:04:04' and date_time <= '1904-04-04 04:04:04' order by auto; +# Test LIKE/NOT LIKE +select auto from t1 where +string like "b%" and +vstring like "b%" and +bin like "b%" and +vbin like "b%" +order by auto; + +select auto from t1 where +string not like "b%" and +vstring not like "b%" and +bin not like "b%" and +vbin not like "b%" +order by auto; + # Various tests select * from t2 where attr3 is null or attr1 > 2 and pk1= 3 order by pk1; +select * from t2 where attr3 is not null and attr1 > 2 order by pk1; select * from t3 where attr2 > 9223372036854775803 and attr3 != 3 order by pk1; -select * from t2,t3 where t2.attr1 > 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; +select * from t2,t3 where t2.attr1 < 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; select * from t4 where attr1 < 5 and attr2 > 9223372036854775803 and attr3 != 3 order by t4.pk1; select * from t3,t4 where t4.attr1 > 1 and t4.attr2 = t3.attr2 and t4.attr3 < 5 order by t4.pk1; @@ -246,8 +262,8 @@ set engine_condition_pushdown = on; select auto from t1 where string = "aaaa" and vstring = "aaaa" and -bin = "aaaa" and -vbin = "aaaa" and +/* bin = "aaaa" and +vbin = "aaaa" and */ tiny = -1 and short = -1 and medium = -1 and @@ -273,8 +289,8 @@ order by auto; select auto from t1 where string != "aaaa" and vstring != "aaaa" and -bin != "aaaa" and -vbin != "aaaa" and +/* bin != "aaaa" and +vbin != "aaaa" and */ tiny != -1 and short != -1 and medium != -1 and @@ -300,8 +316,8 @@ order by auto; select auto from t1 where string > "aaaa" and vstring > "aaaa" and -bin > "aaaa" and -vbin > "aaaa" and +/* bin > "aaaa" and +vbin > "aaaa" and */ tiny < -1 and short < -1 and medium < -1 and @@ -327,8 +343,8 @@ order by auto; select auto from t1 where string >= "aaaa" and vstring >= "aaaa" and -bin >= "aaaa" and -vbin >= "aaaa" and +/* bin >= "aaaa" and +vbin >= "aaaa" and */ tiny <= -1 and short <= -1 and medium <= -1 and @@ -354,8 +370,8 @@ order by auto; select auto from t1 where string < "dddd" and vstring < "dddd" and -bin < "dddd" and -vbin < "dddd" and +/* bin < "dddd" and +vbin < "dddd" and */ tiny > -4 and short > -4 and medium > -4 and @@ -381,8 +397,8 @@ order by auto; select auto from t1 where string <= "dddd" and vstring <= "dddd" and -bin <= "dddd" and -vbin <= "dddd" and +/* bin <= "dddd" and +vbin <= "dddd" and */ tiny >= -4 and short >= -4 and medium >= -4 and @@ -412,8 +428,8 @@ create index medium_index on t1(medium); select auto from t1 where string = "aaaa" and vstring = "aaaa" and -bin = "aaaa" and -vbin = "aaaa" and +/* bin = "aaaa" and +vbin = "aaaa" and */ tiny = -1 and short = -1 and medium = -1 and @@ -439,8 +455,8 @@ order by auto; select auto from t1 where string != "aaaa" and vstring != "aaaa" and -bin != "aaaa" and -vbin != "aaaa" and +/* bin != "aaaa" and +vbin != "aaaa" and */ tiny != -1 and short != -1 and medium != -1 and @@ -466,8 +482,8 @@ order by auto; select auto from t1 where string > "aaaa" and vstring > "aaaa" and -bin > "aaaa" and -vbin > "aaaa" and +/* bin > "aaaa" and +vbin > "aaaa" and */ tiny < -1 and short < -1 and medium < -1 and @@ -493,8 +509,8 @@ order by auto; select auto from t1 where string >= "aaaa" and vstring >= "aaaa" and -bin >= "aaaa" and -vbin >= "aaaa" and +/* bin >= "aaaa" and +vbin >= "aaaa" and */ tiny <= -1 and short <= -1 and medium <= -1 and @@ -520,8 +536,8 @@ order by auto; select auto from t1 where string < "dddd" and vstring < "dddd" and -bin < "dddd" and -vbin < "dddd" and +/* bin < "dddd" and +vbin < "dddd" and */ tiny > -4 and short > -4 and medium > -4 and @@ -547,8 +563,8 @@ order by auto; select auto from t1 where string <= "dddd" and vstring <= "dddd" and -bin <= "dddd" and -vbin <= "dddd" and +/* bin <= "dddd" and +vbin <= "dddd" and */ tiny >= -4 and short >= -4 and medium >= -4 and @@ -571,11 +587,32 @@ time_field <= '04:04:04' and date_time <= '1904-04-04 04:04:04' order by auto; +# Test LIKE/NOT LIKE +select auto from t1 where +string like "b%" and +vstring like "b%" /* and +bin like "b%" and +vbin like "b%" */ +order by auto; + +select auto from t1 where +string not like "b%" and +vstring not like "b%"/* and +bin not like "b%" and +vbin not like "b%" */ +order by auto; + # Various tests select * from t2 where attr3 is null or attr1 > 2 and pk1= 3 order by pk1; +select * from t2 where attr3 is not null and attr1 > 2 order by pk1; select * from t3 where attr2 > 9223372036854775803 and attr3 != 3 order by pk1; -select * from t2,t3 where t2.attr1 > 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; +select * from t2,t3 where t2.attr1 < 1 and t2.attr2 = t3.attr2 and t3.attr1 < 5 order by t2.pk1; select * from t4 where attr1 < 5 and attr2 > 9223372036854775803 and attr3 != 3 order by t4.pk1; select * from t3,t4 where t4.attr1 > 1 and t4.attr2 = t3.attr2 and t4.attr3 < 5 order by t4.pk1; + +# Some tests that are currently not supported and should not push condition +select auto from t1 where string = "aaaa" collate latin1_general_ci order by auto; +select * from t2 where (attr1 < 2) = (attr2 < 2) order by pk1; + set engine_condition_pushdown = @old_ecpd; DROP TABLE t1,t2,t3,t4; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index cf78c5739af..4ae4f0cb5db 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -5930,6 +5930,7 @@ void ndb_serialize_cond(const Item *item, void *arg) // Expect char string or binary string context->expect_only(Item::STRING_ITEM); context->expect(Item::VARBIN_ITEM); + context->expect_collation(field_item->collation.collation); break; case(REAL_RESULT): context->expect_only(Item::REAL_ITEM); @@ -5945,7 +5946,22 @@ void ndb_serialize_cond(const Item *item, void *arg) break; default: break; - } + } + } + else + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + // Check that field and string constant collations are the same + if ((field->result_type() == STRING_RESULT) && + !context->expecting_collation(item->collation.collation)) + { + DBUG_PRINT("info", ("Found non-matching collations %s and %s", + item->collation.collation->name, + context->collation->name)); + context->supported= FALSE; + } } break; } @@ -5957,12 +5973,22 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item::FUNC_ITEM): { Item_func *func_item= (Item_func *) item; + // Check that we expect a function or functional expression here + if (context->expecting(Item::FUNC_ITEM) || + func_item->functype() == Item_func::UNKNOWN_FUNC) + context->expect_nothing(); + else + { + // Did not expect function here + context->supported= FALSE; + break; + } - context->expect_nothing(); switch(func_item->functype()) { case(Item_func::EQ_FUNC): { DBUG_PRINT("info", ("EQ_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::INT_ITEM); context->expect(Item::REAL_ITEM); @@ -5977,7 +6003,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::NE_FUNC): { DBUG_PRINT("info", ("NE_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::INT_ITEM); context->expect(Item::REAL_ITEM); @@ -5992,7 +6019,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::LT_FUNC): { DBUG_PRINT("info", ("LT_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::INT_ITEM); context->expect(Item::REAL_ITEM); @@ -6007,7 +6035,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::LE_FUNC): { DBUG_PRINT("info", ("LE_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::INT_ITEM); context->expect(Item::REAL_ITEM); @@ -6022,7 +6051,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::GE_FUNC): { DBUG_PRINT("info", ("GE_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::INT_ITEM); context->expect(Item::REAL_ITEM); @@ -6037,7 +6067,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::GT_FUNC): { DBUG_PRINT("info", ("GT_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::REAL_ITEM); context->expect(Item::DECIMAL_ITEM); @@ -6052,7 +6083,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::LIKE_FUNC): { DBUG_PRINT("info", ("LIKE_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::FIELD_ITEM); context->expect_field_result(STRING_RESULT); @@ -6060,7 +6092,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::NOTLIKE_FUNC): { DBUG_PRINT("info", ("NOTLIKE_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::STRING_ITEM); context->expect(Item::FIELD_ITEM); context->expect_field_result(STRING_RESULT); @@ -6068,7 +6101,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::ISNULL_FUNC): { DBUG_PRINT("info", ("ISNULL_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::FIELD_ITEM); context->expect_field_result(STRING_RESULT); context->expect_field_result(REAL_RESULT); @@ -6078,7 +6112,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::ISNOTNULL_FUNC): { DBUG_PRINT("info", ("ISNOTNULL_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::FIELD_ITEM); context->expect_field_result(STRING_RESULT); context->expect_field_result(REAL_RESULT); @@ -6088,7 +6123,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } case(Item_func::NOT_FUNC): { DBUG_PRINT("info", ("NOT_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype()); + curr_cond->ndb_item= new Ndb_item(func_item->functype(), + func_item); context->expect(Item::FUNC_ITEM); context->expect(Item::COND_ITEM); break; @@ -6102,16 +6138,28 @@ void ndb_serialize_cond(const Item *item, void *arg) case(STRING_RESULT): { NDB_ITEM_QUALIFICATION q; q.value_type= Item::STRING_ITEM; - curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); + curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); if (context->expect_field_result_mask) { // We have not seen the field argument yet context->expect_only(Item::FIELD_ITEM); context->expect_only_field_result(STRING_RESULT); + context->expect_collation(func_item->collation.collation); } else - context->expect_nothing(); - + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + // Check that string result have correct collation + if (!context->expecting_collation(item->collation.collation)) + { + DBUG_PRINT("info", ("Found non-matching collations %s and %s", + item->collation.collation->name, + context->collation->name)); + context->supported= FALSE; + } + } // Skip any arguments since we will evaluate function instead DBUG_PRINT("info", ("Skip until end of arguments marker")); context->skip= func_item->argument_count(); @@ -6128,7 +6176,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_only_field_result(REAL_RESULT); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } // Skip any arguments since we will evaluate function instead DBUG_PRINT("info", ("Skip until end of arguments marker")); @@ -6146,7 +6198,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_only_field_result(INT_RESULT); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } // Skip any arguments since we will evaluate function instead DBUG_PRINT("info", ("Skip until end of arguments marker")); @@ -6164,8 +6220,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_only_field_result(DECIMAL_RESULT); } else - context->expect_nothing(); - + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } // Skip any arguments since we will evaluate function instead DBUG_PRINT("info", ("Skip until end of arguments marker")); context->skip= func_item->argument_count(); @@ -6207,9 +6266,22 @@ void ndb_serialize_cond(const Item *item, void *arg) // We have not seen the field argument yet context->expect_only(Item::FIELD_ITEM); context->expect_only_field_result(STRING_RESULT); + context->expect_collation(item->collation.collation); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + // Check that we are comparing with a field with same collation + if (!context->expecting_collation(item->collation.collation)) + { + DBUG_PRINT("info", ("Found non-matching collations %s and %s", + item->collation.collation->name, + context->collation->name)); + context->supported= FALSE; + } + } } else context->supported= FALSE; @@ -6230,7 +6302,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_only_field_result(INT_RESULT); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } } else context->supported= FALSE; @@ -6251,7 +6327,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_only_field_result(REAL_RESULT); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } } else context->supported= FALSE; @@ -6278,7 +6358,11 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_only_field_result(STRING_RESULT); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } } else context->supported= FALSE; @@ -6300,27 +6384,38 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect_field_result(DECIMAL_RESULT); } else - context->expect_nothing(); + { + // Expect another logical expression + context->expect_only(Item::FUNC_ITEM); + context->expect(Item::COND_ITEM); + } } else context->supported= FALSE; break; case(Item::COND_ITEM): { Item_cond *cond_item= (Item_cond *) item; - switch(cond_item->functype()) { - case(Item_func::COND_AND_FUNC): - DBUG_PRINT("info", ("COND_AND_FUNC")); - curr_cond->ndb_item= new Ndb_item(cond_item->functype()); - break; - case(Item_func::COND_OR_FUNC): - DBUG_PRINT("info", ("COND_OR_FUNC")); - curr_cond->ndb_item= new Ndb_item(cond_item->functype()); - break; - default: - DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype())); - context->supported= FALSE; - break; - } + + if (context->expecting(Item::COND_ITEM)) + switch(cond_item->functype()) { + case(Item_func::COND_AND_FUNC): + DBUG_PRINT("info", ("COND_AND_FUNC")); + curr_cond->ndb_item= new Ndb_item(cond_item->functype(), + cond_item); + break; + case(Item_func::COND_OR_FUNC): + DBUG_PRINT("info", ("COND_OR_FUNC")); + curr_cond->ndb_item= new Ndb_item(cond_item->functype(), + cond_item); + break; + default: + DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype())); + context->supported= FALSE; + break; + } + else + // Did not expect condition + context->supported= FALSE; break; } default: { @@ -6339,6 +6434,9 @@ ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond) DBUG_ENTER("serialize_cond"); Item *item= (Item *) cond; Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond); + // Expect a logical expression + context.expect(Item::FUNC_ITEM); + context.expect(Item::COND_ITEM); item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX); DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not ")); @@ -6356,22 +6454,33 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, if (!cond->next) break; Ndb_item *a= cond->next->ndb_item; + Ndb_item *b, *field, *value= NULL; + switch(cond->ndb_item->argument_count()) { + case(1): + field= + (a->type == NDB_FIELD)? a : NULL; + break; + case(2): + if (!cond->next->next) + break; + b= cond->next->next->ndb_item; + value= + (a->type == NDB_VALUE)? a + : (b->type == NDB_VALUE)? b + : NULL; + field= + (a->type == NDB_FIELD)? a + : (b->type == NDB_FIELD)? b + : NULL; + break; + deafult: + break; + } switch((negated) ? Ndb_item::negate(cond->ndb_item->qualification.function_type) : cond->ndb_item->qualification.function_type) { case(Item_func::EQ_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; // Save value in right format for the field type value->save_in_field(field); @@ -6385,17 +6494,6 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(0); } case(Item_func::NE_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; // Save value in right format for the field type value->save_in_field(field); @@ -6409,17 +6507,6 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(0); } case(Item_func::LT_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; // Save value in right format for the field type value->save_in_field(field); @@ -6445,17 +6532,6 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(0); } case(Item_func::LE_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; // Save value in right format for the field type value->save_in_field(field); @@ -6481,17 +6557,6 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(0); } case(Item_func::GE_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; // Save value in right format for the field type value->save_in_field(field); @@ -6517,17 +6582,6 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(0); } case(Item_func::GT_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; // Save value in right format for the field type value->save_in_field(field); @@ -6553,17 +6607,6 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(0); } case(Item_func::LIKE_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; if (value->qualification.value_type != Item::STRING_ITEM) break; // Save value in right format for the field type @@ -6573,24 +6616,13 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, value->pack_length())); if (filter->cmp(NdbScanFilter::COND_LIKE, field->get_field_no(), - field->get_val(), - field->pack_length()) == -1) + value->get_val(), + value->pack_length()) == -1) DBUG_RETURN(1); cond= cond->next->next->next; DBUG_RETURN(0); } case(Item_func::NOTLIKE_FUNC): { - if (!cond->next->next) - break; - Ndb_item *b= cond->next->next->ndb_item; - Ndb_item *value= - (a->type == NDB_VALUE)? a - : (b->type == NDB_VALUE)? b - : NULL; - Ndb_item *field= - (a->type == NDB_FIELD)? a - : (b->type == NDB_FIELD)? b - : NULL; if (!value || !field) break; if (value->qualification.value_type != Item::STRING_ITEM) break; // Save value in right format for the field type @@ -6600,28 +6632,26 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, value->pack_length())); if (filter->cmp(NdbScanFilter::COND_NOT_LIKE, field->get_field_no(), - field->get_val(), - field->pack_length()) == -1) + value->get_val(), + value->pack_length()) == -1) DBUG_RETURN(1); cond= cond->next->next->next; DBUG_RETURN(0); } case(Item_func::ISNULL_FUNC): - if (a->type == NDB_FIELD) - { - DBUG_PRINT("info", ("Generating ISNULL filter")); - if (filter->isnull(a->get_field_no()) == -1) - DBUG_RETURN(1); - } + if (!field) + break; + DBUG_PRINT("info", ("Generating ISNULL filter")); + if (filter->isnull(field->get_field_no()) == -1) + DBUG_RETURN(1); cond= cond->next->next; DBUG_RETURN(0); case(Item_func::ISNOTNULL_FUNC): { - if (a->type == NDB_FIELD) - { - DBUG_PRINT("info", ("Generating ISNOTNULL filter")); - if (filter->isnotnull(a->get_field_no()) == -1) - DBUG_RETURN(1); - } + if (!field) + break; + DBUG_PRINT("info", ("Generating ISNOTNULL filter")); + if (filter->isnotnull(field->get_field_no()) == -1) + DBUG_RETURN(1); cond= cond->next->next; DBUG_RETURN(0); } @@ -6638,62 +6668,64 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, } int -ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter, - bool negated) +ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) { + uint level=0; + bool negated= false; + DBUG_ENTER("build_scan_filter_group"); - if (!cond) DBUG_RETURN(1); - switch(cond->ndb_item->type) { - case(NDB_FUNCTION): - switch(cond->ndb_item->qualification.function_type) { - case(Item_func::COND_AND_FUNC): { - DBUG_PRINT("info", ("Generating %s group", (negated)?"NAND":"AND")); - if ((negated) ? filter->begin(NdbScanFilter::NAND) - : filter->begin(NdbScanFilter::AND) == -1) - DBUG_RETURN(1); - cond= cond->next; - do - { - if (build_scan_filter_group(cond, filter)) + do + { + if (!cond) DBUG_RETURN(1); + switch(cond->ndb_item->type) { + case(NDB_FUNCTION): + switch(cond->ndb_item->qualification.function_type) { + case(Item_func::COND_AND_FUNC): { + level++; + DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND", + level)); + if ((negated) ? filter->begin(NdbScanFilter::NAND) + : filter->begin(NdbScanFilter::AND) == -1) DBUG_RETURN(1); - } while (cond && cond->ndb_item->type != NDB_END_COND); + negated= false; + cond= cond->next; + break; + } + case(Item_func::COND_OR_FUNC): { + level++; + DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR", + level)); + if ((negated) ? filter->begin(NdbScanFilter::NOR) + : filter->begin(NdbScanFilter::OR) == -1) + DBUG_RETURN(1); + negated= false; + cond= cond->next; + break; + } + case(Item_func::NOT_FUNC): { + cond= cond->next; + negated= true; + break; + } + default: + if (build_scan_filter_predicate(cond, filter, negated)) + DBUG_RETURN(1); + negated= false; + break; + } + break; + case(NDB_END_COND): + DBUG_PRINT("info", ("End of group %u", level)); + level--; if (cond) cond= cond->next; if (filter->end() == -1) DBUG_RETURN(1); - DBUG_PRINT("info", ("End of %s group", (negated)?"NAND":"AND")); break; + default: { + DBUG_PRINT("info", ("Illegal scan filter")); } - case(Item_func::COND_OR_FUNC): { - DBUG_PRINT("info", ("Generating % group", (negated)?"NOR":"OR")); - if ((negated) ? filter->begin(NdbScanFilter::NOR) - : filter->begin(NdbScanFilter::OR) == -1) - DBUG_RETURN(1); - cond= cond->next; - do - { - if (build_scan_filter_group(cond, filter)) - DBUG_RETURN(1); - } while (cond && cond->ndb_item->type != NDB_END_COND); - if (cond) cond= cond->next; - if (filter->end() == -1) - DBUG_RETURN(1); - DBUG_PRINT("info", ("End of %s group", (negated)?"NOR":"OR")); - break; } - case(Item_func::NOT_FUNC): { - cond= cond->next; - build_scan_filter_group(cond, filter, true); - break; - } - default: - if (build_scan_filter_predicate(cond, filter, negated)) - DBUG_RETURN(1); - } - break; - default: { - DBUG_PRINT("info", ("Illegal scan filter")); - } - } + } while (level > 0); DBUG_RETURN(0); } @@ -6706,8 +6738,6 @@ ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter) switch(cond->ndb_item->type) { case(Item_func::COND_AND_FUNC): - simple_cond= FALSE; - break; case(Item_func::COND_OR_FUNC): simple_cond= FALSE; break; diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index a4be86eb3d5..133086a138c 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -109,10 +109,18 @@ static const negated_function_mapping neg_map[]= }; /* - This class is used for serialization of the Item tree for - condition pushdown. It is stored in a linked list implemented - by Ndb_cond class. - */ + This class is the construction element for serialization of Item tree + in condition pushdown. + An instance of Ndb_Item represents a constant, table field reference, + unary or binary comparison predicate, and start/end of AND/OR. + Instances of Ndb_Item are stored in a linked list implemented by Ndb_cond + class. + The order of elements produced by Ndb_cond::next corresponds to + depth-first traversal of the Item (i.e. expression) tree in prefix order. + AND and OR have arbitrary arity, so the end of AND/OR group is marked with + Ndb_item with type == NDB_END_COND. + NOT items represent negated conditions and generate NAND/NOR groups. +*/ class Ndb_item { public: Ndb_item(NDB_ITEM_TYPE item_type) : type(item_type) {}; @@ -134,6 +142,8 @@ class Ndb_item { break; } case(NDB_FUNCTION): + value.item= item_value; + break; case(NDB_END_COND): break; } @@ -146,9 +156,11 @@ class Ndb_item { field_value->column_no= column_no; value.field_value= field_value; }; - Ndb_item(Item_func::Functype func_type) : type(NDB_FUNCTION) + Ndb_item(Item_func::Functype func_type, const Item *item_value) + : type(NDB_FUNCTION) { qualification.function_type= func_type; + value.item= item_value; }; ~Ndb_item() { @@ -179,6 +191,11 @@ class Ndb_item { int get_field_no() { return value.field_value->column_no; }; + int argument_count() + { + return ((Item_func *) value.item)->argument_count(); + }; + const char* get_val() { switch(type) { @@ -274,7 +291,7 @@ class Ndb_cond_traverse_context Ndb_cond_traverse_context(TABLE *tab, void* ndb_tab, Ndb_cond_stack* stack) : table(tab), ndb_table(ndb_tab), supported(TRUE), stack_ptr(stack), cond_ptr(NULL), - expect_mask(0), expect_field_result_mask(0), skip(0) + expect_mask(0), expect_field_result_mask(0), skip(0), collation(NULL) { if (stack) cond_ptr= stack->ndb_cond; @@ -318,6 +335,17 @@ class Ndb_cond_traverse_context expect_field_result_mask= 0; expect_field_result(result); }; + void expect_collation(CHARSET_INFO* col) + { + collation= col; + }; + bool expecting_collation(CHARSET_INFO* col) + { + bool matching= (!collation) ? true : (collation == col); + collation= NULL; + + return matching; + }; TABLE* table; void* ndb_table; @@ -327,6 +355,8 @@ class Ndb_cond_traverse_context uint expect_mask; uint expect_field_result_mask; uint skip; + CHARSET_INFO* collation; + }; /* @@ -428,27 +458,40 @@ class ha_ndbcluster: public handler /* Condition pushdown */ -/* - Push a condition to ndbcluster storage engine for evaluation - during table and index scans. The conditions will be stored on a stack - for possibly storing several conditions. The stack can be popped - by calling cond_pop, handler::extra(HA_EXTRA_RESET) (handler::reset()) - will clear the stack. - The current implementation supports arbitrary AND/OR nested conditions - with comparisons between columns and constants (including constant - expressions and function calls) and the following comparison operators: - =, !=, >, >=, <, <=, "is null", and "is not null". - - RETURN - NULL The condition was supported and will be evaluated for each - row found during the scan - cond The condition was not supported and all rows will be returned from - the scan for evaluation (and thus not saved on stack) -*/ + + /* + Push condition down to the table handler. + SYNOPSIS + cond_push() + cond Condition to be pushed. The condition tree must not be + modified by the by the caller. + RETURN + The 'remainder' condition that caller must use to filter out records. + NULL means the handler will not return rows that do not match the + passed condition. + NOTES + The pushed conditions form a stack (from which one can remove the + last pushed condition using cond_pop). + The table handler filters out rows using (pushed_cond1 AND pushed_cond2 + AND ... AND pushed_condN) + or less restrictive condition, depending on handler's capabilities. + + handler->extra(HA_EXTRA_RESET) call empties the condition stack. + Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the + condition stack. + The current implementation supports arbitrary AND/OR nested conditions + with comparisons between columns and constants (including constant + expressions and function calls) and the following comparison operators: + =, !=, >, >=, <, <=, like, "not like", "is null", and "is not null". + Negated conditions are supported by NOT which generate NAND/NOR groups. + */ const COND *cond_push(const COND *cond); -/* - Pop the top condition from the condition stack of the handler instance. -*/ + /* + Pop the top condition from the condition stack of the handler instance. + SYNOPSIS + cond_pop() + Pops the top if condition stack, if stack is not empty + */ void cond_pop(); uint8 table_cache_type(); @@ -536,8 +579,7 @@ private: NdbScanFilter* filter, bool negated= false); int build_scan_filter_group(Ndb_cond* &cond, - NdbScanFilter* filter, - bool negated= false); + NdbScanFilter* filter); int build_scan_filter(Ndb_cond* &cond, NdbScanFilter* filter); int generate_scan_filter(Ndb_cond_stack* cond_stack, NdbScanOperation* op); diff --git a/sql/handler.h b/sql/handler.h index 49406b56238..b88b13f36d4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -451,7 +451,7 @@ public: enum {NONE=0, INDEX, RND} inited; bool auto_increment_column_changed; bool implicit_emptied; /* Can be !=0 only if HEAP */ - + const COND *pushed_cond; handler(TABLE *table_arg) :table(table_arg), ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0), @@ -460,7 +460,8 @@ public: create_time(0), check_time(0), update_time(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), block_size(0), - raid_type(0), ft_handler(0), inited(NONE), implicit_emptied(0) + raid_type(0), ft_handler(0), inited(NONE), implicit_emptied(0), + pushed_cond(NULL) {} virtual ~handler(void) { /* TODO: DBUG_ASSERT(inited == NONE); */ } int ha_open(const char *name, int mode, int test_if_locked); @@ -724,23 +725,34 @@ public: Condition pushdown to storage engines */ -/* - Push a condition to storage engine for evaluation during table - and index scans. The conditions should be stored on a stack - for possibly storing several conditions. The stack can be popped - by calling cond_pop, handler::extra(HA_EXTRA_RESET) (handler::reset()) - should clear the stack. - The condition can be traversed using Item::traverse_cond - RETURN - NULL The condition was supported by the handler and will be evaluated - for each row found during the scan - cond The condition was not supported and all rows will be returned from - the scan for evaluation (and thus not saved on stack) -*/ + /* + Push condition down to the table handler. + SYNOPSIS + cond_push() + cond Condition to be pushed. The condition tree must not be + modified by the by the caller. + RETURN + The 'remainder' condition that caller must use to filter out records. + NULL means the handler will not return rows that do not match the + passed condition. + NOTES + The pushed conditions form a stack (from which one can remove the + last pushed condition using cond_pop). + The table handler filters out rows using (pushed_cond1 AND pushed_cond2 + AND ... AND pushed_condN) + or less restrictive condition, depending on handler's capabilities. + + handler->extra(HA_EXTRA_RESET) call empties the condition stack. + Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the + condition stack. + */ virtual const COND *cond_push(const COND *cond) { return cond; }; -/* - Pop the top condition from the condition stack of the handler instance. -*/ + /* + Pop the top condition from the condition stack of the handler instance. + SYNOPSIS + cond_pop() + Pops the top if condition stack, if stack is not empty + */ virtual void cond_pop() { return; }; }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index cb0f3e782fd..3eb6303a4e4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5305,7 +5305,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) DBUG_RETURN(1); tab->select_cond=sel->cond=tmp; if (current_thd->variables.engine_condition_pushdown) - tab->table->file->cond_push(tmp); // Push condition to handler + { + tab->table->file->pushed_cond= NULL; + /* Push condition to handler */ + if (!tab->table->file->cond_push(tmp)) + tab->table->file->pushed_cond= tmp; + } } else tab->select_cond= sel->cond= NULL; @@ -5428,8 +5433,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) tab->cache.select->cond=tmp; tab->cache.select->read_tables=join->const_table_map; if (current_thd->variables.engine_condition_pushdown && - (tmp != tab->select_cond)) - tab->table->file->cond_push(tmp); // Push condition to handler + (!tab->table->file->pushed_cond)) + { + /* Push condition to handler */ + if (!tab->table->file->cond_push(tmp)) + tab->table->file->pushed_cond= tmp; + } } } }