List:Commits« Previous MessageNext Message »
From:Jonas Oreland Date:May 11 2010 8:12pm
Subject:bzr commit into mysql-5.1-telco-7.0-spj branch (jonas:3152)
View as plain text  
#At file:///home/jonas/src/70-spj/ based on revid:jonas@stripped

 3152 Jonas Oreland	2010-05-11
      ndb spj - add HugoQueryBuilder, HugoQueries and hugoJoin

    added:
      storage/ndb/test/include/HugoQueries.hpp
      storage/ndb/test/include/HugoQueryBuilder.hpp
      storage/ndb/test/ndbapi/testSpj.cpp
      storage/ndb/test/src/HugoQueries.cpp
      storage/ndb/test/src/HugoQueryBuilder.cpp
      storage/ndb/test/tools/hugoJoin.cpp
    modified:
      storage/ndb/include/ndbapi/NdbDictionary.hpp
      storage/ndb/src/ndbapi/NdbDictionary.cpp
      storage/ndb/test/include/HugoCalculator.hpp
      storage/ndb/test/include/NDBT_ResultRow.hpp
      storage/ndb/test/include/NDBT_Test.hpp
      storage/ndb/test/ndbapi/CMakeLists.txt
      storage/ndb/test/ndbapi/Makefile.am
      storage/ndb/test/run-test/daily-basic-tests.txt
      storage/ndb/test/src/CMakeLists.txt
      storage/ndb/test/src/Makefile.am
      storage/ndb/test/src/NDBT_Test.cpp
      storage/ndb/test/tools/CMakeLists.txt
      storage/ndb/test/tools/Makefile.am
=== modified file 'storage/ndb/include/ndbapi/NdbDictionary.hpp'
--- a/storage/ndb/include/ndbapi/NdbDictionary.hpp	2010-04-29 14:52:05 +0000
+++ b/storage/ndb/include/ndbapi/NdbDictionary.hpp	2010-05-11 20:12:03 +0000
@@ -615,6 +615,12 @@ public:
 
     int getBlobVersion() const; // NDB_BLOB_V1 or NDB_BLOB_V2
     void setBlobVersion(int blobVersion); // default NDB_BLOB_V2
+
+    /**
+     * 0 = yes
+     * -1 = no
+     */
+    int isBindable(const Column&) const;
 #endif
     
   private:

=== modified file 'storage/ndb/src/ndbapi/NdbDictionary.cpp'
--- a/storage/ndb/src/ndbapi/NdbDictionary.cpp	2010-04-30 12:59:48 +0000
+++ b/storage/ndb/src/ndbapi/NdbDictionary.cpp	2010-05-11 20:12:03 +0000
@@ -459,6 +459,25 @@ NdbDictionary::Column::getIndexSourced()
   return m_impl.m_indexSourced;
 }
 
+int
+NdbDictionary::Column::isBindable(const NdbDictionary::Column & col) const
+{
+  const NdbColumnImpl& parentColumn = col.m_impl;
+
+  if (m_impl.m_type      != parentColumn.m_type ||
+      m_impl.m_precision != parentColumn.m_precision ||
+      m_impl.m_scale     != parentColumn.m_scale ||
+      m_impl.m_length    != parentColumn.m_length ||
+      m_impl.m_cs        != parentColumn.m_cs)
+    return -1;
+
+  if (m_impl.m_type == NdbDictionary::Column::Blob ||
+      m_impl.m_type == NdbDictionary::Column::Text)
+    return -1;
+
+  return 0; // ok
+}
+
 /*****************************************************************
  * Table facade
  */

=== modified file 'storage/ndb/test/include/HugoCalculator.hpp'
--- a/storage/ndb/test/include/HugoCalculator.hpp	2009-10-06 10:39:02 +0000
+++ b/storage/ndb/test/include/HugoCalculator.hpp	2010-05-11 20:12:03 +0000
@@ -44,6 +44,8 @@ public:
   int getUpdatesValue(NDBT_ResultRow* const pRow) const;
   int isIdCol(int colId) { return m_idCol == colId; };
   int isUpdateCol(int colId){ return m_updatesCol == colId; };
+
+  const NdbDictionary::Table& getTable() const { return m_tab;}
 private:
   const NdbDictionary::Table& m_tab;
   int m_idCol;

