List:Commits« Previous MessageNext Message »
From:Ole John Aske Date:October 26 2010 9:22am
Subject:bzr push into mysql-5.1-telco-7.0-spj-scan-vs-scan branch
(ole.john.aske:3322 to 3323)
View as plain text  
 3323 Ole John Aske	2010-10-26
      spj-svs: Added EXPLAIN EXTENDED functionality which add info as warnings about why (parts of) a query is not pushed:
      
      Usage:
       set ndb_join_pushdown = true
      
       EXPLAIN EXTENDED <query>;
       SHOW WARNINGS;

    modified:
      sql/ha_ndbcluster.cc
      sql/ha_ndbcluster.h
      sql/share/errmsg.txt
 3322 Ole John Aske	2010-10-21
      spj-svs: Enhanced implementation of handling 'sorted indexscan(t1) -> index ref(t2)'
      
      We previously had to block pushing of scan childs if the root operation was an ordered index scan as
      we are not able to provide these result ordered through the root operation.
      
      This fix is now reverted - Instead we now allow these kind of queries to be pushed, but require
      the result to be written to temp. file where it is filesorted. We believe the overhead of this
      extra filesort to be far less than not pushing the query.

    modified:
      mysql-test/suite/ndb/r/ndb_join_pushdown.result
      sql/abstract_query_plan.cc
      sql/abstract_query_plan.h
      sql/ha_ndbcluster.cc
      sql/handler.h
      sql/sql_select.cc
=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-10-21 08:31:51 +0000
+++ b/sql/ha_ndbcluster.cc	2010-10-26 09:21:10 +0000
@@ -306,6 +306,14 @@ static int ndb_to_mysql_error(const NdbE
   code= ndb_to_mysql_error(&tmp);        \
 }
 
+#define EXPLAIN_NO_PUSH(msgfmt, ...)                              \
+{                                                                 \
+  if (unlikely((current_thd->lex->describe & DESCRIBE_EXTENDED))) \
+  {                                                               \
+    explain_no_push ((msgfmt), __VA_ARGS__);                      \
+  }                                                               \
+}
+
 static int ndbcluster_inited= 0;
 int ndbcluster_terminating= 0;
 
@@ -455,11 +463,21 @@ public:
 class ndb_pushed_builder_ctx
 {
 public:
-  ndb_pushed_builder_ctx(AQP::Join_plan& plan, const AQP::Table_access* const join_root)
-   : m_plan(plan), m_join_root(join_root), m_join_scope(join_root), m_const_scope()
+
+  ndb_pushed_builder_ctx(AQP::Join_plan& plan)
+   : m_plan(plan), m_join_root(), m_join_scope(), m_const_scope()
+  { init_pushability();
+  }
+
+  void set_root(const AQP::Table_access* const join_root)
   {
+    m_join_root= join_root;
+    m_join_scope.clear_all();
+    m_const_scope.clear_all();
+
+    m_join_scope.add(join_root);
     for (uint i= 0; i<join_root->get_access_no(); i++)
-      m_const_scope.add(plan.get_table_access(i));
+      m_const_scope.add(m_plan.get_table_access(i));
   }
 
   const AQP::Join_plan& plan() const
@@ -488,7 +506,10 @@ public:
                   const ndb_table_access_map& old_parents,
                   ndb_table_access_map& parents) const;
 
-  bool field_ref_is_join_pushable(
+  bool is_pushable_as_parent(
+                  const AQP::Table_access* table);
+
+  bool is_pushable_as_child(
                   const AQP::Table_access* table,
                   const Item* join_items[],
                   const AQP::Table_access*& parent);
@@ -506,9 +527,18 @@ public:
     return parents.is_overlapping(m_tables[table->get_access_no()].m_ancestors);
   }
 
+  enum pushability
+  {
+    PUSHABLE_AS_PARENT= 0x01,
+    PUSHABLE_AS_CHILD= 0x02
+  } enum_pushability;
+
+private:
+  void init_pushability();
+
 private:
   const AQP::Join_plan& m_plan;
