List:Commits« Previous MessageNext Message »
From:Jan Wedvik Date:August 7 2009 9:33am
Subject:bzr commit into mysql-5.1-telco-7.0-spj branch (jan.wedvik:2940)
View as plain text  
#At file:///export/home2/tmp/jw1159207/mysql/mysql-5.1-telco-7.0-spj/ based on revid:jan.wedvik@stripped4008-5ley5iqnyhiads6i

 2940 Jan Wedvik	2009-08-07
      Before changing NdbQueryOperationImpl *not* to count TCKEYREF 
      messages when root operation is a scan.

    modified:
      storage/ndb/include/ndbapi/Ndb.hpp
      storage/ndb/include/ndbapi/NdbQueryOperation.hpp
      storage/ndb/include/ndbapi/NdbReceiver.hpp
      storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp
      storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp
      storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
      storage/ndb/src/ndbapi/NdbQueryBuilder.cpp
      storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp
      storage/ndb/src/ndbapi/NdbQueryOperation.cpp
      storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp
      storage/ndb/src/ndbapi/NdbReceiver.cpp
      storage/ndb/src/ndbapi/NdbTransactionScan.cpp
      storage/ndb/src/ndbapi/Ndbif.cpp
      storage/ndb/src/ndbapi/TransporterFacade.cpp
      storage/ndb/src/ndbapi/ndberror.c
      storage/ndb/test/tools/test_spj.cpp
=== modified file 'storage/ndb/include/ndbapi/Ndb.hpp'
--- a/storage/ndb/include/ndbapi/Ndb.hpp	2009-08-04 07:40:08 +0000
+++ b/storage/ndb/include/ndbapi/Ndb.hpp	2009-08-07 09:33:27 +0000
@@ -1073,8 +1073,6 @@ class Ndb
   friend class NdbScanFilterImpl;
   friend class NdbQueryImpl;
   friend class NdbQueryOperationImpl;
-  friend class NdbQueryReceiver;
-  friend class NdbMappable;
 #endif
 
 public:

=== modified file 'storage/ndb/include/ndbapi/NdbQueryOperation.hpp'
--- a/storage/ndb/include/ndbapi/NdbQueryOperation.hpp	2009-07-24 08:55:33 +0000
+++ b/storage/ndb/include/ndbapi/NdbQueryOperation.hpp	2009-08-07 09:33:27 +0000
@@ -221,7 +221,7 @@ public:
                        const unsigned char* result_mask = 0);
 
   int setResultRowRef (const NdbRecord* rec,
-                       char* & bufRef,
+                       const char* & bufRef,
                        const unsigned char* result_mask = 0);
 
   // TODO: define how BLOB/CLOB should be retrieved.

=== modified file 'storage/ndb/include/ndbapi/NdbReceiver.hpp'
--- a/storage/ndb/include/ndbapi/NdbReceiver.hpp	2009-08-04 07:40:08 +0000
+++ b/storage/ndb/include/ndbapi/NdbReceiver.hpp	2009-08-07 09:33:27 +0000
@@ -33,14 +33,13 @@ class NdbReceiver
   friend class Ndb;
   friend class NdbOperation;
   friend class NdbQueryImpl;
-  // friend class NdbQueryOperation;
-  friend class NdbQueryLookupOperationImpl;
-  friend class NdbQueryScanOperationImpl;
-  friend class NdbQueryReceiver;
+  friend class NdbQueryOperation;
+  friend class NdbQueryOperationImpl;
   friend class NdbScanOperation;
   friend class NdbIndexOperation;
   friend class NdbIndexScanOperation;
   friend class NdbTransaction;
+  friend int spjTest(int argc, char** argv);
 
 public:
   enum ReceiverType	{ NDB_UNINITIALIZED,
@@ -90,7 +89,7 @@ private:
   void getValues(const NdbRecord*, char*);
   void prepareSend();
   void calculate_batch_size(Uint32, Uint32, Uint32&, Uint32&, Uint32&,
-                            const NdbRecord *);
+                            const NdbRecord *) const;
   /*
     Set up buffers for receiving TRANSID_AI and KEYINFO20 signals
     during a scan using NdbRecord.

=== modified file 'storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp	2009-07-17 21:57:40 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp	2009-08-07 09:33:27 +0000
@@ -210,6 +210,15 @@ public:
      *  should only do local cleanup(s)
      */
     void (Dbspj::*m_cleanup)(Ptr<Request>, Ptr<TreeNode>);
+
+    /**
+     * This function is called on the root operation  when a LQHKEYCONF, 
+     * LQKEYREF or LQHKEYREQ signal is sent or received on behalf of a 
+     * descendant operation*/
+    void (Dbspj::*m_count_descendant_signal)(Signal* signal,
+                                             Ptr<Request> requestPtr,
+                                             Ptr<TreeNode> rootPtr,
+                                             Uint32 globalSignalNo);
   };
 
   struct LookupData
@@ -242,8 +251,19 @@ public:
 
     Uint32 m_scan_state;     // Only valid is TreeNodeState >= TN_ACTIVE
     Uint32 m_scan_status;    // fragmentCompleted
+    /** True if signal has been received since sending 
+     * last SCAN_FRAGREQ/SCAN_NEXTREQ*/
+    bool   m_scan_fragconf_received; 
     Uint32 m_rows_received;  // #execTRANSID_AI
     Uint32 m_rows_expecting; // ScanFragConf
+    /** Number of receiced LQHKEYCONF messages from descendant lookup 
+     * operations.*/
+    Uint32 m_descendant_keyconfs_received;
+    /** Number of received LQHKEYREF messages from descendant lookup 
+     * operations.*/
+    Uint32 m_descendant_keyrefs_received;
+    /** Number of LQHKEYREQ messages sent for descendant lookup operations.*/
+    Uint32 m_descendant_keyreqs_sent;
     Uint32 m_scanFragReq[ScanFragReq::SignalLength + 1];
   };
 
@@ -485,7 +505,9 @@ private:
                  DABuffer param, Uint32 paramBits);
 
   Uint32 zeroFill(Uint32 & ptrI, Uint32 cnt);
-
+  /** Find root operation.*/
+  const Ptr<TreeNode> getRoot(TreeNode_list::Head& head);
+  
   /**
    * Lookup
    */
@@ -500,6 +522,10 @@ private:
   void lookup_execLQHKEYCONF(Signal*, Ptr<Request>, Ptr<TreeNode>);
   void lookup_start_child(Signal*, Ptr<Request>, Ptr<TreeNode>, const RowRef &);
   void lookup_cleanup(Ptr<Request>, Ptr<TreeNode>);
+  void lookup_count_descendant_signal(Signal* signal,
+                                      Ptr<Request> requestPtr,
+                                      Ptr<TreeNode> rootPtr,
+                                      Uint32 globalSignalNo){};
 
   Uint32 computeHash(Signal*, BuildKeyReq&, Uint32 table, Uint32 keyInfoPtrI);
   Uint32 getNodes(Signal*, BuildKeyReq&, Uint32 tableId);
@@ -519,6 +545,10 @@ private:
   void scanFrag_batch_complete(Signal*, Ptr<Request>, Ptr<TreeNode>);
   void scanFrag_start_child(Signal*,Ptr<Request>,Ptr<TreeNode>, const RowRef &);
   void scanFrag_cleanup(Ptr<Request>, Ptr<TreeNode>);
+  void scanFrag_count_descendant_signal(Signal* signal,
+                                        Ptr<Request> requestPtr,
+                                        Ptr<TreeNode> rootPtr,
+                                        Uint32 globalSignalNo);
 
   /**
    * Scratch buffers...

=== modified file 'storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp	2009-07-24 08:55:33 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp	2009-08-07 09:33:27 +0000
@@ -1020,7 +1020,8 @@ Dbspj::g_LookupOpInfo =
   0, // Dbspj::lookup_execSCAN_NEXTREQ
   0, // Dbspj::lookup_complete
   0, // Dbspj::lookup_abort
-  &Dbspj::lookup_cleanup
+  &Dbspj::lookup_cleanup,
+  &Dbspj::lookup_count_descendant_signal
 };
 
 Uint32
@@ -1283,6 +1284,12 @@ Dbspj::lookup_send(Signal* signal,
     add = 1;
   }
   treeNodePtr.p->m_lookup_data.m_outstanding += add;
+
+  Ptr<TreeNode> root = getRoot(requestPtr.p->m_nodes);
+  (this->*(root.p->m_info->m_count_descendant_signal))(NULL,
+                                                       requestPtr,
+                                                       root,
+                                                       GSN_LQHKEYREQ);
 }
 
 void
@@ -1365,6 +1372,12 @@ Dbspj::lookup_execLQHKEYREF(Signal* sign
     jam();
     nodeFinished(signal, requestPtr, treeNodePtr);
   }
+
+  Ptr<TreeNode> root = getRoot(requestPtr.p->m_nodes);
+  (this->*(root.p->m_info->m_count_descendant_signal))(signal,
+                                                       requestPtr,
+                                                       root,
+                                                       GSN_LQHKEYREF);
 }
 
 void
@@ -1380,6 +1393,12 @@ Dbspj::lookup_execLQHKEYCONF(Signal* sig
     jam();
     nodeFinished(signal, requestPtr, treeNodePtr);
   }
+
+  Ptr<TreeNode> root = getRoot(requestPtr.p->m_nodes);
+  (this->*(root.p->m_info->m_count_descendant_signal))(signal,
+                                                       requestPtr,
+                                                       root,
+                                                       GSN_LQHKEYCONF);
 }
 
 void
@@ -1552,7 +1571,8 @@ Dbspj::g_ScanFragOpInfo =
   0, // Dbspj::scanFrag_execSCAN_NEXTREQ
   0, // Dbspj::scanFrag_complete
   0, // Dbspj::scanFrag_abort
-  &Dbspj::scanFrag_cleanup
+  &Dbspj::scanFrag_cleanup,
+  &Dbspj::scanFrag_count_descendant_signal
 };
 
 Uint32
@@ -1805,8 +1825,24 @@ Dbspj::scanFrag_send(Signal* signal,
 
   treeNodePtr.p->m_scanfrag_data.m_scan_state = ScanFragData::SF_RUNNING;
   treeNodePtr.p->m_scanfrag_data.m_scan_status = 0;
+  treeNodePtr.p->m_scanfrag_data.m_scan_fragconf_received = false;
   treeNodePtr.p->m_scanfrag_data.m_rows_received = 0;
   treeNodePtr.p->m_scanfrag_data.m_rows_expecting = 0;
+  treeNodePtr.p->m_scanfrag_data.m_descendant_keyconfs_received = 0;
+  treeNodePtr.p->m_scanfrag_data.m_descendant_keyrefs_received = 0;
+  treeNodePtr.p->m_scanfrag_data.m_descendant_keyreqs_sent = 0;
+}
+
+/** Return true if scan is complete. This happens when all scan rows have */
+static bool isScanComplete(const Dbspj::ScanFragData& scanFragData)
+{
+  return scanFragData.m_scan_fragconf_received &&
+    // All rows for root scan received.
+    scanFragData.m_rows_received == scanFragData.m_rows_expecting &&
+    // All rows for descendant lookups received.
+    scanFragData.m_descendant_keyreqs_sent == 
+    scanFragData.m_descendant_keyconfs_received + 
+    scanFragData.m_descendant_keyrefs_received;
 }
 
 void