=== added file 'storage/ndb/test/include/HugoQueries.hpp'
--- a/storage/ndb/test/include/HugoQueries.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/include/HugoQueries.hpp	2010-05-11 20:12:03 +0000
@@ -0,0 +1,63 @@
+/*
+   Copyright (C) 2003 MySQL AB
+    All rights reserved. Use is subject to license terms.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#ifndef HUGO_QUERIES_HPP
+#define HUGO_QUERIES_HPP
+
+
+#include <NDBT.hpp>
+#include <HugoCalculator.hpp>
+#include <HugoOperations.hpp>
+#include <NdbQueryOperation.hpp>
+
+class HugoQueries
+{
+public:
+  HugoQueries(const NdbQueryDef & query);
+  virtual ~HugoQueries();
+
+  int runLookupQuery(Ndb*, int records, int batchsize = 1);
+  int runScanQuery(Ndb*,
+                   int abort = 0,
+                   int parallelism = 0,
+                   int scan_flags = 0);
+
+  static int equalForParameters(char * buf,
+                                HugoCalculator&,
+                                NdbQueryParamValue params[],
+                                int rowNo);
+  static int getValueForQueryOp(NdbQueryOperation* pOp, NDBT_ResultRow* pRow);
+
+
+  void allocRows(int batch);
+protected:
+
+  const NdbQueryDef* m_query_def;
+  struct Op
+  {
+    const NdbQueryOperationDef* m_query_op;
+    Vector<NDBT_ResultRow*> m_rows;
+    HugoCalculator * m_calc;
+  };
+
+  Vector<Op> m_ops;
+  int m_retryMax;
+};
+
+#endif
+

=== added file 'storage/ndb/test/include/HugoQueryBuilder.hpp'
--- a/storage/ndb/test/include/HugoQueryBuilder.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/include/HugoQueryBuilder.hpp	2010-05-11 20:12:03 +0000
@@ -0,0 +1,155 @@
+/*
+   Copyright (C) 2003 MySQL AB
+    All rights reserved. Use is subject to license terms.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#ifndef HUGO_QUERY_BUILDER_HPP
+#define HUGO_QUERY_BUILDER_HPP
+
+#include <NDBT.hpp>
+#include <Vector.hpp>
+#include <NdbQueryBuilder.hpp>
+
+class HugoQueryBuilder {
+public:
+
+  /**
+   * Options that affects what kind of query is built
+   */
+  enum QueryOption
+  {
+    /**
+     * Query should be a lookup
+     */
+    O_LOOKUP = 0x1,
+
+    /**
+     * Query should be a scan
+     */
+    O_SCAN = 0x2,
+
+    /**
+     * Query might use primary key index
+     */
+    O_PK_INDEX = 0x4,
+
+    /**
+     * Query might use unique index
+     */
+    O_UNIQUE_INDEX = 0x8,
+
+    /**
+     * Query might use ordered index
+     */
+    O_ORDERED_INDEX = 0x10,
+
+    /**
+     * Query might table scan
+     */
+    O_TABLE_SCAN = 0x20,
+
+    /**
+     * If not any options set, random query qill be created
+     */
+    O_RANDOM = 0
+  };
+  typedef Uint64 OptionMask;
+
+  HugoQueryBuilder(Ndb* ndb, const NdbDictionary::Table**tabptr, OptionMask om){
+    init();
+    for (; * tabptr != 0; tabptr++)
+      addTable(ndb, * tabptr);
+    setOptionMask(om);
+    fixOptions();
+  }
+  HugoQueryBuilder(Ndb* ndb, const NdbDictionary::Table* tab, QueryOption o) {
+    init();
+    addTable(ndb, tab);
+    setOption(o);
+    fixOptions();
+  }
+  virtual ~HugoQueryBuilder();
+
+  void setMinJoinLevel(int level) { m_joinLevel[0] = level;}
+  int getMinJoinLevel() const { return m_joinLevel[0];}
+  void setMaxJoinLevel(int level) { m_joinLevel[1] = level;}
+  int getMaxJoinLevel() const { return m_joinLevel[1];}
+
+  void setJoinLevel(int level) { setMinJoinLevel(level);setMaxJoinLevel(level);}
+  int getJoinLevel() const;
+
+  void addTable(Ndb*, const NdbDictionary::Table*);
+  void removeTable(const NdbDictionary::Table*);
+
+  void setOption(QueryOption o) { m_options |= (OptionMask)o;}
+  void clearOption(QueryOption o) const { m_options &= ~(OptionMask)o;}
+  bool testOption(QueryOption o) const { return (m_options & o) != 0;}
+
+  OptionMask getOptionMask() const { return m_options;}
+  void setOptionMask(OptionMask om) { m_options = om;}
+
+  const NdbQueryDef * createQuery(Ndb*, bool takeOwnership = false);
+
+private:
+  struct TableDef
+  {
+    const NdbDictionary::Table * m_table;
+    Vector<const NdbDictionary::Index*> m_unique_indexes;
+    Vector<const NdbDictionary::Index*> m_ordered_indexes;
+  };
+
+  void init();
+  mutable OptionMask m_options;
+  int m_joinLevel[2]; // min/max
+  Vector<TableDef> m_tables;
+  Vector<const NdbQueryDef*> m_queries;
+
+  // get random table
+  struct TableDef getTable() const;
+
+  struct OpIdx
+  {
+    NdbQueryOperationDef::Type m_type;
+    const NdbDictionary::Table * m_table;
+    const NdbDictionary::Index * m_index;
+  };
+  OpIdx getOp() const;
+
+  struct Op
+  {
+    int m_parent;
+    int m_idx;
+    NdbQueryOperationDef * m_op;
+  };
+
+  Vector<Op> m_query; // Query built sofar
+
+  /**
+   * Check if all column in cols can be bound to a column in tables in
+   *   ops
+   */
+  static bool checkBindable(Vector<const NdbDictionary::Column*> cols,
+                            Vector<Op> ops);
+
+  Vector<Op> getParents(OpIdx); //
+  NdbQueryOperand * createLink(NdbQueryBuilder&, const NdbDictionary::Column*,
+                               Vector<Op> & parents);
+  NdbQueryOperationDef* createOp(NdbQueryBuilder&);
+
+  void fixOptions();
+};
+
+#endif

=== modified file 'storage/ndb/test/include/NDBT_ResultRow.hpp'
--- a/storage/ndb/test/include/NDBT_ResultRow.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/test/include/NDBT_ResultRow.hpp	2010-05-11 20:12:03 +0000
@@ -43,6 +43,8 @@ public:
   bool operator!=(const NDBT_ResultRow& other) const { 
     return ! (*this == other);
   }
+
+  const NdbDictionary::Table& getTable() const { return m_table;}
   
 private:
   int cols;

=== modified file 'storage/ndb/test/include/NDBT_Test.hpp'
--- a/storage/ndb/test/include/NDBT_Test.hpp	2010-03-11 09:44:08 +0000
+++ b/storage/ndb/test/include/NDBT_Test.hpp	2010-05-11 20:12:03 +0000
@@ -75,6 +75,7 @@ public:
 
   void decProperty(const char *);
   void incProperty(const char *);
+  Uint32 casProperty(const char *, Uint32 oldValue, Uint32 newValue);
 
   // Communicate with other tests
   void stopTest();