-  const AQP::Table_access* const m_join_root;
+  const AQP::Table_access* m_join_root;
 
   // Scope of tables covered by this pushed join
   ndb_table_access_map m_join_scope;
@@ -520,12 +550,14 @@ private:
   struct pushed_tables
   {
     pushed_tables() : 
+      m_maybe_pushable(PUSHABLE_AS_CHILD | PUSHABLE_AS_PARENT),
       m_parent(MAX_TABLES), 
       m_ancestors(), 
       m_last_scan_descendant(MAX_TABLES), 
       op(NULL) 
     {}
 
+    int  m_maybe_pushable;
     uint m_parent;
     ndb_table_access_map m_ancestors;
     uint m_last_scan_descendant;
@@ -672,6 +704,17 @@ static bool is_lookup_operation(AQP::enu
           accessType == AQP::AT_UNIQUE_KEY);
 }
 
+static void
+explain_no_push(const char* msgfmt, ...)
+{
+  va_list args;
+  char wbuff[1024];
+  va_start(args,msgfmt);
+  (void) my_vsnprintf (wbuff, sizeof(wbuff), msgfmt, args);
+  va_end(args);
+  push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_QUERY_NOT_PUSHED, wbuff);
+} // explain_no_push();
+
 void
 ndb_pushed_builder_ctx::add_pushed(
                   const AQP::Table_access* table,
@@ -682,6 +725,7 @@ ndb_pushed_builder_ctx::add_pushed(
   DBUG_ASSERT(table_no < MAX_TABLES);
   m_join_scope.add(table);
   m_tables[table_no].op= query_op;
+  m_tables[table_no].m_maybe_pushable= 0; // Exclude from further pushing
   if (likely(parent))
   {
     uint parent_no= parent->get_access_no();
@@ -775,9 +819,69 @@ ndb_pushed_builder_ctx::add_parent_candi
   }
 } // ndb_pushed_builder_ctx::add_parent_candidate
 
+/**
+ * Set up the 'm_maybe_pushable' property of each table from the 'Abstract Query Plan' associated
+ * with this ndb_pushed_builder_ctx. A table may be possibly pushable as both:
+ * PUSHABLE_AS_CHILD and/or PUSHABLE_AS_PARENT.
+ * When a table is annotated as not PUSHABLE_AS_... it will be excluded from further
+ * pushability investigation for this specific table.
+ */
+void
+ndb_pushed_builder_ctx::init_pushability()
+{
+  for (uint i= 0; i< m_plan.get_access_count(); i++)
+  {
+    m_tables[i].m_maybe_pushable= 0;
+
+    const TABLE* const table= m_plan.get_table_access(i)->get_table();
+    if (table->file->ht != ndbcluster_hton)
+    {
+      m_tables[i].m_maybe_pushable= 0;
+      EXPLAIN_NO_PUSH("Table %s not in Cluster engine, not pushable", table->alias);
+      continue;
+    }
+    m_tables[i].m_maybe_pushable= static_cast<ha_ndbcluster*>(table->file)->get_pushability();
+  }
+
+  m_tables[0].m_maybe_pushable &= ~PUSHABLE_AS_CHILD;
+  m_tables[m_plan.get_access_count()-1].m_maybe_pushable &= ~PUSHABLE_AS_PARENT;
+} // ndb_pushed_builder_ctx::init_pushability()
+
+
+bool
+ndb_pushed_builder_ctx::is_pushable_as_parent(const AQP::Table_access* table)
+{
+  DBUG_ENTER("::is_pushable_as_parent");
+  uint table_no = table->get_access_no();
+  if ((m_tables[table_no].m_maybe_pushable & PUSHABLE_AS_PARENT) != PUSHABLE_AS_PARENT)
+  {
+    DBUG_PRINT("info", ("Table %d already reported 'not pushable_as_parent'", table_no));
+    DBUG_RETURN(false);
+  }
+
+  const AQP::enum_access_type access_type= table->get_access_type();
+  DBUG_ASSERT(access_type != AQP::AT_VOID);
+
+  if (access_type == AQP::AT_OTHER)
+  {
+    EXPLAIN_NO_PUSH("Table %s is not pushable, unknown access 'type'", table->get_table()->alias);
+    m_tables[table_no].m_maybe_pushable &= ~PUSHABLE_AS_PARENT;
+    DBUG_RETURN(false);
+  }
+  if (access_type == AQP::AT_MULTI_UNIQUE_KEY)
+  {
+    EXPLAIN_NO_PUSH("Table %s is not pushable, access type 'MULTI_UNIQUE_KEY' not implemented",
+                     table->get_table()->alias);
+    m_tables[table_no].m_maybe_pushable &= ~PUSHABLE_AS_PARENT;
+    DBUG_RETURN(false);
+  }
+
+  DBUG_RETURN(true);
+} // ndb_pushed_builder_ctx::is_pushable_as_parent()
+
 
 /***************************************************************
- *  field_ref_is_join_pushable()
+ *  is_pushable_as_child()
  *
  * Determines if the specified child ('table') can be appended to 
  * an existing chain of previously pushed join operations.
@@ -796,21 +900,48 @@ ndb_pushed_builder_ctx::add_parent_candi
  * join_items[] .
  ****************************************************************/
 bool
-ndb_pushed_builder_ctx::field_ref_is_join_pushable(
+ndb_pushed_builder_ctx::is_pushable_as_child(
                            const AQP::Table_access* table,
                            const Item* join_items[ndb_pushed_join::MAX_LINKED_KEYS+1],
                            const AQP::Table_access*& parent)
 {
-  DBUG_ENTER("field_ref_is_join_pushable");
+  DBUG_ENTER("is_pushable_as_child");
   uint tab_no = table->get_access_no();
   parent= NULL;
 
   DBUG_ASSERT (join_root() < table);
 
+  if ((m_tables[tab_no].m_maybe_pushable & PUSHABLE_AS_CHILD) != PUSHABLE_AS_CHILD)
+  {
+    DBUG_PRINT("info", ("Table %s already known 'not is_pushable_as_child'", table->get_table()->alias));
+    DBUG_RETURN(false);
+  }
+
+  AQP::enum_access_type access_type= table->get_access_type();
+  AQP::enum_access_type root_type= join_root()->get_access_type();
+
+  if (!(is_lookup_operation(access_type) || access_type==AQP::AT_ORDERED_INDEX_SCAN))
+  {
+    EXPLAIN_NO_PUSH("Can't push table %s as child, 'type' must be a REF access",
+                     table->get_table()->alias);
+    m_tables[tab_no].m_maybe_pushable &= ~PUSHABLE_AS_CHILD;
+    DBUG_RETURN(false);
+  }
+     
+  // Currently there is a limitation in not allowing LOOKUP - (index)SCAN operations
+  if (is_lookup_operation(root_type) && access_type==AQP::AT_ORDERED_INDEX_SCAN)
+  {
+    EXPLAIN_NO_PUSH("Push of table %s as scan-child with lookup-root %s not implemented",
+                     table->get_table()->alias, join_root()->get_table()->alias);
+    // 'table' may still be PUSHABLE_AS_CHILD with another parent
+    DBUG_RETURN(false);
+  }
+
   if (table->get_no_of_key_fields() > ndb_pushed_join::MAX_LINKED_KEYS)
   {
-    DBUG_PRINT("info", ("  'key_parts >= ndb_pushed_join::MAX_LINKED_KEYS', "
-                        "can't append table:%d", tab_no+1));
+    EXPLAIN_NO_PUSH("Can't push table %s as child; to many #REF'ed parent fields",
+                     table->get_table()->alias);
+    m_tables[tab_no].m_maybe_pushable &= ~PUSHABLE_AS_CHILD; // Permanently dissable
     DBUG_RETURN(false);
   }
 
@@ -841,7 +972,7 @@ ndb_pushed_builder_ctx::field_ref_is_joi
     {
       DBUG_PRINT("info", ("  Item type:%d not join_pushable -> can't append table:%d",
                   key_item->type(), tab_no+1));
-      DBUG_RETURN(false);
+      DBUG_RETURN(false);  // TODO, handle gracefull -> continue?
     }
 
     const Item_field* const key_item_field 
@@ -855,7 +986,7 @@ ndb_pushed_builder_ctx::field_ref_is_joi
         ->eq_def(table->get_key_part_info(key_part_no)->field))
     {
       DBUG_PRINT("info", ("Item_field does not have same definition as REF'ed key"));
-      DBUG_RETURN(false);
+      DBUG_RETURN(false);  // TODO, handle gracefull -> continue?
     }
 
     /**
@@ -956,26 +1087,37 @@ ndb_pushed_builder_ctx::field_ref_is_joi
     }
     else
     {
-      DBUG_PRINT("info", ("Item_field %s.%s is outside scope of pushable join",
-                  get_referred_table_access_name(key_item_field),
-                  get_referred_field_name(key_item_field)));
+      EXPLAIN_NO_PUSH("Can't push table %s as child of %s, "
+                      "Item_field '%s.%s' is outside scope of pushable join",
+                       table->get_table()->alias, join_root()->get_table()->alias,
+                       get_referred_table_access_name(key_item_field),
+                       get_referred_field_name(key_item_field));
       DBUG_RETURN(false);
     }
   } // for (uint key_part_no= 0 ...
 
   join_items[table->get_no_of_key_fields()]= NULL;
 
-  if (m_const_scope.contain(current_parents))
+  if (current_parents.is_clear_all())
+  {
+    EXPLAIN_NO_PUSH("Can't push table %s as child; no usable REF'ed parent FIELD_ITEMs",
+                     table->get_table()->alias);
+    m_tables[tab_no].m_maybe_pushable &= ~PUSHABLE_AS_CHILD;  // Permanently disable as child
+    DBUG_RETURN(false);
+  }
+  else if (m_const_scope.contain(current_parents))
    {
      // NOTE: This is a constant table wrt. this instance of the pushed join.
      //       It should be relatively simple to extend the SPJ block to 
      //       allow such tables to be included in the pushed join.
-    DBUG_PRINT("info", ("  Contain only const/param REFs, -> can't append(yet) as 'const' wrt. pushed join:%d\n",tab_no+1));
+    EXPLAIN_NO_PUSH("Can't push table %s as child of %s, their dependency is 'const'",
+                     table->get_table()->alias, join_root()->get_table()->alias);
     DBUG_RETURN(false);
   }
   else if (parents.is_clear_all())
   {
-    DBUG_PRINT("info", ("  No common parents, -> can't append table to pushed joins:%d\n",tab_no+1));
+    EXPLAIN_NO_PUSH("Can't push table %s as child of %s, no parents found within scope",
+                     table->get_table()->alias, join_root()->get_table()->alias);
     DBUG_RETURN(false);
   }
 
@@ -1022,17 +1164,20 @@ ndb_pushed_builder_ctx::field_ref_is_joi
       if (scan_descendant->get_join_type(parent) == AQP::JT_OUTER_JOIN)
       {
         DBUG_PRINT("info", ("  There are outer joins between parent and artificial parent -> can't append"));
+        EXPLAIN_NO_PUSH("Can't push table %s as child of %s, implementation limitations for outer joins",
+                     table->get_table()->alias, join_root()->get_table()->alias);
         DBUG_RETURN(false);
       }
       parent_no= descendant_no;
 //    parent= scan_descendant;
-      DBUG_PRINT("info", ("  Force artificial grandparent dependency through scan-child %s", scan_descendant->get_table()->alias));
+      DBUG_PRINT("info", ("  Force artificial grandparent dependency through scan-child %s",
+                         scan_descendant->get_table()->alias));
 
       if (scan_descendant && 
          table->get_join_type(scan_descendant) == AQP::JT_OUTER_JOIN)
       {
-        DBUG_PRINT("info", ("  Table scan %d is outer joined with scan-descendant %d, not pushable (yet)",
-                    tab_no, descendant_no));
+        EXPLAIN_NO_PUSH("Can't push table %s as child of %s, outer join with scan-descendant %s not implemented",
+                     table->get_table()->alias, join_root()->get_table()->alias, scan_descendant->get_table()->alias);
         DBUG_RETURN(false);
       }
     }
@@ -1047,13 +1192,17 @@ ndb_pushed_builder_ctx::field_ref_is_joi
       uint ancestor_no= parent_no;
       while (ancestor_no != MAX_TABLES)
       {
+        scan_ancestor= m_plan.get_table_access(ancestor_no);
         if (m_tables[ancestor_no].m_last_scan_descendant < MAX_TABLES)
         {
-          DBUG_PRINT("info", ("  Ancestor tab %d has scan descendants -> can't append", ancestor_no));
+          EXPLAIN_NO_PUSH("Can't push table %s as child of %s, "
+                          "implementation limitations due to bushy scan with %s indirect through %s",
+                           table->get_table()->alias, join_root()->get_table()->alias,
+                           m_plan.get_table_access(m_tables[ancestor_no].m_last_scan_descendant)->get_table()->alias,
+                           scan_ancestor->get_table()->alias);
           DBUG_RETURN(false);
         }
 
-        scan_ancestor= m_plan.get_table_access(ancestor_no);
         if (!is_lookup_operation(scan_ancestor->get_access_type()))
         {
           break; // As adding this scanop was prev. allowed, above ancestor can't be scan bushy
@@ -1067,8 +1216,8 @@ ndb_pushed_builder_ctx::field_ref_is_joi
       if (scan_ancestor && 
          table->get_join_type(scan_ancestor) == AQP::JT_OUTER_JOIN)
       {
-        DBUG_PRINT("info", ("  Table scan %d is outer joined with scan-ancestor %d, not pushable (yet)",
-                    tab_no, ancestor_no));
+        EXPLAIN_NO_PUSH("Can't push table %s as child of %s, outer join with scan-ancestor %s not implemented",
+                     table->get_table()->alias, join_root()->get_table()->alias, scan_ancestor->get_table()->alias);
         DBUG_RETURN(false);
       }
     }
@@ -1152,7 +1301,7 @@ ndb_pushed_builder_ctx::field_ref_is_joi
   } // substitute
 
   DBUG_RETURN(true);
-} // ndb_pushed_builder_ctx::field_ref_is_join_pushable
+} // ndb_pushed_builder_ctx::is_pushable_as_child
 
 
 /**
@@ -1210,44 +1359,20 @@ build_key_map(const NDBTAB* table, const
 } // build_key_map
 
 int
-ha_ndbcluster::make_pushed_join(AQP::Join_plan& plan,
+ha_ndbcluster::make_pushed_join(ndb_pushed_builder_ctx& context,
                                 const AQP::Table_access* const join_root)
 {
   DBUG_ENTER("make_pushed_join");
 
-  if (m_pushed_join_member)  // Already member of another pushed join.
-    DBUG_RETURN(0);
-
-  const AQP::enum_access_type access_type= join_root->get_access_type();
-  DBUG_ASSERT(access_type != AQP::AT_VOID);
+  DBUG_ASSERT (context.is_pushable_as_parent(join_root));
+  context.set_root(join_root);
+  const AQP::Join_plan& plan= context.plan();
 
-  DBUG_PRINT("enter", ("Investigating from table %d as root", join_root->get_access_no()));
-  if (access_type == AQP::AT_OTHER)
-  {
-    DBUG_PRINT("info", ("join_root->get_access_type() == AQP::AT_OTHER"
-                        " -> not pushable"));
-    DBUG_RETURN(0);
-  }
-  if (access_type == AQP::AT_MULTI_UNIQUE_KEY)
-  {
-    DBUG_PRINT("info", ("join_root->get_access_type() == AQP::AT_MULTI_KEY"
-                        " -> not (yet) pushable"));
-    DBUG_RETURN(0);
-  }
-  if (uses_blob_value(table->read_set))
-  {
-    DBUG_PRINT("info", ("'read_set' contain BLOB columns -> not pushable"));
-    DBUG_RETURN(0);
-  }
-  if (m_user_defined_partitioning)
-  {
-    DBUG_PRINT("info", ("Tables has user defined partioning -> not pushable"));
-    DBUG_RETURN(0);
-  }
-
-  DBUG_PRINT("info", ("join_root is pushable:"));
+  DBUG_PRINT("enter", ("Table %d as root is pushable", join_root->get_access_no()));
   DBUG_EXECUTE("info", join_root->dbug_print(););
 
+  const AQP::enum_access_type access_type= join_root->get_access_type();
+
   /**
    * Past this point we know at least join_root to be join pushable 
    * as parent operation. Search for tables to be appendable as child 
@@ -1256,7 +1381,6 @@ ha_ndbcluster::make_pushed_join(AQP::Joi
    * appendable child.
    */
   NdbQueryBuilder builder(*m_thd_ndb->ndb);
-  ndb_pushed_builder_ctx context(plan, join_root);
 
   uint push_cnt= 0;
   uint fld_refs= 0;
@@ -1270,39 +1394,7 @@ ha_ndbcluster::make_pushed_join(AQP::Joi
     const AQP::Table_access* const join_tab= plan.get_table_access(join_cnt);
     const NdbQueryOperationDef* query_op= NULL;
 
-    if (join_tab->get_table()->file->ht != ht)
-    {
-      DBUG_PRINT("info", ("Table %d not same SE, not pushable", join_cnt));
-      continue;
-    }
-
-    const ha_ndbcluster* const handler=
-      static_cast<ha_ndbcluster*>(join_tab->get_table()->file);
-
-    if (handler->m_pushed_join_member)
-    {
-      DBUG_PRINT("info", ("Table %d already contained in another pushed join", join_cnt));
-      continue;
-    }
-    if (handler->uses_blob_value(join_tab->get_table()->read_set))
-    {
-      DBUG_PRINT("info", ("Table %d, 'read_set' contains BLOBs, not pushable", join_cnt));
-      continue;
-    }
-    if (handler->m_user_defined_partitioning)
-    {
-      DBUG_PRINT("info", ("Table %d has user defined partioning, not pushable", join_cnt));
-      continue;
-    }
-    AQP::enum_access_type child_type= join_tab->get_access_type();
-    if (!(is_lookup_operation(child_type)  ||
-          // Currently there is a limitation in not allowing LOOKUP - (index)SCAN operations
-          (child_type==AQP::AT_ORDERED_INDEX_SCAN && !is_lookup_operation(access_type))))
-    {
-      DBUG_PRINT("info", ("Table %d not a pushable access type", join_cnt));
-      continue;
-    }
-    if (!context.field_ref_is_join_pushable(join_tab, join_items, join_parent))
+    if (!context.is_pushable_as_child(join_tab, join_items, join_parent))
     {
       DBUG_PRINT("info", ("Table %d not REF-joined, not pushable", join_cnt));
       continue;
@@ -1386,6 +1478,10 @@ ha_ndbcluster::make_pushed_join(AQP::Joi
     DBUG_PRINT("info", ("Appending child, join_cnt:%d, key_parts:%d", push_cnt, 
                         join_tab->get_no_of_key_fields()));
 
+    DBUG_ASSERT(join_tab->get_table()->file->ht == ht);
+    const ha_ndbcluster* const handler=
+      static_cast<ha_ndbcluster*>(join_tab->get_table()->file);
+
     KEY *key= &handler->table->key_info[join_tab->get_index_no()];
 
     const NdbQueryOperand* linked_key[ndb_pushed_join::MAX_LINKED_KEYS]= {NULL};
@@ -1428,7 +1524,7 @@ ha_ndbcluster::make_pushed_join(AQP::Joi
         if (field->is_real_null())
         {
           DBUG_PRINT("info", ("NULL constValues in key -> not pushable"));
-          DBUG_RETURN(0);
+          DBUG_RETURN(0);  // TODO, handle gracefull -> continue?
         }
         const uchar* const ptr= (field->real_type() == MYSQL_TYPE_VARCHAR)
                 ? field->ptr + ((Field_varstring*)field)->length_bytes
@@ -1472,7 +1568,7 @@ ha_ndbcluster::make_pushed_join(AQP::Joi
           if (unlikely(fld_refs >= ndb_pushed_join::MAX_REFERRED_FIELDS))
           {
             DBUG_PRINT("info", ("Too many Field refs ( >= MAX_REFERRED_FIELDS) encountered"));
-            DBUG_RETURN(0);
+            DBUG_RETURN(0);  // TODO, handle gracefull -> continue?
           }
           referred_fields[fld_refs++]= field_item->field;
           linked_key[map[i]]= builder.paramValue();
@@ -1556,6 +1652,25 @@ ha_ndbcluster::make_pushed_join(AQP::Joi
 } // ha_ndbcluster::make_pushed_join()
 
 
+//ndb_pushed_builder_ctx::enum_pushability
+int
+ha_ndbcluster::get_pushability() const
+{
+  if (uses_blob_value(table->read_set))
+  {
+    EXPLAIN_NO_PUSH("Table %s not pushable, 'read_set' contain BLOB columns", table->alias);
+    return 0;
+  }
+  if (m_user_defined_partitioning)
+  {
+    EXPLAIN_NO_PUSH("Table %s not pushable, has user defined partioning", table->alias);
+    return 0;
+  }
+  return (ndb_pushed_builder_ctx::PUSHABLE_AS_CHILD | 
+          ndb_pushed_builder_ctx::PUSHABLE_AS_PARENT);
+} // ha_ndbcluster::get_pushability()
+
+
 static
 int ndbcluster_make_pushed_join(handlerton *hton, THD* thd,
                                 AQP::Join_plan* plan)
@@ -1565,15 +1680,16 @@ int ndbcluster_make_pushed_join(handlert
   if (!THDVAR(thd, join_pushdown))
     DBUG_RETURN(0);
 
+  ndb_pushed_builder_ctx context(*plan);
   for (uint i= 0; i < plan->get_access_count()-1; i++)
   {
     const AQP::Table_access* const join_root=  plan->get_table_access(i);
-    if (join_root->get_table()->file->ht == ndbcluster_hton)
+    if (context.is_pushable_as_parent(join_root))
     {
       ha_ndbcluster* const handler=
         static_cast<ha_ndbcluster*>(join_root->get_table()->file);
 
-      int error= handler->make_pushed_join(*plan,join_root);
+      int error= handler->make_pushed_join(context,join_root);
       if (unlikely(error))
       {
         handler->print_error(error, MYF(0));

=== modified file 'sql/ha_ndbcluster.h'
--- a/sql/ha_ndbcluster.h	2010-10-15 21:04:14 +0000
+++ b/sql/ha_ndbcluster.h	2010-10-26 09:21:10 +0000
@@ -417,6 +417,8 @@ class ha_ndbcluster: public handler
   ha_ndbcluster(handlerton *hton, TABLE_SHARE *table);
   ~ha_ndbcluster();
 
+  int get_pushability() const;
+
   int ha_initialise();
   void column_bitmaps_signal(uint sig_type);
   int open(const char *name, int mode, uint test_if_locked);
@@ -588,7 +590,7 @@ static void set_tabname(const char *path
  */
   void cond_pop();
 
-  int make_pushed_join(AQP::Join_plan& plan,
+  int make_pushed_join(class ndb_pushed_builder_ctx& context,
                        const AQP::Table_access* const join_root);
 
   bool test_push_flag(enum ha_push_flag flag) const;

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2010-06-16 12:04:53 +0000
+++ b/sql/share/errmsg.txt	2010-10-26 09:21:10 +0000
@@ -6221,3 +6221,5 @@ ER_SLAVE_CONVERSION_FAILED
   eng "Column %d of table '%-.192s.%-.192s' cannot be converted from type '%-.32s' to type '%-.32s'"
 ER_SLAVE_CANT_CREATE_CONVERSION
   eng "Can't create conversion table for table '%-.192s.%-.192s'"
+WARN_QUERY_NOT_PUSHED
+  eng "Table can't be part of pushed join" 

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.1-telco-7.0-spj-scan-vs-scan branch(ole.john.aske:3322 to 3323) Ole John Aske26 Oct