@@ -1818,10 +1854,6 @@ Dbspj::scanFrag_execTRANSID_AI(Signal* s
   jam();
   treeNodePtr.p->m_scanfrag_data.m_rows_received++;
 
-  const bool done =
-    treeNodePtr.p->m_scanfrag_data.m_rows_expecting ==
-    treeNodePtr.p->m_scanfrag_data.m_rows_received;
-
   {
     LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
     Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
@@ -1837,8 +1869,7 @@ Dbspj::scanFrag_execTRANSID_AI(Signal* s
     }
   }
 
-  if (done)
-  {
+  if(isScanComplete(treeNodePtr.p->m_scanfrag_data)){
     jam();
     scanFrag_batch_complete(signal, requestPtr, treeNodePtr);
   }
@@ -1869,9 +1900,8 @@ Dbspj::scanFrag_execSCAN_FRAGCONF(Signal
 
   treeNodePtr.p->m_scanfrag_data.m_scan_status = done;
   treeNodePtr.p->m_scanfrag_data.m_rows_expecting = rows;
-
-  if (rows == treeNodePtr.p->m_scanfrag_data.m_rows_received)
-  {
+  treeNodePtr.p->m_scanfrag_data.m_scan_fragconf_received = true;
+  if(isScanComplete(treeNodePtr.p->m_scanfrag_data)){
     jam();
     scanFrag_batch_complete(signal, requestPtr, treeNodePtr);
   }
@@ -1894,13 +1924,13 @@ Dbspj::scanFrag_batch_complete(Signal* s
   conf->senderData = requestPtr.p->m_senderData;
   conf->transId1 = requestPtr.p->m_transId[0];
   conf->transId2 = requestPtr.p->m_transId[1];
-  conf->completedOps = treeNodePtr.p->m_scanfrag_data.m_rows_expecting;
+  conf->completedOps = treeNodePtr.p->m_scanfrag_data.m_rows_expecting
+    + treeNodePtr.p->m_scanfrag_data.m_descendant_keyconfs_received;
   conf->fragmentCompleted = treeNodePtr.p->m_scanfrag_data.m_scan_status;
   conf->total_len = 0; // Not supported...
 
   sendSignal(requestPtr.p->m_senderRef, GSN_SCAN_FRAGCONF, signal,
 	     ScanFragConf::SignalLength, JBB);
-
   if (treeNodePtr.p->m_scanfrag_data.m_scan_status == 2)
   {
     jam();
@@ -1937,6 +1967,46 @@ Dbspj::scanFrag_cleanup(Ptr<Request> req
 {
   cleanup_common(requestPtr, treeNodePtr);
 }
+
+void
+Dbspj::scanFrag_count_descendant_signal(Signal* signal,
+                                        Ptr<Request> requestPtr,
+                                        Ptr<TreeNode> rootPtr,
+                                        Uint32 globalSignalNo)
+{
+  switch(globalSignalNo){
+  case GSN_LQHKEYCONF:
+    jam();
+    rootPtr.p->m_scanfrag_data.m_descendant_keyconfs_received++;
+    ndbout << "Dbspj::scanFrag_count_descendant_signal() incremented "
+      "m_scanfrag_data.m_descendant_keyconfs_received to "<< 
+      rootPtr.p->m_scanfrag_data.m_descendant_keyconfs_received << endl;
+    break;
+  case GSN_LQHKEYREF:
+    jam();
+    rootPtr.p->m_scanfrag_data.m_descendant_keyrefs_received++;
+    ndbout << "Dbspj::scanFrag_count_descendant_signal() incremented "
+      "m_scanfrag_data.m_descendant_keyrefs_received to "<< 
+      rootPtr.p->m_scanfrag_data.m_descendant_keyrefs_received << endl;
+    break;
+  case GSN_LQHKEYREQ:
+    jam();
+    rootPtr.p->m_scanfrag_data.m_descendant_keyreqs_sent++;
+    ndbout << "Dbspj::scanFrag_count_descendant_signal() incremented "
+      "m_scanfrag_data.m_descendant_keyreqs_sent to "<< 
+      rootPtr.p->m_scanfrag_data.m_descendant_keyreqs_sent << endl;
+    break;
+  default:
+    jam();
+    ndbrequire(false);
+  }
+  if(isScanComplete(rootPtr.p->m_scanfrag_data)){
+    jam();
+    ndbrequire(globalSignalNo!=GSN_LQHKEYREQ);
+    scanFrag_batch_complete(signal, requestPtr, rootPtr);
+  }
+}
+
 /**
  * END - MODULE SCAN FRAG
  */
@@ -2252,6 +2322,18 @@ error:
   return DbspjErr::OutOfSectionMemory;
 }
 
+const Ptr<Dbspj::TreeNode> 
+Dbspj::getRoot(TreeNode_list::Head& head)
+{
+  Ptr<TreeNode> rootPtr;
+  const Local_TreeNode_list list(m_treenode_pool, head);
+  const bool found = list.first(rootPtr); 
+  ndbassert(found);
+  ndbassert(!rootPtr.isNull());
+  ndbassert(rootPtr.p->m_node_no==0);
+  return rootPtr;
+}
+
 /**
  * This function takes a pattern and a row and expands it into a section
  */

=== modified file 'storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp	2009-06-12 12:01:12 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp	2009-08-07 09:33:27 +0000
@@ -10604,7 +10604,6 @@ void Dbtc::execSCAN_FRAGCONF(Signal* sig
   }//if
   
   ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::LQH_ACTIVE);
-  
   if(scanptr.p->scanState == ScanRecord::CLOSING_SCAN){
     jam();
     if(status == 0){
@@ -10667,6 +10666,10 @@ void Dbtc::execSCAN_FRAGCONF(Signal* sig
   scanFragptr.p->scanFragState = ScanFragRec::QUEUED_FOR_DELIVERY;
   scanFragptr.p->stopFragTimer();
   
+  if(refToMain(signal->header.theSendersBlockRef)==DBSPJ){
+    ndbout << "Dbtc::execSCAN_FRAGCONF() got SCAN_FRAGCONF from DBSPJ "
+      "scanptr.p->m_queued_count=" << scanptr.p->m_queued_count << endl;
+  }
   if(scanptr.p->m_queued_count > /** Min */ 0){
     jam();
     sendScanTabConf(signal, scanptr);

=== modified file 'storage/ndb/src/ndbapi/NdbQueryBuilder.cpp'
--- a/storage/ndb/src/ndbapi/NdbQueryBuilder.cpp	2009-08-03 07:58:08 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryBuilder.cpp	2009-08-07 09:33:27 +0000
@@ -17,7 +17,6 @@
 */
 
 #include "NdbQueryBuilderImpl.hpp"
-#include "NdbQueryOperationImpl.hpp"
 #include <ndb_global.h>
 #include <Vector.hpp>
 
@@ -204,10 +203,6 @@ private:
   virtual Type getType() const
   { return (index!=NULL) ? PrimaryKeyAccess : UniqueIndexAccess; }
 
-  /** Make an instance for a concrete query.*/
-  virtual NdbQueryOperationImpl* makeOperation(NdbQueryImpl& query) const 
-  { return new NdbQueryLookupOperationImpl(query, *this);}
-
 private:
   NdbQueryLookupOperationDef m_interface;
   const NdbDictionary::Index* const m_index;
@@ -227,10 +222,6 @@ public:
                            Uint32      ix)
   : NdbQueryOperationDefImpl(table,ident,ix)
   {};
-
-  /** Make an instance for a concrete query.*/
-  virtual NdbQueryOperationImpl* makeOperation(NdbQueryImpl& query) const 
-  { return new NdbQueryScanOperationImpl(query, *this);}
 }; // class NdbQueryScanOperationDefImpl
 
 class NdbQueryTableScanOperationDefImpl : public NdbQueryScanOperationDefImpl
@@ -1056,7 +1047,7 @@ NdbQueryIndexScanOperationDefImpl
 ::materializeRootOperands(NdbOperation& ndbOperation,
                           const constVoidPtr actualParam[]) const
 {
-  assert(false); // TODO: Implement this.
+  // TODO: Implement this.
 }
 
 void 
@@ -1272,12 +1263,10 @@ NdbQueryLookupOperationDefImpl
       break;
     }
     case NdbQueryOperandImpl::Param:  // TODO: Implement this
-      if(false){
-        /**** FIXME: can't set NI_KEY_PARAMS yet as this also require PI_KEY_PARAMS in parameter part *****/
-        node.requestInfo |= DABits::NI_KEY_PARAMS;
-        paramCnt++;
-        keyPattern.get(keyPatternPos++) = QueryPattern::data(0);  // Simple hack to avoid 'assert(keyPatternPos>0)' below
-      }
+      /**** FIXME: can't set NI_KEY_PARAMS yet as this also require PI_KEY_PARAMS in parameter part *****/
+      node.requestInfo |= DABits::NI_KEY_PARAMS;
+      paramCnt++;
+      keyPattern.get(keyPatternPos++) = QueryPattern::data(0);  // Simple hack to avoid 'assert(keyPatternPos>0)' below
       break;
     default:
       assert(false);

=== modified file 'storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp	2009-07-31 12:24:15 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp	2009-08-07 09:33:27 +0000
@@ -31,7 +31,7 @@
 #define QRY_OPERAND_ALREADY_BOUND 4807
 #define QRY_DEFINITION_TOO_LARGE 4808
 #define QRY_DUPLICATE_COLUMN_IN_PROJ 4809
-
+#define QRY_RESULT_ROW_ALREADY_DEFINED 4810
 
 #ifdef __cplusplus
 #include "signaldata/TcKeyReq.hpp"
@@ -46,8 +46,6 @@ class NdbQueryOperationDefImpl;
 class NdbParamOperandImpl;
 class NdbConstOperandImpl;
 class NdbLinkedOperandImpl;
-class NdbQueryOperationImpl;
-class NdbQueryImpl;
 
 /** A buffer for holding serialized data.*/
 class Uint32Buffer{
@@ -277,9 +275,6 @@ public:
   // Get type of query operation
   virtual Type getType() const = 0;
 
-  /** Make an instance for a concrete query.*/
-  virtual NdbQueryOperationImpl* makeOperation(NdbQueryImpl& query) const = 0;
-  
   virtual const NdbQueryOperationDef& getInterface() const = 0; 
 
   /** Make a serialized representation of this operation, corresponding to

=== modified file 'storage/ndb/src/ndbapi/NdbQueryOperation.cpp'
--- a/storage/ndb/src/ndbapi/NdbQueryOperation.cpp	2009-08-04 07:40:08 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryOperation.cpp	2009-08-07 09:33:27 +0000
@@ -167,7 +167,7 @@ NdbQueryOperation::setResultRowBuf (
 int
 NdbQueryOperation::setResultRowRef (
                        const NdbRecord* rec,
-                       char* & bufRef,
+                       const char* & bufRef,
                        const unsigned char* result_mask)
 {
   return m_impl.setResultRowRef(rec, bufRef, result_mask);
@@ -215,8 +215,7 @@ NdbQueryImpl::NdbQueryImpl(NdbTransactio
   {
     const NdbQueryOperationDefImpl& def = queryDef.getQueryOperation(i);
 
-    NdbQueryOperationImpl* op = def.makeOperation(*this);
-    assert(op!=NULL);
+    NdbQueryOperationImpl* op = new NdbQueryOperationImpl(*this, def);
 
     m_operations.push_back(op);
     if(def.getQueryOperationIx()==0)
@@ -304,7 +303,16 @@ NdbQueryImpl::getParameter(Uint32 num) c
 int
 NdbQueryImpl::nextResult(bool fetchAllowed, bool forceSend)
 {
-  return 1; // FIXME
+#ifdef UNUSED
+  if(!fetchAllowed){
+    for(Uint32 i = 0; i<m_operations.size(); i++){
+      if(!m_operations[i].hasNextInBuffer()){
+        return 2; 
+      }
+    }
+  }
+#endif
+  return m_operations[0]->nextResult(fetchAllowed, forceSend);
 }
 
 void
@@ -404,55 +412,32 @@ NdbQueryImpl::release(){
 }
 
 ////////////////////////////////////////////////////
-/////////  NdbQueryReceiver methods ///////////
-////////////////////////////////////////////////////
-
-NdbQueryReceiver::NdbQueryReceiver(NdbQueryOperationImpl& owner):
-  m_magic(MAGIC), 
-  m_id(owner.getQuery().getNdbTransaction()->getNdb()->theImpl
-       ->theNdbObjectIdMap.map(this)), 
-  m_owner(owner),
-  m_receiver(owner.getQuery().getNdbTransaction()->getNdb())
-{
-  m_receiver.init(NdbReceiver::NDB_OPERATION, false, NULL);
-};
-
-NdbQueryReceiver::~NdbQueryReceiver()
-{
-  if (m_id != NdbObjectIdMap::InvalidId) {
-    m_owner.getQuery().getNdbTransaction()->getNdb()->theImpl
-      ->theNdbObjectIdMap.unmap(m_id, this);
-  }
-}
-
-bool NdbQueryReceiver::execTRANSID_AI(const Uint32* ptr, Uint32 len)
-{
-  m_receiver.execTRANSID_AI(ptr, len);
-  return m_owner.execTRANSID_AI();
-}
-
-////////////////////////////////////////////////////
 /////////  NdbQueryOperationImpl methods ///////////
 ////////////////////////////////////////////////////
 
 NdbQueryOperationImpl::NdbQueryOperationImpl(
            NdbQueryImpl& queryImpl,
-           const NdbQueryOperationDefImpl& def,
-           Uint32 magic):
-  m_magic(magic),
+           const NdbQueryOperationDefImpl& def):
+  m_interface(*this),
+  m_magic(MAGIC),
   m_id(queryImpl.getNdbTransaction()->getNdb()->theImpl
        ->theNdbObjectIdMap.map(this)),
   m_operationDef(def),
-  /* Initially, a result is only expected for the root operation.*/
-  m_pendingResults(def.getQueryOperationIx() == 0 ? 1 : 0),
+  m_parents(def.getNoOfParentOperations()),
+  m_children(def.getNoOfChildOperations()),
+  m_receiver(queryImpl.getNdbTransaction()->getNdb()),
+  m_queryImpl(queryImpl),
+  m_pendingResults(0),
+  m_pendingScanTabConfs(0),
   m_userProjection(def.getTable()),
   m_resultStyle(Style_None),
-  m_queryImpl(queryImpl),
-  m_interface(*this),
-  m_parents(def.getNoOfParentOperations()),
-  m_children(def.getNoOfChildOperations())
+  m_batchBuffer(NULL),
+  m_resultBuffer(NULL),
+  m_resultRef(NULL),
+  m_isRowNull(true)
 { 
   assert(m_id != NdbObjectIdMap::InvalidId);
+  m_receiver.init(NdbReceiver::NDB_OPERATION, false, NULL);
   // Fill in operations parent refs, and append it as child of its parents
   for (Uint32 p=0; p<def.getNoOfParentOperations(); ++p)
   { 
@@ -469,6 +454,14 @@ NdbQueryOperationImpl::~NdbQueryOperatio
     m_queryImpl.getNdbTransaction()->getNdb()->theImpl
       ->theNdbObjectIdMap.unmap(m_id, this);
   }
+  if(m_batchBuffer){
+    // Check against buffer overun.
+    assert(m_batchBuffer[m_batchByteSize] == 'a' &&
+           m_batchBuffer[m_batchByteSize+1] == 'b' &&
+           m_batchBuffer[m_batchByteSize+2] == 'c' &&
+           m_batchBuffer[m_batchByteSize+3] == 'd');
+  }
+  delete[] m_batchBuffer;
 }
 
 
@@ -496,6 +489,12 @@ NdbQueryOperationImpl::getChildOperation
   return *m_children[i];
 }
 
+const NdbQueryOperationDefImpl&
+NdbQueryOperationImpl::getQueryOperationDef() const
+{
+  return m_operationDef;
+}
+
 NdbQueryImpl& 
 NdbQueryOperationImpl::getQuery() const
 {
@@ -530,16 +529,147 @@ NdbQueryOperationImpl::getValue(
   }
 }
 
+NdbRecAttr*
+NdbQueryOperationImpl::getValue(
+                            const NdbDictionary::Column* column, 
+                            char* resultBuffer)
+{
+  /* This code will only work for the lookup example in test_spj.cpp.
+   */
+  if(unlikely(m_resultStyle == Style_NdbRecord)){
+    return NULL;
+  }
+  m_resultStyle = Style_NdbRecAttr;
+  if(unlikely(m_userProjection.addColumn(*column) !=0)){
+    return NULL;
+  }
+  return m_receiver.getValue(&NdbColumnImpl::getImpl(*column), resultBuffer);
+}
+
+static bool isSetInMask(const unsigned char* mask, int bitNo){
+  return mask[bitNo>>3] & 1<<(bitNo&7);
+}
+
+int
+NdbQueryOperationImpl::setResultRowBuf (
+                       const NdbRecord *rec,
+                       char* resBuffer,
+                       const unsigned char* result_mask)
+{
+  if (rec->tableId != 
+      static_cast<Uint32>(m_operationDef.getTable().getTableId())){
+    /* The key_record and attribute_record in primary key operation do not 
+       belong to the same table.*/
+    return 4287;
+  }
+  if(unlikely(m_resultStyle==Style_NdbRecAttr)){
+    /* Cannot mix NdbRecAttr and NdbRecord methods in one operation. */
+    return 4284; 
+  }else if(unlikely(m_resultStyle==Style_NdbRecord)){
+    return QRY_RESULT_ROW_ALREADY_DEFINED;
+  }
+  m_resultStyle = Style_NdbRecord;
+  m_resultBuffer = resBuffer;
+  assert(m_batchBuffer==NULL);
+    for(Uint32 i = 0; i<rec->noOfColumns; i++){
+    if(likely(result_mask==NULL || isSetInMask(result_mask, i))){
+      m_userProjection.addColumn(*m_operationDef.getTable()
+                                 .getColumn(rec->columns[i].column_no));
+    }
+  }
+  if(getQuery().getQueryOperation(0U).isScan()){
+    // Root operation is a scan.
+    Uint32 batchSize = 0;
+    Uint32 firstBatchSize = 0;
+    m_receiver
+      .calculate_batch_size(0U, // key_size
+                            m_operationDef.getTable().getFragmentCount(),
+                            batchSize,
+                            m_batchByteSize,
+                            firstBatchSize,
+                            rec);
+    assert(batchSize!=0);
+    assert(firstBatchSize!=0);
+    m_batchBuffer = new char[m_batchByteSize+4];
+    m_receiver.do_setup_ndbrecord(rec, 
+                                  batchSize, 
+                                  0 /*key_size*/, 
+                                  0 /*read_range_no*/, 
+                                  rec->m_row_size,
+                                  m_batchBuffer,
+                                  m_userProjection.getColumnCount());
+  }else{
+    m_batchByteSize = rec->m_row_size + 4;
+    m_batchBuffer = new char[m_batchByteSize+4];
+    m_receiver.m_using_ndb_record = true;
+    m_receiver.getValues(rec, m_batchBuffer);
+  }
+  /* To be able to check for buffer overrun.*/
+  m_batchBuffer[m_batchByteSize] = 'a';
+  m_batchBuffer[m_batchByteSize+1] = 'b';
+  m_batchBuffer[m_batchByteSize+2] = 'c';
+  m_batchBuffer[m_batchByteSize+3] = 'd';
+  return 0;
+}
+
+int
+NdbQueryOperationImpl::setResultRowRef (
+                       const NdbRecord* rec,
+                       const char* & bufRef,
+                       const unsigned char* result_mask)
+{
+  m_resultRef = &bufRef;
+  return setResultRowBuf(rec, NULL, result_mask);
+}
+
+int 
+NdbQueryOperationImpl::nextResult(bool fetchAllowed, bool forceSend){
+  assert(!forceSend); // FIXME
+  const NdbQueryOperationDefImpl::Type opType 
+    = getQueryOperationDef().getType();
+  const bool isLookup = (opType == NdbQueryOperationDefImpl::PrimaryKeyAccess ||
+                         opType == NdbQueryOperationDefImpl::UniqueIndexAccess);
+
+  const char* buff = NULL;
+  if(m_receiver.nextResult()){
+    buff = m_receiver.get_row();
+  }else if(m_operationDef.getQueryOperationIx()==0){
+    // Root operation.
+    if(isLookup){
+      return 1; // No more tuples to scan.
+    }else if(fetchAllowed){
+      assert(false); // FIXME
+    }else{
+      return 2; // There are no more cached records in NdbApi
+    }
+  }else{
+    // Non-root operation
+    setNullRowDescendants();
+    return 2; // Dummy return value.
+  }
+  assert(buff!=NULL);
+  for(Uint32 i = 0; i<getNoOfChildOperations(); i++){
+    // For now, only the root can be a scan.
+    getChildOperation(i).nextResult(false, false); 
+  }
+  if(m_resultRef!=NULL){
+    *m_resultRef = buff;
+  }else{
+    memcpy(m_resultBuffer, buff, m_receiver.m_record.m_ndb_record->m_row_size);
+  }
+  return 0;
+}
+
 bool
 NdbQueryOperationImpl::isRowNULL() const
 {
-  return true;  // FIXME
+  return m_isRowNull;
 }
 
 bool
 NdbQueryOperationImpl::isRowChanged() const
 {
-  return false;  // FIXME
+  return true;
 }
 
 // Constructor.
@@ -607,45 +737,20 @@ NdbQueryOperationImpl::UserProjection::s
   return 0;
 }
 
-bool 
-NdbQueryOperationImpl::execTCKEYREF(NdbApiSignal* aSignal){
-  ndbout << "NdbQueryOperationImpl::execTCKEYREF(): *this="
-	 << *this << endl;
-  m_pendingResults--;
-  if(m_pendingResults==0){
-    /* This operation is complete. Check if the query is also complete.*/
-    return m_queryImpl.incPendingOperations(-1);
-  }else if(m_pendingResults==-1){
-    /* This happens because we received results for the child before those
-     * of the parent. This operation will be set as complete again when the 
-     * TRANSID_AI for the parent arrives.*/
-    m_queryImpl.incPendingOperations(1);
-  }
-  return false;
-}
-
-//
-// NdbQueryLookupOperationImpl method definitions.
-//
-
 #define POS_IN_PARAM(field) \
 (offsetof(QueryNodeParameters, field)/sizeof(Uint32))
 
 #define POS_IN_LOOKUP_PARAM(field) \
 (offsetof(QN_LookupParameters, field)/sizeof(Uint32)) 
 
-int NdbQueryLookupOperationImpl::prepareSend(Uint32Buffer& serializedParams)
-{
-  const NdbQueryOperationDefImpl::Type opType 
-    = getQueryOperationDef().getType();
-  assert(opType == NdbQueryOperationDefImpl::PrimaryKeyAccess ||
-         opType == NdbQueryOperationDefImpl::UniqueIndexAccess);
 
-  m_queryReceiver.getReceiver().prepareSend();
+int NdbQueryOperationImpl::prepareSend(Uint32Buffer& serializedParams)
+{
+  m_receiver.prepareSend();
   Uint32Slice lookupParams(serializedParams, serializedParams.getSize());
   Uint32 requestInfo = 0;
   lookupParams.get(POS_IN_PARAM(requestInfo)) = 0;
-  lookupParams.get(POS_IN_PARAM(resultData)) = m_queryReceiver.getId();
+  lookupParams.get(POS_IN_PARAM(resultData)) = m_id;
   Uint32Slice optional(lookupParams, POS_IN_LOOKUP_PARAM(optional));
 
   int optPos = 0;
@@ -653,7 +758,7 @@ int NdbQueryLookupOperationImpl::prepare
   //printf("operation has %d params\n", getQueryOperationDef().getNoOfParameters());
 
   // SPJ block assume PARAMS to be supplied before ATTR_LIST
-  if (false && getQueryOperationDef().getNoOfParameters() > 0)
+  if (getQueryOperationDef().getNoOfParameters() > 0)
   {
     int size = 0;
     requestInfo |= DABits::PI_KEY_PARAMS;
@@ -680,16 +785,31 @@ int NdbQueryLookupOperationImpl::prepare
   }
   lookupParams.get(POS_IN_PARAM(requestInfo)) = requestInfo;
 
+  // TODO: Implement for scans as well.
   QueryNodeParameters::setOpLen(lookupParams.get(POS_IN_PARAM(len)),
-                                QueryNodeParameters::QN_LOOKUP,
+				isScan() 
+                                  ?QueryNodeParameters::QN_SCAN_FRAG
+                                  :QueryNodeParameters::QN_LOOKUP,
 				lookupParams.getSize());
-
+  if(m_operationDef.getQueryOperationIx()==0)
+  {
+    if(isScan())
+    {
+      m_pendingResults = 0;
+      m_pendingScanTabConfs 
+        = getQueryOperationDef().getTable().getFragmentCount();
+    }
+    else 
+    {
+      m_pendingResults = 1; 
+    }
+  }
   if(unlikely(lookupParams.isMaxSizeExceeded())){
     return QRY_DEFINITION_TOO_LARGE; // Query definition too large.
   }
 #ifdef TRACE_SERIALIZATION
   ndbout << "Serialized params for node " 
-	 << getQueryOperationDef().getQueryOperationIx() << " : ";
+	 << m_operationDef.getQueryOperationIx() << " : ";
   for(Uint32 i = 0; i < lookupParams.getSize(); i++){
     char buf[12];
     sprintf(buf, "%.8x", lookupParams.get(i));
@@ -701,32 +821,34 @@ int NdbQueryLookupOperationImpl::prepare
 }
 
 
-void NdbQueryLookupOperationImpl::release(){
-  m_queryReceiver.getReceiver().release();
+void NdbQueryOperationImpl::release(){
+  m_receiver.release();
 }
 
 
 bool 
-NdbQueryLookupOperationImpl::execTRANSID_AI(){
+NdbQueryOperationImpl::execTRANSID_AI(const Uint32* ptr, Uint32 len){
   ndbout << "NdbQueryOperationImpl::execTRANSID_AI(): *this="
 	 << *this << endl;  
+  // Process result values.
+  m_receiver.execTRANSID_AI(ptr, len);
   m_pendingResults--;
   /* Receiving this message means that each child has been instantiated 
    * one more. Therefore, increment the pending message count for the children.
    */
   for(Uint32 i = 0; i<getNoOfChildOperations(); i++){
-    int& childPendingResults = getPendingResults(getChildOperation(i));
-    childPendingResults++;
-    if(childPendingResults == 0){
-      /* This child appears to be complete. Therefore decrement total count
-       * of pending operations.*/
-     m_queryImpl.incPendingOperations(-1);
-    }else if(childPendingResults == 1){
+    if(getChildOperation(i).isComplete()){
       /* This child appeared to be complete prior to receiving this message, 
        * but now we know that there will be
        * an extra instance. Therefore, increment total count of pending 
        * operations.*/
       m_queryImpl.incPendingOperations(1);
+    }    
+    getChildOperation(i).m_pendingResults++;
+    if(getChildOperation(i).isComplete()){
+      /* This child appears to be complete. Therefore decrement total count
+       * of pending operations.*/
+      m_queryImpl.incPendingOperations(-1);
     }
   }
 
@@ -743,224 +865,70 @@ NdbQueryLookupOperationImpl::execTRANSID
 }
 
 
-NdbQueryLookupOperationImpl
-::NdbQueryLookupOperationImpl(NdbQueryImpl& queryImpl, 
-                              const NdbQueryOperationDefImpl& def):
-  NdbQueryOperationImpl(queryImpl, def, MAGIC_LOOKUP),
-  m_queryReceiver(*this)
-{
-}
-
-NdbRecAttr*
-NdbQueryLookupOperationImpl::getValue(
-                            const NdbDictionary::Column* column, 
-                            char* resultBuffer)
-{
-  /* This code will only work for the lookup example in test_spj.cpp.
-   */
-  if(unlikely(m_resultStyle == Style_NdbRecord)){
-    return NULL;
-  }
-  m_resultStyle = Style_NdbRecAttr;
-  if(unlikely(m_userProjection.addColumn(*column) !=0)){
-    return NULL;
+bool 
+NdbQueryOperationImpl::execTCKEYREF(NdbApiSignal* aSignal){
+  ndbout << "NdbQueryOperationImpl::execTCKEYREF(): *this="
+	 << *this << endl;
+  if(isComplete()){
+    /* This happens because we received results for the child before those
+     * of the parent. This operation will be set as complete again when the 
+     * TRANSID_AI for the parent arrives.*/
+    m_queryImpl.incPendingOperations(1);
+  }  
+  m_pendingResults--;
+  if(isComplete()){
+    /* This operation is complete. Check if the query is also complete.*/
+    return m_queryImpl.incPendingOperations(-1);
   }
-  return m_queryReceiver.getReceiver().getValue(&NdbColumnImpl::getImpl(*column), resultBuffer);
-}
-
-static bool isSetInMask(const unsigned char* mask, int bitNo){
-  return mask[bitNo>>3] & 1<<(bitNo&7);
+  return false;
 }
 
-int
-NdbQueryLookupOperationImpl::setResultRowBuf (
-                       const NdbRecord *rec,
-                       char* resBuffer,
-                       const unsigned char* result_mask)
+void 
+NdbQueryOperationImpl::execSCAN_TABCONF(Uint32 tcPtrI, 
+                                        Uint32 rowCount)
 {
-  if (rec->tableId != 
-      static_cast<Uint32>(getQueryOperationDef().getTable().getTableId())){
-    /* The key_record and attribute_record in primary key operation do not 
-       belong to the same table.*/
-    return 4287;
-  }
-  if(unlikely(m_resultStyle==Style_NdbRecAttr)){
-    /* Cannot mix NdbRecAttr and NdbRecord methods in one operation. */
-    return 4284; 
-  }
-  m_resultStyle = Style_NdbRecord;
-  m_queryReceiver.getReceiver().m_using_ndb_record = true;
-  m_queryReceiver.getReceiver().getValues(rec, resBuffer);
-  for(Uint32 i = 0; i<rec->noOfColumns; i++){
-    if(likely(result_mask==NULL || isSetInMask(result_mask, i))){
-      m_userProjection.addColumn(*getQueryOperationDef().getTable()
-                                 .getColumn(rec->columns[i].column_no));
+  ndbout << "NdbQueryOperationImpl::execSCAN_TABCONF(): tcPtrI="
+         << tcPtrI << " rowCount=" << rowCount 
+         << " *this=" << *this << endl;
+  // For now, only the root operation may be a scan.
+  assert(m_operationDef.getQueryOperationIx()==0);
+  assert(m_pendingScanTabConfs>0);
+  m_pendingScanTabConfs--;
+  if(tcPtrI != RNIL || rowCount > 0){
+    m_receiver.execSCANOPCONF(tcPtrI, 0, rowCount);
+    m_pendingResults += rowCount;
+#ifdef UNUSED
+    for(Uint32 i = 0; i<getNoOfChildOperations(); i++){
+      if(getChildOperation(i).isComplete()){
+        /* This child appeared to be complete prior to receiving this message, 
+         * but now we know that there will be
+         * extra instances. Therefore, increment total count of pending 
+         * operations.*/
+        m_queryImpl.incPendingOperations(1);
+      }
+      getChildOperation(i).m_pendingResults += rowCount;
+      if(getChildOperation(i).isComplete()){
+        /* This child appears to be complete. Therefore decrement total count
+         * of pending operations.*/
+        m_queryImpl.incPendingOperations(-1);
+      }
     }
+#endif 
   }
-  return 0;
-}
-
-int
-NdbQueryLookupOperationImpl::setResultRowRef (
-                       const NdbRecord* rec,
-                       char* & bufRef,
-                       const unsigned char* result_mask)
-{
-/***
-  if (rec->tableId != m_table->tableId)
-  {
-    setErrorCode(4287);
-    return -1;
-  }
-***/
-  return 0; // FIXME
-}
-
-
-//
-// NdbQueryScanOperationImpl method definitions.
-//
-
-NdbQueryScanOperationImpl
-::NdbQueryScanOperationImpl(NdbQueryImpl& queryImpl, 
-                            const NdbQueryOperationDefImpl& def):
-  NdbQueryOperationImpl(queryImpl, def, MAGIC_SCAN)
-{}
-
-
-NdbQueryScanOperationImpl::~NdbQueryScanOperationImpl()
-{
-  for(int i = 0; i<m_queryReceivers.size(); i++){
-    delete m_queryReceivers[i];
+  if(isComplete()){
+    /* This operation appears to be complete*/
+    m_queryImpl.incPendingOperations(-1);
   }
 }
 
-NdbRecAttr*
-NdbQueryScanOperationImpl::getValue(
-                            const NdbDictionary::Column* column, 
-                            char* resultBuffer)
-{
-  assert(false);
-  return 0; // FIXME
-}
-
-int
-NdbQueryScanOperationImpl::setResultRowBuf (
-                       const NdbRecord *rec,
-                       char* resBuffer,
-                       const unsigned char* result_mask)
-{
-  assert(false);
-  return 0; // FIXME
-}
-
-int
-NdbQueryScanOperationImpl::setResultRowRef (
-                       const NdbRecord* rec,
-                       char* & bufRef,
-                       const unsigned char* result_mask)
-{
-  if (rec->tableId != 
-      static_cast<Uint32>(getQueryOperationDef().getTable().getTableId())){
-    /* The key_record and attribute_record in primary key operation do not 
-       belong to the same table.*/
-    return 4287;
-  }
-  if(unlikely(m_resultStyle==Style_NdbRecAttr)){
-    /* Cannot mix NdbRecAttr and NdbRecord methods in one operation. */
-    return 4284; 
-  }
-  m_resultStyle = Style_NdbRecord;
-  // FIXME
-  // m_queryReceiver.getReceiver().m_using_ndb_record = true;
-  // m_queryReceiver.getReceiver().getValues(rec, resBuffer);
-  for(Uint32 i = 0; i<rec->noOfColumns; i++){
-    if(likely(result_mask==NULL || isSetInMask(result_mask, i))){
-      m_userProjection.addColumn(*getQueryOperationDef().getTable()
-                                 .getColumn(rec->columns[i].column_no));
-    }
-  }
-  return 0;
-}
-
-int NdbQueryScanOperationImpl::prepareSend(Uint32Buffer& serializedParams)
-{
-  const NdbQueryOperationDefImpl::Type opType 
-    = getQueryOperationDef().getType();
-  assert(opType == NdbQueryOperationDefImpl::TableScan ||
-         opType == NdbQueryOperationDefImpl::OrderedIndexScan);
-
-  //m_queryReceiver.getReceiver().prepareSend();
-  Uint32Slice lookupParams(serializedParams, serializedParams.getSize());
-  Uint32 requestInfo = 0;
-  lookupParams.get(POS_IN_PARAM(requestInfo)) = 0;
-  lookupParams.get(POS_IN_PARAM(resultData)) = ptr2int();
-  Uint32Slice optional(lookupParams, POS_IN_LOOKUP_PARAM(optional));
-
-  int optPos = 0;
-
-  //printf("operation has %d params\n", getQueryOperationDef().getNoOfParameters());
-
-  // SPJ block assume PARAMS to be supplied before ATTR_LIST
-  if (false && getQueryOperationDef().getNoOfParameters() > 0)
-  {
-    int size = 0;
-    requestInfo |= DABits::PI_KEY_PARAMS;
-    Uint32Slice keyParam(optional, optPos);
-
-    // FIXME: Add parameters here, unsure about the serialized format yet
-    for (Uint32 i=0; i<getQueryOperationDef().getNoOfParameters(); i++)
-    {
-      const NdbParamOperandImpl& param = getQueryOperationDef().getParameter(i);
-      const void* paramValue = getQuery().getParamValue(param.getParamIx());
-      assert (paramValue != NULL);
-    }
-    optPos += size;
-  }
-
-  if (true)
-  {
-    requestInfo |= DABits::PI_ATTR_LIST;
-
-    const int error = m_userProjection.serialize(Uint32Slice(optional, optPos));
-    if(error!=0){
-      return error;
-    }
-  }
-  lookupParams.get(POS_IN_PARAM(requestInfo)) = requestInfo;
-
-  QueryNodeParameters::setOpLen(lookupParams.get(POS_IN_PARAM(len)),
-                                QueryNodeParameters::QN_SCAN_FRAG,
-				lookupParams.getSize());
-
-  if(unlikely(lookupParams.isMaxSizeExceeded())){
-    return QRY_DEFINITION_TOO_LARGE; // Query definition too large.
-  }
-#ifdef TRACE_SERIALIZATION
-  ndbout << "Serialized params for node " 
-	 << getQueryOperationDef().getQueryOperationIx() << " : ";
-  for(Uint32 i = 0; i < lookupParams.getSize(); i++){
-    char buf[12];
-    sprintf(buf, "%.8x", lookupParams.get(i));
-    ndbout << buf << " ";
+void 
+NdbQueryOperationImpl::setNullRowDescendants(){
+  m_isRowNull = true;
+  for(Uint32 i = 0; i<getNoOfChildOperations(); i++){
+    getChildOperation(i).setNullRowDescendants();
   }
-  ndbout << endl;
-#endif
-  return 0;
 }
 
-
-void NdbQueryScanOperationImpl::release(){
-  assert(false);
-}
-
-
-bool 
-NdbQueryScanOperationImpl::execTRANSID_AI(){
-  assert(false);
-}
-
-
 /** For debugging.*/
 NdbOut& operator<<(NdbOut& out, const NdbQueryOperationImpl& op){
   out << "[ this: " << &op
@@ -981,4 +949,4 @@ NdbOut& operator<<(NdbOut& out, const Nd
  
 // Compiler settings require explicit instantiation.
 template class Vector<NdbQueryOperationImpl*>;
-template class Vector<NdbQueryReceiver*>;
+

=== modified file 'storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp	2009-08-04 07:40:08 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp	2009-08-07 09:33:27 +0000
@@ -28,38 +28,33 @@
 #include <Vector.hpp>
 #include <NdbOut.hpp>
 
-//class NdbQueryLookupOperationDefImpl;
-//class NdbQueryScanOperationDefImpl;
-
-#ifdef UNUSED
 template<Uint32 magic>
 class NdbMappable{
 public:
+
   Uint32 getId() const {return m_id;}
+
   bool checkMagicNumber() const {return m_magic == magic;}
 protected:
+
   explicit NdbMappable(const Ndb& ndb):
     m_magic(magic),
     m_id(ndb.theImpl->theNdbObjectIdMap.map(this)),
     m_ndb(ndb)
   {}
+
   virtual ~NdbMappable(){
     if (m_id != NdbObjectIdMap::InvalidId) {
       m_ndb.theImpl->theNdbObjectIdMap.unmap(m_id, this);
     }
   }
-}
+
 private:
   const Uint32 m_magic;
   const Uint32 m_id;
   const Ndb& m_ndb;
 };
-#endif 
-/** This class is the internal implementation of NdbQuery. Unlike NdbQuery
- * it should not be exposed to application developers (NdbAPI users).
- * Like NdbQuery, this class represents an instantiation of a query (as
- * defined by NdbQueryDefImpl) for a given transaction and set of actual 
- * parameters. */
+
 class NdbQueryImpl {
 private:
   // Only constructable from factory ::buildQuery();
@@ -119,7 +114,7 @@ public:
 
   Uint32 ptr2int() const
   { return m_id; }
-
+  
   const NdbQuery& getInterface() const
   { return m_interface; }
 
@@ -166,48 +161,6 @@ private:
   const NdbQueryDefImpl& m_queryDef;
 }; // class NdbQueryImpl
 
-/** This class represents a context for processing TRANSID_AI messages.
- * Scan operation need to differentiate between messages comming from 
- * different data nodes. Therefore, a separate mappable class is needed.*/
-class NdbQueryReceiver{
-public:
-  /** Unique class ID.*/
-  STATIC_CONST( MAGIC = 0xdead8888);
-
-  explicit NdbQueryReceiver(NdbQueryOperationImpl& owner);
-  
-  ~NdbQueryReceiver();
-
-  Uint32 getId() const {return m_id;}
-
-  bool checkMagicNumber() const {return m_magic == MAGIC;}
-
-  bool execTRANSID_AI(const Uint32* ptr, Uint32 len);
-    
-  bool execTCKEYREF(NdbApiSignal* aSignal);
-
-  NdbReceiver& getReceiver(){return m_receiver;}
-
-  const NdbReceiver& getReceiver() const {return m_receiver;}
-
-  NdbTransaction* getNdbTransaction() const; 
-
-  NdbOperation* getNdbOperation() const;
-private:
-  /** For verifying pointers to instances of this class.*/
-  const Uint32 m_magic;
-  /** Id to be used in global object map.*/
-  const Uint32 m_id;
-  /** Operation to which this instance belong.*/
-  NdbQueryOperationImpl& m_owner;
-  /** For extracting tuple values.*/
-  NdbReceiver m_receiver;
-  /** Copy not allowed.*/
-  NdbQueryReceiver(const NdbQueryReceiver&);
-  /** Copy not allowed.*/
-  NdbQueryReceiver& operator=(const NdbQueryReceiver&);
-};
-
 
 /** This class contains data members for NdbQueryOperation, such that these
  * do not need to exposed in NdbQueryOperation.hpp. This class may be 
@@ -217,14 +170,12 @@ class NdbQueryOperationImpl {
   /** For debugging.*/
   friend NdbOut& operator<<(NdbOut& out, const NdbQueryOperationImpl&);
 public:
-  STATIC_CONST (MAGIC_LOOKUP = 0xfade1234);
-  STATIC_CONST (MAGIC_SCAN = 0xfade4321);
+  STATIC_CONST (MAGIC = 0xfade1234);
 
 
   explicit NdbQueryOperationImpl(NdbQueryImpl& queryImpl, 
-                                 const NdbQueryOperationDefImpl& def,
-                                 Uint32 magic);
-  virtual ~NdbQueryOperationImpl() = 0;
+                                 const NdbQueryOperationDefImpl& def);
+  ~NdbQueryOperationImpl();
 
   Uint32 getNoOfParentOperations() const;
   NdbQueryOperationImpl& getParentOperation(Uint32 i) const;
@@ -232,30 +183,38 @@ public:
   Uint32 getNoOfChildOperations() const;
   NdbQueryOperationImpl& getChildOperation(Uint32 i) const;
 
-  const NdbQueryOperationDefImpl& getQueryOperationDef() const 
-  {return m_operationDef;}
+  const NdbQueryOperationDefImpl& getQueryOperationDef() const;
 
   // Get the entire query object which this operation is part of
   NdbQueryImpl& getQuery() const;
 
   NdbRecAttr* getValue(const char* anAttrName, char* resultBuffer);
   NdbRecAttr* getValue(Uint32 anAttrId, char* resultBuffer);
-  virtual NdbRecAttr* getValue(const NdbDictionary::Column*, 
-                               char* resultBuffer) = 0;
+  NdbRecAttr* getValue(const NdbDictionary::Column*, char* resultBuffer);
 
-  virtual int setResultRowBuf (const NdbRecord *rec,
-                               char* resBuffer,
-                               const unsigned char* result_mask) = 0;
-
-  virtual int setResultRowRef (const NdbRecord* rec,
-                               char* & bufRef,
-                               const unsigned char* result_mask) = 0;
+  int setResultRowBuf (const NdbRecord *rec,
+                       char* resBuffer,
+                       const unsigned char* result_mask);
+
+  int setResultRowRef (const NdbRecord* rec,
+                       const char* & bufRef,
+                       const unsigned char* result_mask);
 
   bool isRowNULL() const;    // Row associated with Operation is NULL value?
 
   bool isRowChanged() const; // Prev ::nextResult() on NdbQuery retrived a new
                              // value for this NdbQueryOperation
 
+  /** Fetch next result row. 
+   * @see NdbQuery::nextResult */
+  int nextResult(bool fetchAllowed, bool forceSend);
+
+#ifdef UNUSED
+  /** Returns true if this operation has more resulst available in its buffers
+   * (i.e. *without�� doing a fetch.)*/
+  bool hasNextInBuffer() const {return m_receiver.nextResult()};
+#endif
+
   /** Returns an I-value for the NdbReceiver object that shall receive results
    * for this operation. 
    * @return The I-value.
@@ -266,35 +225,44 @@ public:
 
 
   /** Process result data for this operation. Return true if query complete.*/
-  virtual bool execTRANSID_AI() = 0;
+  bool execTRANSID_AI(const Uint32* ptr, Uint32 len);
   
   /** Process absence of result data for this operation. 
    * Return true if query complete.*/
   bool execTCKEYREF(NdbApiSignal* aSignal);
 
+  /** Scan batch is complete.*/
+  void execSCAN_TABCONF(Uint32 tcPtrI, Uint32 rowCount); 
+
   /** Prepare for execution. 
    *  @return possible error code.*/
-  virtual int prepareSend(Uint32Buffer& serializedParams) = 0;
+  int prepareSend(Uint32Buffer& serializedParams);
 
   /** Release NdbReceiver objects.*/
-  virtual void release() = 0;
+  void release();
 
+  /* TODO: Remove this method. Only needed in spj_test.cpp.*/
   /** Return I-value for putting in object map.*/
   Uint32 ptr2int() const {
     return m_id;
   }
   
   /** Verify magic number.*/
-  bool checkMagicNumber() const {
-    /*This could also have been a vitual function, but invoking a vitual 
-      function on an object of uncertain type is unsafe.*/
-    return m_magic == MAGIC_LOOKUP || m_magic == MAGIC_SCAN;
+  bool checkMagicNumber() const { 
+    return m_magic == MAGIC;
   }
 
-  /** Check if operation is complete. 
-   * For doing sanity check on internal state.*/
+  /** Check if operation is complete. */
   bool isComplete() const { 
-    return m_pendingResults==0;
+    return m_pendingResults==0 && m_pendingScanTabConfs==0;
+  }
+
+  /** Return true if this operation is a scan.*/
+  bool isScan() const {
+    return getQueryOperationDef().getType() 
+      == NdbQueryOperationDefImpl::TableScan ||
+      getQueryOperationDef().getType() 
+      == NdbQueryOperationDefImpl::OrderedIndexScan;
   }
 
   const NdbQueryOperation& getInterface() const
@@ -302,7 +270,7 @@ public:
   NdbQueryOperation& getInterface()
   { return m_interface; }
 
-protected:
+private:
   /** This class represents a projection that shall be sent to the 
    * application.*/
   class UserProjection{
@@ -319,6 +287,9 @@ protected:
      * @return Possible error code.*/
     int serialize(Uint32Slice dst) const;
     
+    /** Get number of columns.*/
+    int getColumnCount() const {return m_columnCount;}
+
   private:
     /** The columns that consitutes the projection.*/
     const NdbDictionary::Column* m_columns[MAX_ATTRIBUTES_IN_TABLE];
@@ -335,7 +306,8 @@ protected:
     int m_maxColNo;
   }; // class UserProjection
 
-  // TODO: Remove this.
+  /** Interface for the application developer.*/
+  NdbQueryOperation m_interface;
   /** For verifying pointers to this class.*/
   const Uint32 m_magic;
   /** I-value for object maps.*/
@@ -343,130 +315,44 @@ protected:
   /** The (transaction independent ) definition from which this instance
    * was created.*/
   const NdbQueryOperationDefImpl& m_operationDef;
+  /* TODO: replace m_children and m_parents with navigation via 
+   * m_operationDef.getParentOperation() etc.*/
+  /** Parents of this operation.*/
+  Vector<NdbQueryOperationImpl*> m_parents;
+  /** Children of this operation.*/
+  Vector<NdbQueryOperationImpl*> m_children;
+  /** For processing results from this operation.*/
+  NdbReceiver m_receiver;
+  /** NdbQuery to which this operation belongs. */
+  NdbQueryImpl& m_queryImpl;
   /** Number of pending TCKEYREF or TRANSID_AI messages for this operation.*/
   int m_pendingResults;
+  /** Number of pending SCAN_TABCONF messages for this operation.*/
+  int m_pendingScanTabConfs;
   /** Projection to be sent to the application.*/
   UserProjection m_userProjection;
   /** NdbRecord and old style result retrieval may not be combined.*/
-  enum{
+  enum {
     Style_None,       // Not set yet.
     Style_NdbRecord,  // Use old style result retrieval.
     Style_NdbRecAttr, // Use NdbRecord.
   } m_resultStyle;
-  /** NdbQuery to which this operation belongs. */
-  NdbQueryImpl& m_queryImpl;
-
-  /** In some places we need to access 
-   * NdbQueryOperationImpl::m_PendingResults without knowing the run-time 
-   * class of the NdbQueryOperationImpl instance. C++ forbids accessing
-   * protected non-static members from a derived class via a pointer or 
-   * reference to the base class.*/
-  static int& getPendingResults(NdbQueryOperationImpl& impl)
-  { return impl.m_pendingResults;}
-private:
-
-  /** Interface for the application developer.*/
-  NdbQueryOperation m_interface;
-  /* TODO: replace m_children and m_parents with navigation via 
-   * m_operationDef.getParentOperation() etc.*/
-  /** Parents of this operation.*/
-  Vector<NdbQueryOperationImpl*> m_parents;
-  /** Children of this operation.*/
-  Vector<NdbQueryOperationImpl*> m_children;
+  /** For temporary storing one result batch.*/
+  char* m_batchBuffer;
+  /** Buffer for final storage of result.*/
+  char* m_resultBuffer;
+  /** Pointer to application pointer that should be set to point to the 
+   * current row.
+   * @see NdbQueryOperationImpl::setResultRowRef */
+  const char** m_resultRef;
+  /** True if this operation gave no result for the current row.*/
+  bool m_isRowNull;
+  /** Batch size for scans or lookups with scan parents.*/
+  Uint32 m_batchByteSize;
+  /** Set m_isRowNull for this operation and all its descendants.*/
+  void setNullRowDescendants();
 }; // class NdbQueryOperationImpl
 
-class NdbQueryLookupOperationImpl: public NdbQueryOperationImpl{
-public:
-  explicit 
-  NdbQueryLookupOperationImpl(NdbQueryImpl& queryImpl, 
-                              const NdbQueryOperationDefImpl& def);
-
-  virtual ~NdbQueryLookupOperationImpl(){}
-
-  /** Process result data for this operation. Return true if query complete.*/
-  virtual bool execTRANSID_AI();
-  
-  /** Prepare for execution. 
-   *  @return possible error code.*/
-  virtual int prepareSend(Uint32Buffer& serializedParams);
-
-  /** Release NdbReceiver objects.*/
-  virtual void release();
-
-  /** Verify magic number.*/
-  bool checkMagicNumber() const {
-    return m_magic == MAGIC_LOOKUP;
-  }
-
-  virtual NdbRecAttr* getValue(const NdbDictionary::Column*, 
-                               char* resultBuffer);
-
-  virtual int setResultRowBuf (const NdbRecord *rec,
-                               char* resBuffer,
-                               const unsigned char* result_mask);
-
-  virtual int setResultRowRef (const NdbRecord* rec,
-                               char* & bufRef,
-                               const unsigned char* result_mask);
-
-private:
-  /** For processing results from this operation.*/
-  NdbQueryReceiver m_queryReceiver;
-
-}; // class NdbQueryLookupOperationImpl
-
-class NdbQueryScanOperationImpl: public NdbQueryOperationImpl{
-public:
-  explicit 
-  NdbQueryScanOperationImpl(NdbQueryImpl& queryImpl, 
-                            const NdbQueryOperationDefImpl& def);
-
-  virtual ~NdbQueryScanOperationImpl();
-
-  /** Process result data for this operation. Return true if query complete.*/
-  virtual bool execTRANSID_AI();
-  
-  /** Prepare for execution. 
-   *  @return possible error code.*/
-  virtual int prepareSend(Uint32Buffer& serializedParams);
-
-  /** Release NdbReceiver objects.*/
-  virtual void release();
-
-  /** Verify magic number.*/
-  bool checkMagicNumber() const {
-    return m_magic == MAGIC_SCAN;
-  }
-
-  virtual NdbRecAttr* getValue(const NdbDictionary::Column*, 
-                               char* resultBuffer);
-
-  virtual int setResultRowBuf (const NdbRecord *rec,
-                               char* resBuffer,
-                               const unsigned char* result_mask);
-
-  virtual int setResultRowRef (const NdbRecord* rec,
-                               char* & bufRef,
-                               const unsigned char* result_mask);
-private:
-  Vector<NdbQueryReceiver*> m_queryReceivers;
-}; // class NdbQueryScanOperationImpl
-
 
-inline NdbTransaction* 
-NdbQueryReceiver::getNdbTransaction() const 
-{
-  return m_owner.getQuery().getNdbTransaction();
-}
-
-inline bool 
-NdbQueryReceiver::execTCKEYREF(NdbApiSignal* aSignal){ 
-  return m_owner.execTCKEYREF(aSignal);
-}
-
-inline NdbOperation* 
-NdbQueryReceiver::getNdbOperation() const {
-  return m_owner.getQuery().getNdbOperation();
-}
 
 #endif

=== modified file 'storage/ndb/src/ndbapi/NdbReceiver.cpp'
--- a/storage/ndb/src/ndbapi/NdbReceiver.cpp	2009-06-25 09:13:50 +0000
+++ b/storage/ndb/src/ndbapi/NdbReceiver.cpp	2009-08-07 09:33:27 +0000
@@ -146,7 +146,7 @@ NdbReceiver::calculate_batch_size(Uint32
                                   Uint32& batch_size,
                                   Uint32& batch_byte_size,
                                   Uint32& first_batch_size,
-                                  const NdbRecord *record)
+                                  const NdbRecord *record) const
 {
   TransporterFacade *tp= m_ndb->theImpl->m_transporter_facade;
   Uint32 max_scan_batch_size= tp->get_scan_batch_size();

=== modified file 'storage/ndb/src/ndbapi/NdbTransactionScan.cpp'
--- a/storage/ndb/src/ndbapi/NdbTransactionScan.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/ndbapi/NdbTransactionScan.cpp	2009-08-07 09:33:27 +0000
@@ -32,7 +32,7 @@
 #include <signaldata/ScanTab.hpp>
 
 #include <NdbOut.hpp>
-
+#include <NdbQueryOperationImpl.hpp>
 
 /***************************************************************************
  * int  receiveSCAN_TABREF(NdbApiSignal* aSignal)
@@ -110,10 +110,21 @@ NdbTransaction::receiveSCAN_TABCONF(NdbA
       NdbReceiver* tOp = theNdb->void2rec(tPtr);
       if (tOp && tOp->checkMagicNumber())
       {
-	if (tcPtrI == RNIL && opCount == 0)
-	  theScanningOp->receiver_completed(tOp);
-	else if (tOp->execSCANOPCONF(tcPtrI, totalLen, opCount))
-	  theScanningOp->receiver_delivered(tOp);
+        NdbOperation* const owner = static_cast<NdbOperation*>(tOp->m_owner);
+        if(owner->checkMagicNumber()==0 && owner->m_isLinked)
+        {
+          assert(len==3);
+          owner->m_queryImpl->getQueryOperation(0u)
+            .execSCAN_TABCONF(tcPtrI, opCount);
+        }
+        else
+        {
+          if (tcPtrI == RNIL && opCount == 0)
+            theScanningOp->receiver_completed(tOp);
+          else if (tOp->execSCANOPCONF(tcPtrI, totalLen, opCount))
+            theScanningOp->receiver_delivered(tOp);
+          
+        }
       }
     }
     return 0;

=== modified file 'storage/ndb/src/ndbapi/Ndbif.cpp'
--- a/storage/ndb/src/ndbapi/Ndbif.cpp	2009-08-04 07:40:08 +0000
+++ b/storage/ndb/src/ndbapi/Ndbif.cpp	2009-08-07 09:33:27 +0000
@@ -400,14 +400,14 @@ Ndb::handleReceivedSignal(NdbApiSignal* 
     tFirstDataPtr = int2void(tFirstData);
     if (tFirstDataPtr){
       NdbReceiver* const tRec = void2rec(tFirstDataPtr);
-      NdbQueryReceiver* const queryReceiver 
-	= reinterpret_cast<NdbQueryReceiver*>(tFirstDataPtr);
+      NdbQueryOperationImpl* const queryOpImpl 
+	= reinterpret_cast<NdbQueryOperationImpl*>(tFirstDataPtr);
       const bool isReceiver = tRec->checkMagicNumber();
-      if(!isReceiver && !queryReceiver->checkMagicNumber()){
+      if(!isReceiver && !queryOpImpl->checkMagicNumber()){
 	return;
       }
       tCon = isReceiver ? tRec->getTransaction() 
-	: queryReceiver->getNdbTransaction();
+	: queryOpImpl->getQuery().getNdbTransaction();
       if((tCon!=NULL) &&
 	 tCon->checkState_TransId(((const TransIdAI*)tDataPtr)->transId)){
 	Uint32 com;
@@ -415,7 +415,7 @@ Ndb::handleReceivedSignal(NdbApiSignal* 
 	  if(isReceiver){
 	    com = tRec->execTRANSID_AI(ptr[0].p, ptr[0].sz);
 	  }else{
-	    com = queryReceiver->execTRANSID_AI(ptr[0].p, ptr[0].sz);
+	    com = queryOpImpl->execTRANSID_AI(ptr[0].p, ptr[0].sz);
 	  }
 	} else {
 	  assert(isReceiver);
@@ -526,8 +526,8 @@ Ndb::handleReceivedSignal(NdbApiSignal* 
 
       const bool isNdbOperation = 
         void2rec(tFirstDataPtr)->checkMagicNumber();
-      NdbQueryReceiver* const queryReceiver 
-        = reinterpret_cast<NdbQueryReceiver*>(tFirstDataPtr);
+      NdbQueryOperationImpl* const queryOpImpl 
+        = reinterpret_cast<NdbQueryOperationImpl*>(tFirstDataPtr);
 
       if (isNdbOperation) {
         tOp = void2rec_op(tFirstDataPtr);
@@ -535,28 +535,30 @@ Ndb::handleReceivedSignal(NdbApiSignal* 
          * an NdbOperation.*/
         assert(tOp->checkMagicNumber()==0); 
         tCon = tOp->theNdbCon;
-      } else if(queryReceiver->checkMagicNumber()) {
-        tCon = queryReceiver->getNdbTransaction();
+      } else if(queryOpImpl->checkMagicNumber()) {
+        tCon = queryOpImpl->getQuery().getNdbTransaction();
       } else{ 
         goto InvalidSignal;
       }
       if (tCon != NULL) {
-        if (tCon->theSendStatus == NdbTransaction::sendTC_OP) {
-          if(isNdbOperation){
-            tReturnCode = tOp->receiveTCKEYREF(aSignal);
-            if (tReturnCode != -1) {
-              completedTransaction(tCon);
-              return;
-            }//if
-          } else {
-            if(queryReceiver->execTCKEYREF(aSignal) &&
-               tCon->OpCompleteFailure(queryReceiver->getNdbOperation()) != -1){
-              completedTransaction(tCon);
-              return;
-            }//if 
+        if(isNdbOperation){
+          if (tCon->theSendStatus != NdbTransaction::sendTC_OP) {
+            goto InvalidSignal;
+          }
+          tReturnCode = tOp->receiveTCKEYREF(aSignal);
+          if (tReturnCode != -1) {
+            completedTransaction(tCon);
+            return;
           }//if
-          break;
+        } else {
+          if(queryOpImpl->execTCKEYREF(aSignal) &&
+             tCon->OpCompleteFailure(queryOpImpl->getQuery()
+                                     .getNdbOperation()) != -1){
+            completedTransaction(tCon);
+            return;
+          }//if 
         }//if
+        break;
       }//if (tCon != NULL)
       goto InvalidSignal;
       return;

=== modified file 'storage/ndb/src/ndbapi/TransporterFacade.cpp'
--- a/storage/ndb/src/ndbapi/TransporterFacade.cpp	2009-05-28 09:12:44 +0000
+++ b/storage/ndb/src/ndbapi/TransporterFacade.cpp	2009-08-07 09:33:27 +0000
@@ -1960,8 +1960,11 @@ void PollGuard::wait_for_input(int wait_
   }
 }
 
+bool stopHere = false;
+
 void PollGuard::unlock_and_signal()
 {
+  // assert(!stopHere);
   NdbWaiter *t_signal_cond_waiter= 0;
   if (!m_locked)
     return;

=== modified file 'storage/ndb/src/ndbapi/ndberror.c'
--- a/storage/ndb/src/ndbapi/ndberror.c	2009-07-24 08:55:33 +0000
+++ b/storage/ndb/src/ndbapi/ndberror.c	2009-08-07 09:33:27 +0000
@@ -743,6 +743,8 @@ ErrorBundle ErrorCodes[] = {
     "Query definition too large." },
   { QRY_DUPLICATE_COLUMN_IN_PROJ, DMEC, AE, 
     "Duplicate column in result projection for NdbQueryOperation."},
+  { QRY_RESULT_ROW_ALREADY_DEFINED, DMEC, AE, 
+    "Result row already defined for NdbQueryOperation."},
 
 
   { NO_CONTACT_WITH_PROCESS, DMEC, AE,

=== modified file 'storage/ndb/test/tools/test_spj.cpp'
--- a/storage/ndb/test/tools/test_spj.cpp	2009-08-03 07:58:08 +0000
+++ b/storage/ndb/test/tools/test_spj.cpp	2009-08-07 09:33:27 +0000
@@ -43,7 +43,7 @@ paramSpec: 00050001 00000001 00010000 00
 #include <NdbQueryOperation.hpp>
 #include <NdbQueryBuilder.hpp>
 // 'impl' classes is needed for prototype only.
-//#include <NdbQueryOperationImpl.hpp>
+#include <NdbQueryOperationImpl.hpp>
 //#include "NdbQueryBuilderImpl.hpp"
 
 
@@ -567,36 +567,70 @@ int testSerialize(int argc, char** argv)
   if(scan){
     const NdbRecord* resultRec = tab->getDefaultRecord();
     assert(resultRec!=NULL);
-    /*const NdbQueryScanOperationDef* scanOpDef = */ qb->scanTable(tab);
+    const NdbQueryScanOperationDef* scanOpDef = qb->scanTable(tab);
+    const NdbQueryOperand* linkKey[] = 
+      {  qb->linkedValue(scanOpDef, "b0"),
+         qb->linkedValue(scanOpDef, "a0"),
+         0
+      };
+    const NdbQueryLookupOperationDef* readLinked = qb->readTuple(tab, linkKey);
+    if (readLinked == NULL) APIERROR(qb->getNdbError());
     const NdbQueryDef* const scanDef = qb->prepare();
     NdbTransaction* myTransaction= myNdb.startTransaction();
     const void* params[] = {NULL};
     // Instantiate NdbQuery for this transaction.
     NdbQuery* query = myTransaction->createQuery(scanDef, params);
-    Uint32 results[10][6];
-    char* resultPtr = NULL;
-    NdbQueryOperation* const op = query->getQueryOperation(0U);
-    op->setResultRowRef(resultRec, resultPtr);
-    myTransaction->execute(NoCommit);
+    //Uint32 results[10][6];
+    //char* resultPtr = NULL;
+    const Uint32* scanResultPtr;
+    //const unsigned char* mask = NULL;
+    NdbQueryOperation* const scanOp = query->getQueryOperation(0U);
+    scanOp->setResultRowRef(resultRec, 
+                        reinterpret_cast<const char*&>(scanResultPtr),
+                        NULL);
+    const Uint32* lookupResultPtr;
+    NdbQueryOperation* const lookupOp = query->getQueryOperation(1);
+    lookupOp->setResultRowRef(resultRec, 
+                              reinterpret_cast<const char*&>(lookupResultPtr),
+                              NULL);
+    //extern bool stopHere;
+    //stopHere = true;
+    assert(myTransaction->execute(NoCommit)==0);
+    while(!(scanOp->getImpl().isComplete() && 
+            lookupOp->getImpl().isComplete())){
+      sleep(1);
+    }
     bool done = false;
     int rowNo = 0;
     while(!done){
-      resultPtr = reinterpret_cast<char*>(results[rowNo]);
-      int retVal = query->nextResult();
+      // scanResultPtr = reinterpret_cast<char*>(results[rowNo]);
+      const int retVal = query->nextResult();
       switch(retVal){
       case 0:
         break;
       case 1:
         done = true;
         break;
+      case 2:
+        ndbout << "No more results in buffer";
+        done = true;
+        break;
       default:
         assert(false);
       };
-      for(int i = 0; i<6; i++){
-        ndbout << results[rowNo][i] << " ";
+      if(!done){
+        ndbout << "Scan row: " << rowNo << endl;
+        for(int i = 0; i<6; i++){
+          ndbout << scanResultPtr[i] << " ";
+        }
+        ndbout << endl;
+        ndbout << "Loopkup row: " << rowNo << endl;
+        for(int i = 0; i<6; i++){
+          ndbout << lookupResultPtr[i] << " ";
+        }
+        ndbout << endl;
+        rowNo++;
       }
-      ndbout << endl;
-      rowNo++;
     }
   }else{
     /* Dummy for now at least. Key is really set with  ndbOperation->equal().*/

Attachment: [text/bzr-bundle] bzr/jan.wedvik@sun.com-20090807093327-ugnnin00eqbzzd06.bundle
Thread
bzr commit into mysql-5.1-telco-7.0-spj branch (jan.wedvik:2940) Jan Wedvik7 Aug