=== modified file 'storage/ndb/test/ndbapi/CMakeLists.txt'
--- a/storage/ndb/test/ndbapi/CMakeLists.txt	2010-04-29 14:52:05 +0000
+++ b/storage/ndb/test/ndbapi/CMakeLists.txt	2010-05-11 20:12:03 +0000
@@ -67,6 +67,7 @@ ADD_EXECUTABLE(testSRBank testSRBank.cpp
 ADD_EXECUTABLE(test_event_merge test_event_merge.cpp)
 Add_EXECUTABLE(testNdbinfo testNdbinfo.cpp)
 ADD_EXECUTABLE(testNativeDefault testNativeDefault.cpp)
+ADD_EXECUTABLE(testSpj testSpj.cpp)
 ##testDict_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
 ##testIndex_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
 ##testSystemRestart_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
@@ -83,5 +84,5 @@ INSTALL(TARGETS create_all_tabs create_t
                 testSystemRestart testTimeout testTransactions
                 testDeadlock test_event ndbapi_slow_select testReadPerf
                 testLcp testPartitioning testBitfield DbCreate
-                DbAsyncGenerator test_event_merge testNdbinfo 
+                DbAsyncGenerator test_event_merge testNdbinfo testSpj
         DESTINATION bin)

=== modified file 'storage/ndb/test/ndbapi/Makefile.am'
--- a/storage/ndb/test/ndbapi/Makefile.am	2010-04-29 14:52:05 +0000
+++ b/storage/ndb/test/ndbapi/Makefile.am	2010-05-11 20:12:03 +0000
@@ -18,6 +18,7 @@ SUBDIRS = bank
 EXTRA_DIST = CMakeLists.txt
 
 ndbtest_PROGRAMS = \
+testSpj \
 flexBench \
 drop_all_tabs \
 create_all_tabs \
@@ -87,6 +88,7 @@ flexTT_SOURCES = flexTT.cpp
 #flex_bench_mysql_SOURCES = flex_bench_mysql.cpp
 testBackup_SOURCES = testBackup.cpp
 testBasic_SOURCES = testBasic.cpp
+testSpj_SOURCES = testSpj.cpp
 testBasicAsynch_SOURCES = testBasicAsynch.cpp
 testBlobs_SOURCES = testBlobs.cpp
 testDataBuffers_SOURCES = testDataBuffers.cpp

=== added file 'storage/ndb/test/ndbapi/testSpj.cpp'
--- a/storage/ndb/test/ndbapi/testSpj.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/ndbapi/testSpj.cpp	2010-05-11 20:12:03 +0000
@@ -0,0 +1,285 @@
+/*
+   Copyright (C) 2003 MySQL AB
+    All rights reserved. Use is subject to license terms.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <Bitmask.hpp>
+#include <random.h>
+#include <HugoQueryBuilder.hpp>
+#include <HugoQueries.hpp>
+
+int
+runLoadTable(NDBT_Context* ctx, NDBT_Step* step)
+{
+  int records = ctx->getNumRecords();
+  HugoTransactions hugoTrans(*ctx->getTab());
+  if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+    return NDBT_FAILED;
+  }
+  return NDBT_OK;
+}
+
+int
+runClearTable(NDBT_Context* ctx, NDBT_Step* step)
+{
+  UtilTransactions utilTrans(*ctx->getTab());
+  if (utilTrans.clearTable(GETNDB(step)) != 0){
+    return NDBT_FAILED;
+  }
+  return NDBT_OK;
+}
+
+static
+void
+addMask(NDBT_Context* ctx, Uint32 val, const char * name)
+{
+  Uint32 oldValue = 0;
+  do
+  {
+    oldValue = ctx->getProperty(name);
+    Uint32 newValue = oldValue | val;
+    if (ctx->casProperty(name, oldValue, newValue) == oldValue)
+      return;
+    NdbSleep_MilliSleep(5);
+  } while (true);
+}
+
+int
+runLookupJoin(NDBT_Context* ctx, NDBT_Step* step){
+  int loops = ctx->getNumLoops();
+  int joinlevel = ctx->getProperty("JoinLevel", 3);
+  int records = ctx->getNumRecords();
+  int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
+  Uint32 stepNo = step->getStepNo();
+
+  int i = 0;
+  HugoQueryBuilder qb(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_LOOKUP);
+  qb.setJoinLevel(joinlevel);
+  const NdbQueryDef * query = qb.createQuery(GETNDB(step));
+  HugoQueries hugoTrans(*query);
+  while ((i<loops || until_stopped) && !ctx->isTestStopped())
+  {
+    g_info << i << ": ";
+    if (hugoTrans.runLookupQuery(GETNDB(step), records))
+    {
+      g_info << endl;
+      return NDBT_FAILED;
+    }
+    addMask(ctx, (1 << stepNo), "Running");
+    i++;
+  }
+  g_info << endl;
+  ctx->stopTest();
+  return NDBT_OK;
+}
+
+int
+runScanJoin(NDBT_Context* ctx, NDBT_Step* step){
+  int loops = ctx->getNumLoops();
+  int joinlevel = ctx->getProperty("JoinLevel", 3);
+  int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
+  Uint32 stepNo = step->getStepNo();
+
+  int i = 0;
+  HugoQueryBuilder qb(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_SCAN);
+  qb.setJoinLevel(joinlevel);
+  const NdbQueryDef * query = qb.createQuery(GETNDB(step));
+  HugoQueries hugoTrans(* query);
+  while ((i<loops || until_stopped) && !ctx->isTestStopped())
+  {
+    g_info << i << ": ";
+    if (hugoTrans.runScanQuery(GETNDB(step)))
+    {
+      g_info << endl;
+      return NDBT_FAILED;
+    }
+    addMask(ctx, (1 << stepNo), "Running");
+    i++;
+  }
+  g_info << endl;
+  ctx->stopTest();
+  return NDBT_OK;
+}
+
+int
+runJoin(NDBT_Context* ctx, NDBT_Step* step){
+  int loops = ctx->getNumLoops();
+  int joinlevel = ctx->getProperty("JoinLevel", 3);
+  int records = ctx->getNumRecords();
+  int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
+  Uint32 stepNo = step->getStepNo();
+
+  int i = 0;
+  HugoQueryBuilder qb1(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_SCAN);
+  HugoQueryBuilder qb2(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_LOOKUP);
+  qb1.setJoinLevel(joinlevel);
+  qb2.setJoinLevel(joinlevel);
+  const NdbQueryDef * q1 = qb1.createQuery(GETNDB(step));
+  const NdbQueryDef * q2 = qb2.createQuery(GETNDB(step));
+  HugoQueries hugoTrans1(* q1);
+  HugoQueries hugoTrans2(* q2);
+  while ((i<loops || until_stopped) && !ctx->isTestStopped())
+  {
+    g_info << i << ": ";
+    if (hugoTrans1.runScanQuery(GETNDB(step)))
+    {
+      g_info << endl;
+      return NDBT_FAILED;
+    }
+    if (hugoTrans2.runLookupQuery(GETNDB(step), records))
+    {
+      g_info << endl;
+      return NDBT_FAILED;
+    }
+    i++;
+    addMask(ctx, (1 << stepNo), "Running");
+  }
+  g_info << endl;
+  ctx->stopTest();
+  return NDBT_OK;
+}
+
+int
+runRestarter(NDBT_Context* ctx, NDBT_Step* step)
+{
+  int result = NDBT_OK;
+  int loops = ctx->getNumLoops();
+  int waitprogress = ctx->getProperty("WaitProgress", (unsigned)0);
+  int randnode = ctx->getProperty("RandNode", (unsigned)0);
+  NdbRestarter restarter;
+  int i = 0;
+  int lastId = 0;
+
+  if (restarter.getNumDbNodes() < 2){
+    ctx->stopTest();
+    return NDBT_OK;
+  }
+
+  if(restarter.waitClusterStarted() != 0){
+    g_err << "Cluster failed to start" << endl;
+    return NDBT_FAILED;
+  }
+
+  loops *= (restarter.getNumDbNodes() > 2 ? 2 : restarter.getNumDbNodes());
+  if (loops < restarter.getNumDbNodes())
+    loops = restarter.getNumDbNodes();
+
+  while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+
+    int id = lastId % restarter.getNumDbNodes();
+    if (randnode == 1)
+    {
+      id = rand() % restarter.getNumDbNodes();
+    }
+    int nodeId = restarter.getDbNodeId(id);
+    ndbout << "Restart node " << nodeId << endl;
+
+    Uint32 running = ctx->getProperty("Running", (Uint32)0);
+    if(restarter.restartOneDbNode(nodeId, false, true, true) != 0){
+      g_err << "Failed to restartNextDbNode" << endl;
+      result = NDBT_FAILED;
+      break;
+    }
+    ctx->setProperty("Running", (Uint32)0);
+
+    if (restarter.waitNodesNoStart(&nodeId, 1))
+    {
+      g_err << "Failed to waitNodesNoStart" << endl;
+      result = NDBT_FAILED;
+      break;
+    }
+
+    if (waitprogress)
+    {
+      Uint32 maxwait = 30;
+      ndbout_c("running: 0x%.8x", running);
+      for (Uint32 checks = 0; checks < 3; checks++)
+      {
+        for (; maxwait != 0; maxwait--)
+        {
+          if (ctx->getProperty("Running", (Uint32)0) == running)
+            goto ok;
+          NdbSleep_SecSleep(1);
+        }
+        g_err << "No progress made!!" << endl;
+        return NDBT_FAILED;
+    ok:
+        g_err << "Progress made!! " << endl;
+        (void)1;
+      }
+    }
+
+    if (restarter.startNodes(&nodeId, 1))
+    {
+      g_err << "Failed to start node" << endl;
+      result = NDBT_FAILED;
+      break;
+    }
+
+    if(restarter.waitClusterStarted() != 0){
+      g_err << "Cluster failed to start" << endl;
+      result = NDBT_FAILED;
+      break;
+    }
+
+    lastId++;
+    i++;
+  }
+
+  ctx->stopTest();
+
+  return result;
+}
+
+NDBT_TESTSUITE(testSpj);
+TESTCASE("LookupJoin", ""){
+  INITIALIZER(runLoadTable);
+  STEP(runLookupJoin);
+  VERIFIER(runClearTable);
+}
+TESTCASE("ScanJoin", ""){
+  INITIALIZER(runLoadTable);
+  STEP(runScanJoin);
+  FINALIZER(runClearTable);
+}
+TESTCASE("MixedJoin", ""){
+  INITIALIZER(runLoadTable);
+  STEPS(runJoin, 6);
+  FINALIZER(runClearTable);
+}
+TESTCASE("NF_Join", ""){
+  TC_PROPERTY("UntilStopped", 1);
+  TC_PROPERTY("WaitProgress", 20);
+  INITIALIZER(runLoadTable);
+  STEPS(runJoin, 6);
+  STEP(runRestarter);
+  FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testSpj);
+
+
+int main(int argc, const char** argv){
+  ndb_init();
+  NDBT_TESTSUITE_INSTANCE(testSpj);
+  return testSpj.execute(argc, argv);
+}
+

=== modified file 'storage/ndb/test/run-test/daily-basic-tests.txt'
--- a/storage/ndb/test/run-test/daily-basic-tests.txt	2010-04-29 14:52:05 +0000
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt	2010-05-11 20:12:03 +0000
@@ -88,6 +88,18 @@ cmd: testBasic
 args: -n DeleteRead
 
 max-time: 500
+cmd: testSpj
+args: -n LookupJoin
+
+max-time: 500
+cmd: testSpj
+args: -n ScanJoin
+
+max-time: 500
+cmd: testSpj
+args: -n MixedJoin
+
+max-time: 500
 cmd: testBasic
 args: -n PkReadAndLocker T6 D1 D2
 
@@ -633,6 +645,10 @@ max-time: 2500
 cmd: testIndex
 args: -n NFNR2_O T6 T13 
 
+max-time: 2500
+cmd: testSpj
+args: -n NF_Join T6 T13
+
 #
 # DICT TESTS
 max-time: 500

=== modified file 'storage/ndb/test/src/CMakeLists.txt'
--- a/storage/ndb/test/src/CMakeLists.txt	2009-11-18 11:05:02 +0000
+++ b/storage/ndb/test/src/CMakeLists.txt	2010-05-11 20:12:03 +0000
@@ -47,4 +47,6 @@ ADD_LIBRARY(ndbNDBT STATIC
 	SocketInputStream2.cpp
         AtrtClient.cpp
         DbUtil.cpp
+	HugoQueryBuilder.cpp
+        HugoQueries.cpp
 )

=== added file 'storage/ndb/test/src/HugoQueries.cpp'
--- a/storage/ndb/test/src/HugoQueries.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/src/HugoQueries.cpp	2010-05-11 20:12:03 +0000
@@ -0,0 +1,316 @@
+/*
+   Copyright (C) 2003 MySQL AB
+    All rights reserved. Use is subject to license terms.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include "HugoQueries.hpp"
+#include <NDBT_Stats.hpp>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbQueryOperation.hpp>
+
+HugoQueries::HugoQueries(const NdbQueryDef & query)
+{
+  m_retryMax = 100;
+  m_query_def = &query;
+
+  for (Uint32 i = 0; i<query.getNoOfOperations(); i++)
+  {
+    struct Op op;
+    op.m_query_op = query.getQueryOperation(i);
+    op.m_calc = 0;
+    if (op.m_query_op->getTable())
+    {
+      op.m_calc = new HugoCalculator(* op.m_query_op->getTable());
+    }
+    m_ops.push_back(op);
+  }
+}
+
+HugoQueries::~HugoQueries()
+{
+  for (size_t o = 0; o<m_ops.size(); o++)
+  {
+    while (m_ops[o].m_rows.size())
+    {
+      delete m_ops[o].m_rows.back();
+      m_ops[o].m_rows.erase(m_ops[o].m_rows.size() - 1);
+    }
+    if (m_ops[o].m_calc)
+      delete m_ops[o].m_calc;
+  }
+}
+
+void
+HugoQueries::allocRows(int batch)
+{
+  for (size_t o = 0; o<m_ops.size(); o++)
+  {
+    const NdbQueryOperationDef * pOp =m_query_def->getQueryOperation((Uint32)o);
+    const NdbDictionary::Table* tab = pOp->getTable();
+
+    if (tab)
+    {
+      while (m_ops[o].m_rows.size() < (size_t)batch)
+      {
+        m_ops[o].m_rows.push_back(new NDBT_ResultRow(* tab));
+      }
+    }
+  }
+}
+
+int
+HugoQueries::equalForParameters(char * buf,
+                                HugoCalculator& calc,
+                                NdbQueryParamValue params[],
+                                int rowNo)
+{
+  Uint32 no = 0;
+  const NdbDictionary::Table & tab = calc.getTable();
+  for (int i = 0; i<tab.getNoOfColumns(); i++)
+  {
+    const NdbDictionary::Column* attr = tab.getColumn(i);
+    if (attr->getPrimaryKey())
+    {
+      Uint32 len = attr->getSizeInBytes();
+      Uint32 real_len;
+      bzero(buf, len);
+      calc.calcValue((Uint32)rowNo, i, 0, buf, len, &real_len);
+      params[no++]= NdbQueryParamValue((void*)buf);
+      buf += len;
+    }
+  }
+  return 0;
+}
+
+int
+HugoQueries::getValueForQueryOp(NdbQueryOperation* pOp, NDBT_ResultRow * pRow)
+{
+  const NdbDictionary::Table & tab = pRow->getTable();
+  for(int a = 0; a<tab.getNoOfColumns(); a++)
+  {
+    pRow->attributeStore(a) = pOp->getValue(tab.getColumn(a)->getName());
+  }
+
+  return 0;
+}
+
+int
+HugoQueries::runLookupQuery(Ndb* pNdb,
+                            int records,
+                            int batch)
+{
+  int r = 0;
+  int retryAttempt = 0;
+
+  if (batch == 0) {
+    g_info << "ERROR: Argument batch == 0 in runLookupQuery. Not allowed."
+           << endl;
+    return NDBT_FAILED;
+  }
+
+  allocRows(batch);
+
+  while (r < records)
+  {
+    if(r + batch > records)
+      batch = records - r;
+
+    if (retryAttempt >= m_retryMax)
+    {
+      g_info << "ERROR: has retried this operation " << retryAttempt
+	     << " times, failing!" << endl;
+      return NDBT_FAILED;
+    }
+
+    NdbTransaction * pTrans = pNdb->startTransaction();
+    if (pTrans == NULL)
+    {
+      const NdbError err = pNdb->getNdbError();
+
+      if (err.status == NdbError::TemporaryError){
+	ERR(err);
+	NdbSleep_MilliSleep(50);
+	retryAttempt++;
+	continue;
+      }
+      ERR(err);
+      return NDBT_FAILED;
+    }
+
+    NdbQuery * query = 0;
+    for (int b = 0; b<batch; b++)
+    {
+      char buf[NDB_MAX_TUPLE_SIZE];
+      NdbQueryParamValue params[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY];
+      equalForParameters(buf, * m_ops[0].m_calc, params, b + r);
+
+      query = pTrans->createQuery(m_query_def, params);
+      if (query == 0)
+      {
+        const NdbError err = pTrans->getNdbError();
+        ERR(err);
+        return NDBT_FAILED;
+      }
+
+      for (size_t o = 0; o<m_ops.size(); o++)
+      {
+        NdbQueryOperation * pOp = query->getQueryOperation((Uint32)o);
+        HugoQueries::getValueForQueryOp(pOp, m_ops[o].m_rows[b]);
+      }
+    }
+
+    int check = pTrans->execute(NoCommit, AbortOnError);
+    if (check == -1)
+    {
+      const NdbError err = pTrans->getNdbError();
+      if (err.status == NdbError::TemporaryError){
+	ERR(err);
+	pTrans->close();
+	NdbSleep_MilliSleep(50);
+	retryAttempt++;
+	continue;
+      }
+      ERR(err);
+      pTrans->close();
+      return NDBT_FAILED;
+    }
+
+    for (int b = 0; b<batch; b++)
+    {
+      for (size_t o = 0; o<m_ops.size(); o++)
+      {
+        NdbQueryOperation * pOp = query->getQueryOperation((Uint32)o);
+        if (!pOp->isRowNULL())
+        {
+          if (m_ops[o].m_calc->verifyRowValues(m_ops[o].m_rows[b]) != 0)
+          {
+            pTrans->close();
+            return NDBT_FAILED;
+          }
+        }
+      }
+    }
+    pTrans->close();
+    r += batch;
+  }
+
+  return NDBT_OK;
+}
+
+int
+HugoQueries::runScanQuery(Ndb * pNdb,
+                          int abort,
+                          int parallelism,
+                          int scan_flags)
+{
+  int retryAttempt = 0;
+
+  allocRows(1);
+
+  while (retryAttempt < m_retryMax)
+  {
+    NdbTransaction * pTrans = pNdb->startTransaction();
+    if (pTrans == NULL)
+    {
+      const NdbError err = pNdb->getNdbError();
+
+      if (err.status == NdbError::TemporaryError){
+	ERR(err);
+	NdbSleep_MilliSleep(50);
+	retryAttempt++;
+	continue;
+      }
+      ERR(err);
+      return NDBT_FAILED;
+    }
+
+    NdbQuery * query = 0;
+    //char buf[NDB_MAX_TUPLE_SIZE];
+    NdbQueryParamValue params[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY];
+
+    query = pTrans->createQuery(m_query_def, params);
+    if (query == 0)
+    {
+      const NdbError err = pTrans->getNdbError();
+      ERR(err);
+      return NDBT_FAILED;
+    }
+
+    for (size_t o = 0; o<m_ops.size(); o++)
+    {
+      NdbQueryOperation * pOp = query->getQueryOperation((Uint32)o);
+      HugoQueries::getValueForQueryOp(pOp, m_ops[o].m_rows[0]);
+    }
+
+    int check = pTrans->execute(NoCommit, AbortOnError);
+    if (check == -1)
+    {
+      const NdbError err = pTrans->getNdbError();
+      if (err.status == NdbError::TemporaryError){
+	ERR(err);
+	pTrans->close();
+	NdbSleep_MilliSleep(50);
+	retryAttempt++;
+	continue;
+      }
+      ERR(err);
+      pTrans->close();
+      return NDBT_FAILED;
+    }
+
+    NdbQuery::NextResultOutcome res;
+    while ((res = query->nextResult()) == NdbQuery::NextResult_gotRow)
+    {
+      for (size_t o = 0; o<m_ops.size(); o++)
+      {
+        NdbQueryOperation * pOp = query->getQueryOperation((Uint32)o);
+        if (!pOp->isRowNULL())
+        {
+          if (m_ops[o].m_calc->verifyRowValues(m_ops[o].m_rows[0]) != 0)
+          {
+            pTrans->close();
+            return NDBT_FAILED;
+          }
+        }
+      }
+    }
+
+    pTrans->close();
+    if (res == NdbQuery::NextResult_error)
+    {
+      const NdbError err = query->getNdbError();
+      if (err.status == NdbError::TemporaryError)
+      {
+	ERR(err);
+	NdbSleep_MilliSleep(50);
+	retryAttempt++;
+	continue;
+      }
+      return NDBT_FAILED;
+    }
+    else if (res != NdbQuery::NextResult_scanComplete)
+    {
+      ndbout_c("Got %u from nextResult()", res);
+      return NDBT_FAILED;
+    }
+    break;
+  }
+
+  return NDBT_OK;
+}
+
+template class Vector<HugoQueries::Op>;

=== added file 'storage/ndb/test/src/HugoQueryBuilder.cpp'
--- a/storage/ndb/test/src/HugoQueryBuilder.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/src/HugoQueryBuilder.cpp	2010-05-11 20:12:03 +0000
@@ -0,0 +1,447 @@
+/*
+   Copyright (C) 2003 MySQL AB
+    All rights reserved. Use is subject to license terms.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include <HugoQueryBuilder.hpp>
+
+void
+HugoQueryBuilder::init()
+{
+  m_options = 0;
+  setMinJoinLevel(2);
+  setMaxJoinLevel(4);
+}
+
+HugoQueryBuilder::~HugoQueryBuilder()
+{
+  for (size_t i = 0; i<m_queries.size(); i++)
+    m_queries[i]->release();
+}
+
+void
+HugoQueryBuilder::fixOptions()
+{
+  setOption(O_PK_INDEX);
+  setOption(O_UNIQUE_INDEX);
+  setOption(O_TABLE_SCAN);
+  setOption(O_ORDERED_INDEX);
+  if (testOption(O_LOOKUP))
+  {
+    clearOption(O_TABLE_SCAN);
+    clearOption(O_ORDERED_INDEX);
+  }
+}
+
+void
+HugoQueryBuilder::addTable(Ndb* ndb, const NdbDictionary::Table* tab)
+{
+  for (size_t i = 0; i<m_tables.size(); i++)
+  {
+    if (m_tables[i].m_table == tab)
+      return;
+  }
+
+  TableDef def;
+  def.m_table = tab;
+
+  /**
+   * TODO discover indexes...
+   */
+
+  m_tables.push_back(def);
+}
+
+int
+HugoQueryBuilder::getJoinLevel() const
+{
+  int m0 = m_joinLevel[0]; // min
+  int m1 = m_joinLevel[1]; // max
+  if (m0 > m1)
+  {
+    int m = m0;
+    m0 = m1;
+    m1 = m;
+  }
+
+  int d = m1 - m0;
+  if (d == 0)
+    return m0;
+  else
+    return m0 + (rand() % d);
+}
+
+void
+HugoQueryBuilder::removeTable(const NdbDictionary::Table* tab)
+{
+  for (size_t i = 0; i<m_tables.size(); i++)
+  {
+    if (m_tables[i].m_table == tab)
+    {
+      m_tables.erase(i);
+      return;
+    }
+  }
+}
+
+HugoQueryBuilder::TableDef
+HugoQueryBuilder::getTable() const
+{
+  int i = rand() % m_tables.size();
+  return m_tables[i];
+}
+
+HugoQueryBuilder::OpIdx
+HugoQueryBuilder::getOp() const
+{
+  OpIdx oi;
+  TableDef tab = getTable();
+  oi.m_index = 0;
+  oi.m_table = tab.m_table;
+
+  OptionMask save = m_options;
+  if (tab.m_unique_indexes.size() == 0)
+  {
+    clearOption(O_UNIQUE_INDEX);
+  }
+  if (tab.m_ordered_indexes.size() == 0)
+  {
+    clearOption(O_ORDERED_INDEX);
+  }
+
+loop:
+  switch(rand() % 4){
+  case 0:
+    if (testOption(O_PK_INDEX))
+    {
+      oi.m_type = NdbQueryOperationDef::PrimaryKeyAccess;
+      goto found;
+    }
+    break;
+  case 1:
+    if (testOption(O_TABLE_SCAN))
+    {
+      oi.m_type = NdbQueryOperationDef::TableScan;
+      goto found;
+    }
+    break;
+  case 2:
+    if (testOption(O_UNIQUE_INDEX))
+    {
+      oi.m_type = NdbQueryOperationDef::UniqueIndexAccess;
+      int cnt = tab.m_unique_indexes.size();
+      oi.m_index = tab.m_unique_indexes[rand() % cnt];
+      goto found;
+    }
+    break;
+  case 3:
+    if (testOption(O_ORDERED_INDEX))
+    {
+      oi.m_type = NdbQueryOperationDef::OrderedIndexScan;
+      int cnt = tab.m_ordered_indexes.size();
+      oi.m_index = tab.m_ordered_indexes[rand() % cnt];
+      goto found;
+    }
+    break;
+  }
+  goto loop;
+
+found:
+  m_options = save;
+  return oi;
+}
+
+bool
+HugoQueryBuilder::checkBindable(Vector<const NdbDictionary::Column*> cols,
+                                Vector<Op> ops)
+{
+  for (size_t c = 0; c < cols.size(); c++)
+  {
+    const NdbDictionary::Column * col = cols[c];
+    bool found = false;
+    for (size_t t = 0; !found && t<ops.size(); t++)
+    {
+      const NdbDictionary::Table * tab = ops[t].m_op->getTable();
+      if (tab)
+      {
+        for (int i = 0; i<tab->getNoOfColumns(); i++)
+        {
+          if (col->isBindable(* tab->getColumn(i)) == 0)
+          {
+            found = true;
+            break;
+          }
+        }
+      }
+    }
+    if (!found)
+      return false;
+  }
+  return true;
+}
+
+
+Vector<HugoQueryBuilder::Op>
+HugoQueryBuilder::getParents(OpIdx oi)
+{
+  /**
+   * We need to be able to bind all columns in table/index
+   */
+  Vector<const NdbDictionary::Column*> cols;
+  if (oi.m_index == 0)
+  {
+    for (int i = 0; i<oi.m_table->getNoOfColumns(); i++)
+      if (oi.m_table->getColumn(i)->getPrimaryKey())
+        cols.push_back(oi.m_table->getColumn(i));
+  }
+  else
+  {
+    abort(); // TODO
+  }
+
+  int r = rand() % m_query.size();
+  int cnt = (int)m_query.size();
+  for (int i = 0; i < cnt; i++)
+  {
+    Vector<Op> set;
+    Op op = m_query[(i + r) % cnt];
+    set.push_back(op);
+
+#if 0
+    /**
+     * add parents
+     */
+    if (testOption(O_MULTI_PARENT))
+    {
+      while (op.m_parent != -1)
+      {
+        op = m_query[op.m_parent];
+        set.push_back(op);
+      }
+    }
+#endif
+
+    if (checkBindable(cols, set))
+      return set;
+  }
+
+  Vector<Op> ret;
+  return ret;
+}
+
+NdbQueryOperand *
+HugoQueryBuilder::createLink(NdbQueryBuilder& builder,
+                             const NdbDictionary::Column* pCol,
+                             Vector<Op> & parents)
+{
+  int cnt = (int)parents.size();
+  int r = rand();
+
+  Op op;
+  const NdbDictionary::Column* col = 0;
+
+  /**
+   * Start linking with primary key...(for now)
+   */
+  for (int i = 0; i<cnt; i++)
+  {
+    op = parents[(i + r) % cnt];
+    const NdbDictionary::Table* tab = op.m_op->getTable();
+
+    int cntpk = tab->getNoOfPrimaryKeys();
+    int rpk = rand();
+    for (int j = 0; j<cntpk; j++)
+    {
+      col = tab->getColumn(tab->getPrimaryKey((j + rpk) % cntpk));
+      if (pCol->isBindable(* col) == 0)
+      {
+        goto found;
+      }
+    }
+  }
+
+  /**
+   * Check other columns
+   */
+  r = rand();
+  for (int i = 0; i<cnt; i++)
+  {
+    op = parents[(i + r) % cnt];
+    const NdbDictionary::Table* tab = op.m_op->getTable();
+
+    int cntcol = tab->getNoOfColumns();
+    int rcol = rand();
+    for (int j = 0; j<cntcol; j++)
+    {
+      col = tab->getColumn((j + rcol) % cntcol);
+      if (col->getPrimaryKey())
+      {
+        // already checked
+        continue;
+      }
+      if (pCol->isBindable(* col) == 0)
+      {
+        goto found;
+      }
+    }
+  }
+  return 0;
+
+found:
+  NdbQueryOperand * ret = builder.linkedValue(op.m_op, col->getName());
+  assert(ret);
+  return ret;
+}
+
+NdbQueryOperationDef*
+HugoQueryBuilder::createOp(NdbQueryBuilder& builder)
+{
+  struct Op op;
+  op.m_parent = -1;
+  op.m_op = 0;
+  op.m_idx = m_query.size();
+
+  if (m_query.size() == 0)
+  {
+    /**
+     * This is root operation...no linked-values
+     */
+    OpIdx oi = getOp();
+    switch(oi.m_type){
+    case NdbQueryOperationDef::PrimaryKeyAccess:{
+      int opNo = 0;
+      NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
+      for (int a = 0; a<oi.m_table->getNoOfColumns(); a++)
+      {
+        if (oi.m_table->getColumn(a)->getPrimaryKey())
+        {
+          operands[opNo++] = builder.paramValue();
+        }
+      }
+      operands[opNo] = 0;
+      op.m_op = builder.readTuple(oi.m_table, operands);
+      break;
+    }
+    case NdbQueryOperationDef::TableScan:
+      op.m_op = builder.scanTable(oi.m_table);
+      break;
+    case NdbQueryOperationDef::OrderedIndexScan:
+      abort();
+      break;
+    case NdbQueryOperationDef::UniqueIndexAccess:
+      // TODO
+      abort();
+    }
+  }
+  else
+  {
+loop:
+    OpIdx oi = getOp();
+    Vector<Op> parents = getParents(oi);
+    if (parents.size() == 0)
+    {
+      // no possible parents found for pTab...try another
+      goto loop;
+    }
+    switch(oi.m_type){
+    case NdbQueryOperationDef::PrimaryKeyAccess:{
+      int opNo = 0;
+      NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
+      for (int a = 0; a<oi.m_table->getNoOfColumns(); a++)
+      {
+        if (oi.m_table->getColumn(a)->getPrimaryKey())
+        {
+          operands[opNo++] = createLink(builder, oi.m_table->getColumn(a),
+                                        parents);
+        }
+      }
+      operands[opNo] = 0;
+
+      op.m_parent = parents[0].m_idx;
+      op.m_op = builder.readTuple(oi.m_table, operands);
+      break;
+    }
+    case NdbQueryOperationDef::TableScan:
+      // not supported
+      abort();
+    case NdbQueryOperationDef::OrderedIndexScan:
+    case NdbQueryOperationDef::UniqueIndexAccess:
+      // TODO
+      abort();
+    }
+
+  }
+
+  if (op.m_op == 0)
+  {
+    NdbError err = builder.getNdbError();
+    ndbout << err << endl;
+    return 0;
+  }
+
+  m_query.push_back(op);
+  return op.m_op;
+}
+
+const NdbQueryDef *
+HugoQueryBuilder::createQuery(Ndb* pNdb, bool takeOwnership)
+{
+  NdbQueryBuilder builder(*pNdb);
+
+  {
+    OptionMask save = m_options;
+    if (testOption(O_SCAN))
+    {
+      clearOption(O_PK_INDEX);
+      clearOption(O_UNIQUE_INDEX);
+    }
+    NdbQueryOperationDef * rootOp = createOp(builder);
+    assert(rootOp != 0);
+    m_options = save;
+  }
+
+  /**
+   * We only support lookups as child operations...
+   */
+  OptionMask save = m_options;
+  clearOption(O_ORDERED_INDEX);
+  clearOption(O_TABLE_SCAN);
+
+  int levels = getJoinLevel();
+  while (levels --)
+  {
+    createOp(builder);
+  }
+
+  m_options = save;
+
+  const NdbQueryDef * def = builder.prepare();
+  if (def != 0 && !takeOwnership)
+  {
+    m_queries.push_back(def);
+  }
+
+  m_query.clear();
+
+  return def;
+}
+
+template class Vector<const NdbQueryDef*>;
+template class Vector<HugoQueryBuilder::Op>;
+template class Vector<NdbQueryOperationDef::Type>;
+template class Vector<const NdbDictionary::Column*>;
+template class Vector<HugoQueryBuilder::TableDef>;
+template class Vector<const NdbDictionary::Index*>;

