summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/filesort.cc3
-rw-r--r--sql/ha_ndbcluster.cc1070
-rw-r--r--sql/ha_ndbcluster.h238
-rw-r--r--sql/handler.h10
-rw-r--r--sql/item.h13
-rw-r--r--sql/item_cmpfunc.cc24
-rw-r--r--sql/item_cmpfunc.h3
-rw-r--r--sql/item_func.cc26
-rw-r--r--sql/item_func.h3
-rw-r--r--sql/mysqld.cc7
-rw-r--r--sql/set_var.cc8
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_select.cc5
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
}
}
}