diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/filesort.cc | 3 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 1070 | ||||
-rw-r--r-- | sql/ha_ndbcluster.h | 238 | ||||
-rw-r--r-- | sql/handler.h | 10 | ||||
-rw-r--r-- | sql/item.h | 13 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 24 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 3 | ||||
-rw-r--r-- | sql/item_func.cc | 26 | ||||
-rw-r--r-- | sql/item_func.h | 3 | ||||
-rw-r--r-- | sql/mysqld.cc | 7 | ||||
-rw-r--r-- | sql/set_var.cc | 8 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_select.cc | 5 |
13 files changed, 1289 insertions, 122 deletions
diff --git a/sql/filesort.cc b/sql/filesort.cc index de14287003b..14cac5b01db 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -420,9 +420,6 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, next_pos=ref_pos; if (! indexfile && ! quick_select) { - file->reset(); // QQ; Shouldn't be needed - if (sort_form->key_read) // QQ Can be removed after the reset - file->extra(HA_EXTRA_KEYREAD); // QQ is removed next_pos=(byte*) 0; /* Find records in sequence */ file->ha_rnd_init(1); file->extra_opt(HA_EXTRA_CACHE, diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 58f744438fc..cd02634d4a2 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -252,7 +252,6 @@ int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans) /* Place holder for ha_ndbcluster thread specific data */ - Thd_ndb::Thd_ndb() { ndb= new Ndb(g_ndb_cluster_connection, ""); @@ -1767,6 +1766,9 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, if (res) DBUG_RETURN(res); } + + if (!restart && generate_scan_filter(m_cond_stack, op)) + DBUG_RETURN(ndb_err(trans)); if (!restart && (res= define_read_attrs(buf, op))) { @@ -1780,96 +1782,6 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, } /* - Start a filtered scan in NDB. - - NOTE - This function is here as an example of how to start a - filtered scan. It should be possible to replace full_table_scan - with this function and make a best effort attempt - at filtering out the irrelevant data by converting the "items" - into interpreted instructions. - This would speed up table scans where there is a limiting WHERE clause - that doesn't match any index in the table. - - */ - -int ha_ndbcluster::filtered_scan(const byte *key, uint key_len, - byte *buf, - enum ha_rkey_function find_flag) -{ - int res; - NdbTransaction *trans= m_active_trans; - NdbScanOperation *op; - - DBUG_ENTER("filtered_scan"); - DBUG_PRINT("enter", ("key_len: %u, index: %u", - key_len, active_index)); - DBUG_DUMP("key", (char*)key, key_len); - DBUG_PRINT("info", ("Starting a new filtered scan on %s", - m_tabname)); - - NdbOperation::LockMode lm= - (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); - if (!(op= trans->getNdbScanOperation((const NDBTAB *) m_table)) || - op->readTuples(lm, 0, parallelism)) - ERR_RETURN(trans->getNdbError()); - m_active_cursor= op; - - { - // Start scan filter - NdbScanFilter sf(op); - sf.begin(); - - // Set filter using the supplied key data - byte *key_ptr= (byte *) key; - uint tot_len= 0; - KEY* key_info= table->key_info + active_index; - for (uint k= 0; k < key_info->key_parts; k++) - { - KEY_PART_INFO* key_part= key_info->key_part+k; - Field* field= key_part->field; - uint ndb_fieldnr= key_part->fieldnr-1; - DBUG_PRINT("key_part", ("fieldnr: %d", ndb_fieldnr)); - //const NDBCOL *col= ((const NDBTAB *) m_table)->getColumn(ndb_fieldnr); - uint32 field_len= field->pack_length(); - DBUG_DUMP("key", (char*)key, field_len); - - DBUG_PRINT("info", ("Column %s, type: %d, len: %d", - field->field_name, field->real_type(), field_len)); - - // Define scan filter - if (field->real_type() == MYSQL_TYPE_STRING) - sf.cmp(NdbScanFilter::COND_EQ, ndb_fieldnr, key_ptr, field_len); - else - { - if (field_len == 8) - sf.eq(ndb_fieldnr, (Uint64)*key_ptr); - else if (field_len <= 4) - sf.eq(ndb_fieldnr, (Uint32)*key_ptr); - else - DBUG_RETURN(1); - } - - key_ptr += field_len; - tot_len += field_len; - - if (tot_len >= key_len) - break; - } - // End scan filter - sf.end(); - } - - if((res= define_read_attrs(buf, op))) - DBUG_RETURN(res); - - if (execute_no_commit(this,trans) != 0) - DBUG_RETURN(ndb_err(trans)); - DBUG_PRINT("exit", ("Scan started successfully")); - DBUG_RETURN(next_result(buf)); -} - -/* Start full table scan in NDB */ @@ -1888,7 +1800,8 @@ int ha_ndbcluster::full_table_scan(byte *buf) op->readTuples(lm, 0, parallelism)) ERR_RETURN(trans->getNdbError()); m_active_cursor= op; - + if (generate_scan_filter(m_cond_stack, op)) + DBUG_RETURN(ndb_err(trans)); if((res= define_read_attrs(buf, op))) DBUG_RETURN(res); @@ -2339,7 +2252,7 @@ void ha_ndbcluster::unpack_record(byte* buf) DBUG_PRINT("hidden", ("%d: %s \"%llu\"", hidden_no, hidden_col->getName(), rec->u_64_value())); } - print_results(); + //print_results(); #endif DBUG_VOID_RETURN; } @@ -2856,6 +2769,8 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) break; case HA_EXTRA_RESET: /* Reset database to after open */ DBUG_PRINT("info", ("HA_EXTRA_RESET")); + DBUG_PRINT("info", ("Clearing condition stack")); + cond_clear(); break; case HA_EXTRA_CACHE: /* Cash record in HA_rrnd() */ DBUG_PRINT("info", ("HA_EXTRA_CACHE")); @@ -3049,15 +2964,7 @@ int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size) } -int ha_ndbcluster::reset() -{ - DBUG_ENTER("reset"); - // Reset what? - DBUG_RETURN(1); -} - static const char *ha_ndb_bas_ext[]= { ha_ndb_ext, NullS }; - const char** ha_ndbcluster::bas_ext() const { @@ -4071,6 +3978,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_force_send(TRUE), m_autoincrement_prefetch(32), m_transaction_on(TRUE), + m_cond_stack(NULL), m_multi_cursor(NULL) { int i; @@ -4117,6 +4025,10 @@ ha_ndbcluster::~ha_ndbcluster() } DBUG_ASSERT(m_active_trans == NULL); + // Discard the condition stack + DBUG_PRINT("info", ("Clearing condition stack")); + cond_clear(); + DBUG_VOID_RETURN; } @@ -5378,6 +5290,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, } else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) &&!scanOp->readTuples(lm, 0, parallelism, sorted, false, true) + &&!generate_scan_filter(m_cond_stack, scanOp) &&!define_read_attrs(end_of_buffer-reclength, scanOp)) { m_multi_cursor= scanOp; @@ -5389,6 +5302,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, m_active_trans->getNdbError()); } } + const key_range *keys[2]= { &multi_range_curr->start_key, &multi_range_curr->end_key }; if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges))) @@ -5760,5 +5674,959 @@ extern "C" pthread_handler_decl(ndb_util_thread_func, DBUG_RETURN(NULL); } +/* + Condition pushdown +*/ +const +COND* +ha_ndbcluster::cond_push(const COND *cond) +{ + Ndb_cond_stack *ndb_cond = new Ndb_cond_stack(); + DBUG_ENTER("cond_push"); + DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname);); + if (m_cond_stack) + ndb_cond->next= m_cond_stack; + else + ndb_cond->next= NULL; + m_cond_stack= ndb_cond; + + if (serialize_cond(cond, ndb_cond)) + { + DBUG_RETURN(NULL); + } + else + { + cond_pop(); + } + + DBUG_RETURN(cond); +} + +inline +void +ha_ndbcluster::cond_pop() +{ + Ndb_cond_stack *ndb_cond_stack= m_cond_stack; + if (ndb_cond_stack) + { + m_cond_stack= ndb_cond_stack->next; + delete ndb_cond_stack; + } +} + +void +ha_ndbcluster::cond_clear() +{ + DBUG_ENTER("cond_clear"); + while (m_cond_stack) + cond_pop(); + + DBUG_VOID_RETURN; +} + +void ndb_serialize_cond(const Item *item, void *arg) +{ + Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg; + DBUG_ENTER("ndb_serialize_cond"); + + // Check if we are skipping arguments to a function to be evaluated + if (context->skip) + { + DBUG_PRINT("info", ("Skiping argument %d", context->skip)); + context->skip--; + switch(item->type()) { + case (Item::FUNC_ITEM): { + Item_func *func_item= (Item_func *) item; + context->skip+= func_item->argument_count(); + break; + } + case(Item::INT_ITEM): + case(Item::REAL_ITEM): + case(Item::STRING_ITEM): + case(Item::VARBIN_ITEM): + case(Item::DECIMAL_ITEM): + break; + default: + *context->supported_ptr= FALSE; + break; + } + + DBUG_VOID_RETURN; + } + + if (*context->supported_ptr) + { + Ndb_cond_stack *ndb_stack= context->stack_ptr; + Ndb_cond *prev_cond= context->cond_ptr; + Ndb_cond *curr_cond= context->cond_ptr= new Ndb_cond(); + if (!ndb_stack->ndb_cond) + ndb_stack->ndb_cond= curr_cond; + curr_cond->prev= prev_cond; + if (prev_cond) prev_cond->next= curr_cond; + + // Check for end of AND/OR expression + if (!item) + { + // End marker for condition group + DBUG_PRINT("info", ("End of condition group")); + curr_cond->ndb_item= new Ndb_item(NDB_END_COND); + } + else + switch(item->type()) { + case(Item::FIELD_ITEM): { + Item_field *field_item= (Item_field *) item; + Field *field= field_item->field; + enum_field_types type= field->type(); + /* + Check that the field is part of the table of the handler + instance and that we expect a field with of this result type. + */ + if (context->table == field->table) + { + const NDBTAB *tab= (const NDBTAB *) context->ndb_table; + DBUG_PRINT("info", ("FIELD_ITEM")); + DBUG_PRINT("info", ("table %s", tab->getName())); + DBUG_PRINT("info", ("column %s", field->field_name)); + DBUG_PRINT("info", ("result type %d", field->result_type())); + + // Check that we are expecting a field and with the correct + // result type + if(context->expecting(Item::FIELD_ITEM) && + (context->expecting_field_result(field->result_type()) || + // Date and year can be written as strings + (type == MYSQL_TYPE_TIME || + type == MYSQL_TYPE_DATE || + type == MYSQL_TYPE_YEAR || + type == MYSQL_TYPE_DATETIME) + ? context->expecting_field_result(STRING_RESULT) : true) + // Bit fields no yet supported in scan filter + && type != MYSQL_TYPE_BIT) + { + const NDBCOL *col= tab->getColumn(field->field_name); + DBUG_ASSERT(col); + curr_cond->ndb_item= new Ndb_item(field, col->getColumnNo()); + context->dont_expect(Item::FIELD_ITEM); + context->expect_no_field_result(); + if (context->expect_mask) + { + // We have not seen second argument yet + if (type == MYSQL_TYPE_TIME || + type == MYSQL_TYPE_DATE || + type == MYSQL_TYPE_YEAR || + type == MYSQL_TYPE_DATETIME) + { + context->expect_only(Item::STRING_ITEM); + context->expect(Item::INT_ITEM); + } + else + switch(field->result_type()) { + case(STRING_RESULT): + // Expect char string or binary string + context->expect_only(Item::STRING_ITEM); + context->expect(Item::VARBIN_ITEM); + break; + case(REAL_RESULT): + context->expect_only(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + break; + case(INT_RESULT): + context->expect_only(Item::INT_ITEM); + context->expect(Item::VARBIN_ITEM); + break; + case(DECIMAL_RESULT): + context->expect_only(Item::DECIMAL_ITEM); + context->expect(Item::REAL_ITEM); + break; + default: + break; + } + } + break; + } + } + *context->supported_ptr= FALSE; + break; + } + case(Item::FUNC_ITEM): { + Item_func *func_item= (Item_func *) item; + + 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()); + context->expect(Item::STRING_ITEM); + context->expect(Item::INT_ITEM); + context->expect(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + context->expect(Item::VARBIN_ITEM); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::NE_FUNC): { + DBUG_PRINT("info", ("NE_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + context->expect(Item::INT_ITEM); + context->expect(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + context->expect(Item::VARBIN_ITEM); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::LT_FUNC): { + DBUG_PRINT("info", ("LT_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + context->expect(Item::INT_ITEM); + context->expect(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + context->expect(Item::VARBIN_ITEM); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::LE_FUNC): { + DBUG_PRINT("info", ("LE_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + context->expect(Item::INT_ITEM); + context->expect(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + context->expect(Item::VARBIN_ITEM); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::GE_FUNC): { + DBUG_PRINT("info", ("GE_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + context->expect(Item::INT_ITEM); + context->expect(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + context->expect(Item::VARBIN_ITEM); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::GT_FUNC): { + DBUG_PRINT("info", ("GT_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + context->expect(Item::REAL_ITEM); + context->expect(Item::DECIMAL_ITEM); + context->expect(Item::INT_ITEM); + context->expect(Item::VARBIN_ITEM); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::LIKE_FUNC): { + DBUG_PRINT("info", ("LIKE_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + *context->supported_ptr= FALSE; // Currently not supported + break; + } + case(Item_func::NOTLIKE_FUNC): { + DBUG_PRINT("info", ("NOTLIKE_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::STRING_ITEM); + *context->supported_ptr= FALSE; // Currently not supported + break; + } + case(Item_func::ISNULL_FUNC): { + DBUG_PRINT("info", ("ISNULL_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::ISNOTNULL_FUNC): { + DBUG_PRINT("info", ("ISNOTNULL_FUNC")); + curr_cond->ndb_item= new Ndb_item(func_item->functype()); + context->expect(Item::FIELD_ITEM); + context->expect_field_result(STRING_RESULT); + context->expect_field_result(REAL_RESULT); + context->expect_field_result(INT_RESULT); + context->expect_field_result(DECIMAL_RESULT); + break; + } + case(Item_func::UNKNOWN_FUNC): { + DBUG_PRINT("info", ("UNKNOWN_FUNC %s", + func_item->const_item()?"const":"")); + DBUG_PRINT("info", ("result type %d", func_item->result_type())); + if (func_item->const_item()) + switch(func_item->result_type()) { + case(STRING_RESULT): { + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::STRING_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); + } + else + context->expect_nothing(); + + // Skip any arguments since we will evaluate function instead + DBUG_PRINT("info", ("Skip until end of arguments marker")); + context->skip= func_item->argument_count(); + break; + } + case(REAL_RESULT): { + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::REAL_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(REAL_RESULT); + } + else + context->expect_nothing(); + + // Skip any arguments since we will evaluate function instead + DBUG_PRINT("info", ("Skip until end of arguments marker")); + context->skip= func_item->argument_count(); + break; + } + case(INT_RESULT): { + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::INT_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(INT_RESULT); + } + else + context->expect_nothing(); + + // Skip any arguments since we will evaluate function instead + DBUG_PRINT("info", ("Skip until end of arguments marker")); + context->skip= func_item->argument_count(); + break; + } + case(DECIMAL_RESULT): { + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::DECIMAL_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(DECIMAL_RESULT); + } + else + context->expect_nothing(); + + // Skip any arguments since we will evaluate function instead + DBUG_PRINT("info", ("Skip until end of arguments marker")); + context->skip= func_item->argument_count(); + break; + } + default: + break; + } + else + // Function does not return constant expression + *context->supported_ptr= FALSE; + break; + } + default: { + DBUG_PRINT("info", ("Found func_item of type %d", + func_item->functype())); + *context->supported_ptr= FALSE; + } + } + break; + } + case(Item::STRING_ITEM): + DBUG_PRINT("info", ("STRING_ITEM")); + if (context->expecting(Item::STRING_ITEM)) + { + char buff[256]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + str.length(0); + Item_string *string_item= (Item_string *) item; + DBUG_PRINT("info", ("value \"%s\"", + string_item->val_str(&str)->ptr())); + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::STRING_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); + } + else + context->expect_nothing(); + } + else + *context->supported_ptr= FALSE; + break; + case(Item::INT_ITEM): + DBUG_PRINT("info", ("INT_ITEM")); + if (context->expecting(Item::INT_ITEM)) + { + Item_int *int_item= (Item_int *) item; + DBUG_PRINT("info", ("value %d", int_item->value)); + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::INT_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(INT_RESULT); + } + else + context->expect_nothing(); + } + else + *context->supported_ptr= FALSE; + break; + case(Item::REAL_ITEM): + DBUG_PRINT("info", ("REAL_ITEM %s")); + if (context->expecting(Item::REAL_ITEM)) + { + Item_float *float_item= (Item_float *) item; + DBUG_PRINT("info", ("value %f", float_item->value)); + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::REAL_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(REAL_RESULT); + } + else + context->expect_nothing(); + } + else + *context->supported_ptr= FALSE; + break; + case(Item::VARBIN_ITEM): + DBUG_PRINT("info", ("VARBIN_ITEM")); + if (context->expecting(Item::VARBIN_ITEM)) + { + char buff[256]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + str.length(0); + Item_hex_string *varbin_item= (Item_hex_string *) item; + DBUG_PRINT("info", ("value \"%s\"", + varbin_item->val_str(&str)->ptr())); + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::VARBIN_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); + } + else + context->expect_nothing(); + } + else + *context->supported_ptr= FALSE; + break; + case(Item::DECIMAL_ITEM): + DBUG_PRINT("info", ("DECIMAL_ITEM %s")); + if (context->expecting(Item::DECIMAL_ITEM)) + { + Item_decimal *decimal_item= (Item_decimal *) item; + DBUG_PRINT("info", ("value %f", decimal_item->val_real())); + NDB_ITEM_QUALIFICATION q; + q.value_type= Item::DECIMAL_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(REAL_RESULT); + context->expect_field_result(DECIMAL_RESULT); + } + else + context->expect_nothing(); + } + else + *context->supported_ptr= 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_ptr= FALSE; + break; + } + break; + } + default: { + DBUG_PRINT("info", ("Found item of type %d", item->type())); + *context->supported_ptr= FALSE; + } + } + } + + DBUG_VOID_RETURN; +} + +bool +ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond) +{ + DBUG_ENTER("serialize_cond"); + Item *item= (Item *) cond; + bool supported= TRUE; + Ndb_cond_traverse_context context(table, (void *)m_table, + &supported, ndb_cond); + item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX); + DBUG_PRINT("info", ("The pushed condition is %ssupported", (supported)?"":"not ")); + + DBUG_RETURN(supported); +} + +int +ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, + NdbScanFilter *filter) +{ + DBUG_ENTER("build_scan_filter_predicate"); + switch(cond->ndb_item->type) { + case(NDB_FUNCTION): { + if (!cond->next) + break; + Ndb_item *a= cond->next->ndb_item; + switch(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); + DBUG_PRINT("info", ("Generating EQ filter")); + if (filter->cmp(NdbScanFilter::COND_EQ, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + cond= cond->next->next->next; + 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); + DBUG_PRINT("info", ("Generating NE filter")); + if (filter->cmp(NdbScanFilter::COND_NE, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + cond= cond->next->next->next; + 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); + if (a == field) + { + DBUG_PRINT("info", ("Generating LT filter")); + if (filter->cmp(NdbScanFilter::COND_LT, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + else + { + DBUG_PRINT("info", ("Generating GT filter")); + if (filter->cmp(NdbScanFilter::COND_GT, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + cond= cond->next->next->next; + 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); + if (a == field) + { + DBUG_PRINT("info", ("Generating LE filter")); + if (filter->cmp(NdbScanFilter::COND_LE, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + else + { + DBUG_PRINT("info", ("Generating GE filter")); + if (filter->cmp(NdbScanFilter::COND_GE, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + cond= cond->next->next->next; + 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); + if (a == field) + { + DBUG_PRINT("info", ("Generating GE filter")); + if (filter->cmp(NdbScanFilter::COND_GE, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + else + { + DBUG_PRINT("info", ("Generating LE filter")); + if (filter->cmp(NdbScanFilter::COND_LE, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + cond= cond->next->next->next; + 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); + if (a == field) + { + DBUG_PRINT("info", ("Generating GT filter")); + if (filter->cmp(NdbScanFilter::COND_GT, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + else + { + DBUG_PRINT("info", ("Generating LT filter")); + if (filter->cmp(NdbScanFilter::COND_LT, + field->get_field_no(), + field->get_val(), + field->pack_length()) == -1) + DBUG_RETURN(1); + } + cond= cond->next->next->next; + 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 + value->save_in_field(field); + DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", + field->get_field_no(), field->get_val(), + field->pack_length())); + /* + if (filter->like(field->get_field_no(), + field->get_val(), + field->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 + value->save_in_field(field); + DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)", + field->get_field_no(), field->get_val(), + field->pack_length())); + /* + if (filter->notlike(field->get_field_no(), + field->get_val(), field->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); + } + 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); + } + cond= cond->next->next; + DBUG_RETURN(0); + } + default: + break; + } + break; + } + default: + break; + } + DBUG_PRINT("info", ("Found illegal condition")); + DBUG_RETURN(1); +} + +int +ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) +{ + DBUG_ENTER("build_scan_filter_group"); + 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 AND group")); + if (filter->begin(NdbScanFilter::AND) == -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 AND group")); + break; + } + case(Item_func::COND_OR_FUNC): { + DBUG_PRINT("info", ("Generating OR group")); + if (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 OR group")); + break; + } + default: + if (build_scan_filter_predicate(cond, filter)) + DBUG_RETURN(1); + } + break; + default: { + DBUG_PRINT("info", ("Illegal scan filter")); + } + } + + DBUG_RETURN(0); +} + +int +ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter) +{ + bool simple_cond= TRUE; + DBUG_ENTER("build_scan_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; + default: + break; + } + if (simple_cond && filter->begin() == -1) + DBUG_RETURN(1); + if (build_scan_filter_group(cond, filter)) + DBUG_RETURN(1); + if (simple_cond && filter->end() == -1) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + +int +ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack, + NdbScanOperation *op) +{ + DBUG_ENTER("generate_scan_filter"); + if (ndb_cond_stack) + { + NdbScanFilter filter(op); + bool multiple_cond= FALSE; + // Wrap an AND group around multiple conditions + if (ndb_cond_stack->next) { + multiple_cond= TRUE; + if (filter.begin() == -1) + DBUG_RETURN(1); + } + for (Ndb_cond_stack *stack= ndb_cond_stack; + (stack); + stack= stack->next) + { + Ndb_cond *cond= stack->ndb_cond; + + if (build_scan_filter(cond, &filter)) + { + DBUG_PRINT("info", ("build_scan_filter failed")); + DBUG_RETURN(1); + } + } + if (multiple_cond && filter.end() == -1) + DBUG_RETURN(1); + } + else + { + DBUG_PRINT("info", ("Empty stack")); + } + + DBUG_RETURN(0); +} #endif /* HAVE_NDBCLUSTER_DB */ diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index b27908c20ea..cee52d2dbf3 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -32,6 +32,7 @@ class NdbOperation; // Forward declaration class NdbTransaction; // Forward declaration class NdbRecAttr; // Forward declaration class NdbScanOperation; +class NdbScanFilter; class NdbIndexScanOperation; class NdbBlob; @@ -63,6 +64,193 @@ typedef struct st_ndbcluster_share { ulonglong commit_count; } NDB_SHARE; +typedef enum ndb_item_type { + NDB_VALUE = 0, // Qualified more with Item::Type + NDB_FIELD = 1, // Qualified from table definition + NDB_FUNCTION = 2,// Qualified from Item_func::Functype + NDB_END_COND = 3 // End marker for condition group +} NDB_ITEM_TYPE; + +typedef union ndb_item_qualification { + Item::Type value_type; + enum_field_types field_type; // Instead of Item::FIELD_ITEM + Item_func::Functype function_type; // Instead of Item::FUNC_ITEM +} NDB_ITEM_QUALIFICATION; + +typedef struct ndb_item_field_value { + Field* field; + int column_no; +} NDB_ITEM_FIELD_VALUE; + +typedef union ndb_item_value { + const Item *item; + NDB_ITEM_FIELD_VALUE *field_value; +} NDB_ITEM_VALUE; + +class Ndb_item { + public: + Ndb_item(NDB_ITEM_TYPE item_type) : type(item_type) {}; + Ndb_item(NDB_ITEM_TYPE item_type, + NDB_ITEM_QUALIFICATION item_qualification, + const Item *item_value) + : type(item_type), qualification(item_qualification) + { + switch(item_type) { + case(NDB_VALUE): + value.item= item_value; + break; + case(NDB_FIELD): { + NDB_ITEM_FIELD_VALUE *field_value= new NDB_ITEM_FIELD_VALUE(); + Item_field *field_item= (Item_field *) item_value; + field_value->field= field_item->field; + field_value->column_no= -1; // Will be fetched at scan filter generation + value.field_value= field_value; + break; + } + case(NDB_FUNCTION): + case(NDB_END_COND): + break; + } + }; + Ndb_item(Field *field, int column_no) : type(NDB_FIELD) + { + NDB_ITEM_FIELD_VALUE *field_value= new NDB_ITEM_FIELD_VALUE(); + qualification.field_type= field->type(); + field_value->field= field; + field_value->column_no= column_no; + value.field_value= field_value; + }; + Ndb_item(Item_func::Functype func_type) : type(NDB_FUNCTION) + { + qualification.function_type= func_type; + }; + ~Ndb_item() + { + if (type == NDB_FIELD) + { + delete value.field_value; + value.field_value= NULL; + } + }; + + uint32 pack_length() + { + switch(type) { + case(NDB_FIELD): + return value.field_value->field->pack_length(); + default: + break; + } + + return 0; + }; + Field * get_field() { return value.field_value->field; }; + int get_field_no() { return value.field_value->column_no; }; + char* get_val() { return value.field_value->field->ptr; }; + void save_in_field(Ndb_item *field_item) + { + Field *field = field_item->value.field_value->field; + const Item *item= value.item; + + if (item && field) + ((Item *)item)->save_in_field(field, false); + } + + NDB_ITEM_TYPE type; + NDB_ITEM_QUALIFICATION qualification; + private: + NDB_ITEM_VALUE value; +}; + +class Ndb_cond { + public: + Ndb_cond() : ndb_item(NULL), next(NULL), prev(NULL) {}; + ~Ndb_cond() + { + if (ndb_item) delete ndb_item; + ndb_item= NULL; + if (next) delete next; + next= prev= NULL; + }; + Ndb_item *ndb_item; + Ndb_cond *next; + Ndb_cond *prev; +}; + +class Ndb_cond_stack { + public: + Ndb_cond_stack() : ndb_cond(NULL), next(NULL) {}; + ~Ndb_cond_stack() + { + if (ndb_cond) delete ndb_cond; + ndb_cond= NULL; + next= NULL; + }; + Ndb_cond *ndb_cond; + Ndb_cond_stack *next; +}; + +class Ndb_cond_traverse_context { + public: + Ndb_cond_traverse_context(TABLE *tab, void* ndb_tab, + bool *supported, Ndb_cond_stack* stack) + : table(tab), ndb_table(ndb_tab), + supported_ptr(supported), stack_ptr(stack), cond_ptr(NULL), + expect_mask(0), expect_field_result_mask(0), skip(0) + { + if (stack) + cond_ptr= stack->ndb_cond; + }; + void expect(Item::Type type) + { + expect_mask|= (1 << type); + }; + void dont_expect(Item::Type type) + { + expect_mask&= ~(1 << type); + }; + bool expecting(Item::Type type) + { + return (expect_mask & (1 << type)); + }; + void expect_nothing() + { + expect_mask= 0; + }; + void expect_only(Item::Type type) + { + expect_mask= 0; + expect(type); + }; + + void expect_field_result(Item_result result) + { + expect_field_result_mask|= (1 << result); + }; + bool expecting_field_result(Item_result result) + { + return (expect_field_result_mask & (1 << result)); + }; + void expect_no_field_result() + { + expect_field_result_mask= 0; + }; + void expect_only_field_result(Item_result result) + { + expect_field_result_mask= 0; + expect_field_result(result); + }; + + TABLE* table; + void* ndb_table; + bool *supported_ptr; + Ndb_cond_stack* stack_ptr; + Ndb_cond* cond_ptr; + uint expect_mask; + uint expect_field_result_mask; + uint skip; +}; + /* Place holder for ha_ndbcluster thread specific data */ @@ -126,7 +314,6 @@ class ha_ndbcluster: public handler void info(uint); int extra(enum ha_extra_function operation); int extra_opt(enum ha_extra_function operation, ulong cache_size); - int reset(); int external_lock(THD *thd, int lock_type); int start_stmt(THD *thd); const char * table_type() const; @@ -156,6 +343,13 @@ class ha_ndbcluster: public handler static Thd_ndb* seize_thd_ndb(); static void release_thd_ndb(Thd_ndb* thd_ndb); + + /* + Condition pushdown + */ + const COND *cond_push(const COND *cond); + void cond_pop(); + uint8 table_cache_type(); my_bool register_query_cache_table(THD *thd, char *table_key, uint key_length, @@ -219,13 +413,36 @@ private: int ndb_err(NdbTransaction*); bool uses_blob_value(bool all_fields); - int write_ndb_file(); - char *update_table_comment(const char * comment); - private: + int write_ndb_file(); + int check_ndb_connection(); + void set_rec_per_key(); + void records_update(); + void no_uncommitted_rows_execute_failure(); + void no_uncommitted_rows_update(int); + void no_uncommitted_rows_init(THD *); + void no_uncommitted_rows_reset(THD *); + + /* + Condition pushdown + */ + void cond_clear(); + bool serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond); + int build_scan_filter_predicate(Ndb_cond* &cond, + NdbScanFilter* filter); + int build_scan_filter_group(Ndb_cond* &cond, + NdbScanFilter* filter); + int build_scan_filter(Ndb_cond* &cond, NdbScanFilter* filter); + int generate_scan_filter(Ndb_cond_stack* cond_stack, + NdbScanOperation* op); + + friend int execute_commit(ha_ndbcluster*, NdbTransaction*); + friend int execute_no_commit(ha_ndbcluster*, NdbTransaction*); + friend int execute_no_commit_ie(ha_ndbcluster*, NdbTransaction*); + NdbTransaction *m_active_trans; NdbScanOperation *m_active_cursor; void *m_table; @@ -261,7 +478,7 @@ private: bool m_force_send; ha_rows m_autoincrement_prefetch; bool m_transaction_on; - + Ndb_cond_stack *m_cond_stack; bool m_disable_multi_read; byte *m_multi_range_result_ptr; KEY_MULTI_RANGE *m_multi_ranges; @@ -270,18 +487,7 @@ private: NdbIndexScanOperation *m_multi_cursor; byte *m_multi_range_cursor_result_ptr; int setup_recattr(const NdbRecAttr*); - Ndb *get_ndb(); - void set_rec_per_key(); - void records_update(); - void no_uncommitted_rows_execute_failure(); - void no_uncommitted_rows_update(int); - void no_uncommitted_rows_init(THD *); - void no_uncommitted_rows_reset(THD *); - - friend int execute_no_commit(ha_ndbcluster*, NdbTransaction*); - friend int execute_commit(ha_ndbcluster*, NdbTransaction*); - friend int execute_no_commit_ie(ha_ndbcluster*, NdbTransaction*); }; extern struct show_var_st ndb_status_variables[]; diff --git a/sql/handler.h b/sql/handler.h index 64deab48b7d..6fc2552290d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -268,6 +268,9 @@ typedef struct st_table TABLE; struct st_foreign_key_info; typedef struct st_foreign_key_info FOREIGN_KEY_INFO; +/* Forward declaration for Condition Pushdown to Handler (CPDH) */ +typedef struct Item COND; + typedef struct st_ha_check_opt { ulong sort_buffer_size; @@ -607,7 +610,6 @@ public: *engine_callback= 0; return 1; } - /* RETURN true Primary key (if there is one) is clustered key covering all fields @@ -619,6 +621,12 @@ public: { return memcmp(ref1, ref2, ref_length); } + + /* + Condition pushdown to storage engines + */ + virtual const COND *cond_push(const COND *cond) { return cond; }; + virtual void cond_pop() { return; }; }; /* Some extern variables used with handlers */ diff --git a/sql/item.h b/sql/item.h index 299bc6c081b..76bfc295b99 100644 --- a/sql/item.h +++ b/sql/item.h @@ -113,6 +113,8 @@ public: typedef bool (Item::*Item_processor)(byte *arg); typedef Item* (Item::*Item_transformer) (byte *arg); +typedef void (*Item_cond_traverser) (const Item *item, void *arg); + class Item { Item(const Item &); /* Prevent use of these */ void operator=(Item &); @@ -123,7 +125,7 @@ public: static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); } static void operator delete(void *ptr, MEM_ROOT *mem_root) {} - enum Type {FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM, + enum Type {FIELD_ITEM= 0, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM, INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM, COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM, PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, @@ -132,6 +134,8 @@ public: PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; + + enum traverse_order { POSTFIX, PREFIX }; /* str_values's main purpose is to be used to cache the value in @@ -383,6 +387,13 @@ public: { return (this->*transformer)(arg); } + + virtual void traverse_cond(Item_cond_traverser traverser, + void *arg, + traverse_order order = POSTFIX) + { + (*traverser)(this, arg); + } virtual bool remove_dependence_processor(byte * arg) { return 0; } virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3884cce8451..5d764a734bc 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2363,6 +2363,30 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) return Item_func::transform(transformer, arg); } +void Item_cond::traverse_cond(Item_cond_traverser traverser, + void *arg, + traverse_order order) +{ + List_iterator<Item> li(list); + Item *item; + + switch(order) { + case(PREFIX): + (*traverser)(this, arg); + while ((item= li++)) + { + item->traverse_cond(traverser, arg, order); + } + (*traverser)(NULL, arg); + break; + case(POSTFIX): + while ((item= li++)) + { + item->traverse_cond(traverser, arg, order); + } + (*traverser)(this, arg); + } +} /* Move SUM items out from item tree and replace with reference diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 717bcbca7d5..3ec7131aac1 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1028,6 +1028,9 @@ public: void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, byte *arg); Item *transform(Item_transformer transformer, byte *arg); + void traverse_cond(Item_cond_traverser, + void *arg, + traverse_order order = POSTFIX); void neg_arguments(THD *thd); }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 1e61474f412..b741d62d3f9 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -360,6 +360,32 @@ bool Item_func::walk (Item_processor processor, byte *argument) return (this->*processor)(argument); } +void Item_func::traverse_cond(Item_cond_traverser traverser, + void *argument, + traverse_order order) +{ + if (arg_count) + { + Item **arg,**arg_end; + + switch (order) { + case(PREFIX): + (*traverser)(this, argument); + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + (*arg)->traverse_cond(traverser, argument, order); + } + break; + case (POSTFIX): + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + (*arg)->traverse_cond(traverser, argument, order); + } + (*traverser)(this, argument); + } + } +} + /* diff --git a/sql/item_func.h b/sql/item_func.h index 20f70fce575..d454f0ace59 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -163,6 +163,9 @@ public: uint flags= 0); bool walk(Item_processor processor, byte *arg); Item *transform(Item_transformer transformer, byte *arg); + void traverse_cond(Item_cond_traverser traverser, + void * arg, + traverse_order order = POSTFIX); }; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2998d367a36..8d2a5f4c7d7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4170,6 +4170,7 @@ enum options_mysqld OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG, OPT_SAFE_SHOW_DB, OPT_INNODB_SAFE_BINLOG, OPT_INNODB, OPT_ISAM, + OPT_ENGINE_CONDITION_PUSHDOWN, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME, @@ -4429,6 +4430,12 @@ Disable with --skip-bdb (will save memory).", {"enable-pstack", OPT_DO_PSTACK, "Print a symbolic stack trace on failure.", (gptr*) &opt_do_pstack, (gptr*) &opt_do_pstack, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"engine-condition-pushdown", + OPT_ENGINE_CONDITION_PUSHDOWN, + "Push supported query conditions to the storage engine.", + (gptr*) &global_system_variables.engine_condition_pushdown, + (gptr*) &global_system_variables.engine_condition_pushdown, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.", diff --git a/sql/set_var.cc b/sql/set_var.cc index 6fb44ae3ea3..f81aeaaf83d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -402,6 +402,11 @@ sys_var_long_ptr sys_innodb_thread_concurrency("innodb_thread_concurrency", &srv_thread_concurrency); #endif +/* Condition pushdown to storage engine */ +sys_var_thd_bool +sys_engine_condition_pushdown("engine_condition_pushdown", + &SV::engine_condition_pushdown); + #ifdef HAVE_NDBCLUSTER_DB /* ndb thread specific variable settings */ sys_var_thd_ulong @@ -684,6 +689,7 @@ sys_var *sys_variables[]= &sys_innodb_thread_sleep_delay, &sys_innodb_thread_concurrency, #endif + &sys_engine_condition_pushdown, #ifdef HAVE_NDBCLUSTER_DB &sys_ndb_autoincrement_prefetch_sz, &sys_ndb_force_send, @@ -864,6 +870,8 @@ struct show_var_st init_vars[]= { #ifdef __NT__ {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_MY_BOOL}, #endif + {sys_engine_condition_pushdown.name, + (char*) &sys_engine_condition_pushdown, SHOW_SYS}, #ifdef HAVE_NDBCLUSTER_DB {sys_ndb_autoincrement_prefetch_sz.name, (char*) &sys_ndb_autoincrement_prefetch_sz, SHOW_SYS}, diff --git a/sql/sql_class.h b/sql/sql_class.h index 7d3ee78ccf5..2e98c329072 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -431,6 +431,7 @@ struct system_variables my_bool low_priority_updates; my_bool new_mode; my_bool query_cache_wlock_invalidate; + my_bool engine_condition_pushdown; #ifdef HAVE_REPLICATION ulong sync_replication; ulong sync_replication_slave_id; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dead6a6e08e..61a0e2a9061 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5304,6 +5304,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) if (!(tmp= add_found_match_trig_cond(first_inner_tab, tmp, 0))) 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 } else tab->select_cond= sel->cond= NULL; @@ -5425,6 +5427,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) join->thd->memdup((gptr) sel, sizeof(SQL_SELECT)); 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 } } } |