=== modified file 'storage/ndb/test/src/Makefile.am'
--- a/storage/ndb/test/src/Makefile.am	2009-11-18 11:05:02 +0000
+++ b/storage/ndb/test/src/Makefile.am	2010-05-11 20:12:03 +0000
@@ -28,7 +28,8 @@ libNDBT_a_SOURCES = \
 	NdbBackup.cpp  NdbConfig.cpp NDBT_Table.cpp \
 	NdbSchemaCon.cpp NdbSchemaOp.cpp getarg.c AtrtClient.cpp \
 	CpcClient.cpp NdbMixRestarter.cpp NDBT_Thread.cpp DbUtil.cpp \
-	SocketInputStream2.cpp NDBT_Find.cpp
+	SocketInputStream2.cpp NDBT_Find.cpp \
+	HugoQueryBuilder.cpp HugoQueries.cpp
 
 INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/common/mgmcommon -I$(top_srcdir)/storage/ndb/include/mgmcommon -I$(top_srcdir)/storage/ndb/include/kernel -I$(top_srcdir)/storage/ndb/src/mgmapi -I$(top_srcdir)/include -I$(top_srcdir)/storage/ndb/include/debugger
 

=== modified file 'storage/ndb/test/src/NDBT_Test.cpp'
--- a/storage/ndb/test/src/NDBT_Test.cpp	2010-05-04 14:34:54 +0000
+++ b/storage/ndb/test/src/NDBT_Test.cpp	2010-05-11 20:12:03 +0000
@@ -166,6 +166,21 @@ NDBT_Context::incProperty(const char * n
   NdbMutex_Unlock(propertyMutexPtr);
 }
 
+Uint32
+NDBT_Context::casProperty(const char * name, Uint32 oldValue, Uint32 newValue)
+{
+  NdbMutex_Lock(propertyMutexPtr);
+  Uint32 val = 0;
+  props.get(name, &val);
+  if (val == oldValue)
+  {
+    props.put(name, newValue, true);
+    NdbCondition_Broadcast(propertyCondPtr);
+  }
+  NdbMutex_Unlock(propertyMutexPtr);
+  return val;
+}
+
 void  NDBT_Context::setProperty(const char* _name, const char* _val){ 
   NdbMutex_Lock(propertyMutexPtr);
   const bool b = props.put(_name, _val, true);

=== modified file 'storage/ndb/test/tools/CMakeLists.txt'
--- a/storage/ndb/test/tools/CMakeLists.txt	2009-11-13 04:50:47 +0000
+++ b/storage/ndb/test/tools/CMakeLists.txt	2010-05-11 20:12:03 +0000
@@ -37,5 +37,6 @@ ADD_EXECUTABLE(verify_index verify_index
 ADD_EXECUTABLE(copy_tab copy_tab.cpp)
 ADD_EXECUTABLE(create_index create_index.cpp)
 ADD_EXECUTABLE(ndb_cpcc cpcc.cpp)
+ADD_EXECUTABLE(hugoJoin hugoJoin.coo)
 
 INSTALL(TARGETS ndb_cpcc DESTINATION bin)

=== modified file 'storage/ndb/test/tools/Makefile.am'
--- a/storage/ndb/test/tools/Makefile.am	2010-04-29 14:56:00 +0000
+++ b/storage/ndb/test/tools/Makefile.am	2010-05-11 20:12:03 +0000
@@ -16,7 +16,7 @@
 
 EXTRA_DIST = CMakeLists.txt
 
-ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord  hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index  ndb_cpcc listen_event eventlog rep_latency ndb_connect spj_sanity_test spj_performance_test test_spj
+ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord  hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index  ndb_cpcc listen_event eventlog rep_latency ndb_connect spj_sanity_test spj_performance_test test_spj hugoJoin
 
 # transproxy 
 
@@ -29,6 +29,8 @@ hugoPkReadRecord_SOURCES = hugoPkReadRec
 hugoPkUpdate_SOURCES = hugoPkUpdate.cpp
 hugoScanRead_SOURCES = hugoScanRead.cpp
 hugoScanUpdate_SOURCES = hugoScanUpdate.cpp
+hugoJoin_SOURCES = hugoJoin.cpp
+
 restart_SOURCES = restart.cpp
 # transproxy_SOURCES = transproxy.cpp
 verify_index_SOURCES = verify_index.cpp

=== added file 'storage/ndb/test/tools/hugoJoin.cpp'
--- a/storage/ndb/test/tools/hugoJoin.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/tools/hugoJoin.cpp	2010-05-11 20:12:03 +0000
@@ -0,0 +1,216 @@
+/*
+   Copyright (C) 2003 MySQL AB
+    All rights reserved. Use is subject to license terms.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <my_sys.h>
+#include <my_getopt.h>
+#include <mysql_version.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NdbSleep.h>
+#include <NDBT.hpp>
+#include <HugoTransactions.hpp>
+#include <HugoQueryBuilder.hpp>
+#include <HugoQueries.hpp>
+#include <NdbTick.h>
+
+int _verbose = 1;
+int _help = 0;
+int _batch = 128;
+int _records = 1000;
+int _loops = 100;
+int _loops_per_query = 100;
+int _depth = 4;
+unsigned int _seed = 0;
+static const char * _options = "lookup";
+static const char * _db = "TEST_DB";
+
+extern const char *load_default_groups[];
+static struct my_option my_long_options[] =
+{
+  NDB_STD_OPTS("hugoJoin"),
+  { "database", 'd', "Database",
+    (uchar**) &_db, (uchar**) &_db,
+    0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  { "options", 'o', "comma separated list of options",
+    (uchar**) &_options, (uchar**) &_options,
+    0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  { "loops", 'l', "Loops",
+    (uchar**) &_loops, 0,
+    0, GET_INT, REQUIRED_ARG, _loops, -1, 0, 0, 0, 0},
+  { "verbose", 'v', "verbosity",
+    (uchar**) &_verbose, 0,
+    0, GET_INT, REQUIRED_ARG, _verbose, 0, 0, 0, 0, 0},
+  { "loops_per_query", 'q', "Recreate query each #loops",
+    (uchar**) &_loops_per_query, 0,
+    0, GET_INT, REQUIRED_ARG, _loops_per_query, 0, 0, 0, 0, 0},
+  { "batch", 'b', "Batch size (for lookups)",
+    (uchar**) &_batch, 0,
+    0, GET_INT, REQUIRED_ARG, _batch, 0, 0, 0, 0, 0},
+  { "records", 'r', "Records (for lookups)",
+    (uchar**) &_records, 0,
+    0, GET_INT, REQUIRED_ARG, _records, 0, 0, 0, 0, 0},
+  { "join-depth", 'j', "Join depth",
+    (uchar**) &_depth, 0,
+    0, GET_INT, REQUIRED_ARG, _depth, 0, 0, 0, 0, 0},
+  { "seed", NDB_OPT_NOSHORT, "Random seed",
+    (uchar **) &_seed, (uchar **) &_seed, 0,
+    GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static void short_usage_sub(void)
+{
+  ndb_short_usage_sub(my_progname, NULL);
+}
+
+static void usage()
+{
+  char desc[] =
+    "This run random joins on table-list\n";
+  puts(desc);
+  ndb_usage(short_usage_sub, load_default_groups, my_long_options);
+}
+
+int main(int argc, char** argv){
+  NDB_INIT(argv[0]);
+  ndb_opt_set_usage_funcs(NULL, short_usage_sub, usage);
+  load_defaults("my", load_default_groups, &argc, &argv);
+  int ho_error;
+  if ((ho_error=handle_options(&argc, &argv, my_long_options,
+			       ndb_std_get_one_option)))
+    return -1;
+
+
+  // Connect to Ndb
+  Ndb_cluster_connection con;
+  if(con.connect(12, 5, 1) != 0)
+  {
+    return NDBT_ProgramExit(NDBT_FAILED);
+  }
+
+  if (con.wait_until_ready(30,0) < 0)
+  {
+    ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+    return NDBT_ProgramExit(NDBT_FAILED);
+  }
+
+  Ndb MyNdb( &con, _db);
+
+  if(MyNdb.init() != 0)
+  {
+    ERR(MyNdb.getNdbError());
+    return NDBT_ProgramExit(NDBT_FAILED);
+  }
+
+  Vector<const NdbDictionary::Table*> tables;
+  for(int i = 0; i<argc; i++)
+  {
+    const char* _tabname = argv[i];
+    // Check if table exists in db
+    const NdbDictionary::Table* pTab =
+      NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+    if(pTab == NULL)
+    {
+      ndbout << " Table " << _tabname << " does not exist!" << endl;
+      return NDBT_ProgramExit(NDBT_WRONGARGS);
+    }
+    else
+    {
+      ndbout << " Discovered " << _tabname << endl;
+    }
+    tables.push_back(pTab);
+  }
+  tables.push_back(0);
+
+  HugoQueryBuilder::OptionMask mask = 0;
+  struct { const char * name; HugoQueryBuilder::QueryOption option; }
+  _ops[] = {
+    { "lookup", HugoQueryBuilder::O_LOOKUP },
+    { "scan", HugoQueryBuilder::O_SCAN },
+    { "pk", HugoQueryBuilder::O_PK_INDEX },
+    { "uk", HugoQueryBuilder::O_UNIQUE_INDEX },
+    { "oi", HugoQueryBuilder::O_ORDERED_INDEX },
+    { "ts", HugoQueryBuilder::O_TABLE_SCAN },
+
+    // end-marker
+    { 0, HugoQueryBuilder::O_LOOKUP }
+  };
+
+  Vector<BaseString> list;
+  BaseString tmp(_options);
+  tmp.split(list, ",");
+  for (size_t i = 0; i<list.size(); i++)
+  {
+    bool found = false;
+    for (int o = 0; _ops[o].name != 0; o++)
+    {
+      if (strcasecmp(list[i].c_str(), _ops[o].name) == 0)
+      {
+        found = true;
+        mask |= _ops[o].option;
+        break;
+      }
+    }
+    if (!found)
+    {
+      ndbout << "Unknown option " << list[i].c_str() << ", ignoring" << endl;
+    }
+  }
+
+  if (_seed == 0)
+  {
+    _seed = NdbTick_CurrentMillisecond();
+  }
+  ndbout << "--seed=" << _seed << endl;
+
+  for (int i = 0; (_loops == -1) || (i < _loops);)
+  {
+    if (_verbose >= 1)
+    {
+      ndbout << "\tbuilding new query" << endl;
+    }
+    HugoQueryBuilder builder(&MyNdb, tables.getBase(), mask);
+    builder.setJoinLevel(_depth);
+    const NdbQueryDef * q = builder.createQuery(&MyNdb);
+    for (int j = 0; j < _loops_per_query && ((_loops == -1) || (i < _loops));
+         i++, j++)
+    {
+      int res = 0;
+      HugoQueries hq(* q);
+      if (q->isScanQuery())
+      {
+        res = hq.runScanQuery(&MyNdb);
+      }
+      else
+      {
+        res = hq.runLookupQuery(&MyNdb, _records, _batch);
+      }
+      if (res != 0)
+      {
+        return NDBT_ProgramExit(NDBT_FAILED);
+      }
+    }
+  }
+
+  return NDBT_ProgramExit(NDBT_OK);
+}


Attachment: [text/bzr-bundle] bzr/jonas@mysql.com-20100511201203-ssfoed1784vilqj6.bundle
Thread
bzr commit into mysql-5.1-telco-7.0-spj branch (jonas:3152)Jonas Oreland11 May