List:Commits« Previous MessageNext Message »
From:Jan Wedvik Date:July 7 2010 1:25pm
Subject:bzr push into mysql-5.1-telco-7.0-spj branch (jan.wedvik:3194 to 3196)
View as plain text  
 3196 Jan Wedvik	2010-07-07
      This commit contains an initial version of result handling for NdbQuery with
      multiple scans. The code has not been tested much. It will not work
      for (left) outer joins or queries with a lookup root and a scan descendant.
      This code iterates over results from the lowest scan and uppwards. This may
      or or may not be rewritten into going from the root and downwards, depending 
      on what appears to give the simplest code and best performance.
      
      The code may be tested using the following SQL script:
      
      create database spj_ndb;
      use spj_ndb;
      set ndb_join_pushdown = true;
      
      create table t1 (pk int primary key, a int, b int) engine=ndb;
      create index ix1 on t1(b,a);
      
      insert into t1 values (0,10,10);
      insert into t1 values (1,10,10);
      insert into t1 values (2,10,10);
      
      select * from t1 as x join t1 as y on y.b=x.b;
      
      select * from t1 as x join t1 as y join t1 as z on x.a=y.b and y.a=z.b;

    modified:
      storage/ndb/include/ndbapi/NdbQueryBuilder.hpp
      storage/ndb/src/ndbapi/NdbQueryOperation.cpp
      storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp
 3195 Jan Wedvik	2010-07-07 [merge]
      Merged refactorings from main SPJ branch (bk-internal.mysql.com/bzrroot/server/mysql-5.1-telco-7.0-spj/)

    modified:
      storage/ndb/src/ndbapi/NdbQueryOperation.cpp
 3194 Ole John Aske	2010-07-07 [merge]
      Merge fix of multicolumn bounds from main SPJ branch

    modified:
      sql/ha_ndbcluster.cc
      storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp
=== modified file 'storage/ndb/include/ndbapi/NdbQueryBuilder.hpp'
--- a/storage/ndb/include/ndbapi/NdbQueryBuilder.hpp	2010-07-02 09:06:49 +0000
+++ b/storage/ndb/include/ndbapi/NdbQueryBuilder.hpp	2010-07-07 13:24:23 +0000
@@ -32,6 +32,12 @@ class NdbQueryOptionsImpl;
 class NdbQueryOperandImpl;
 class NdbQueryOperationDefImpl;
 
+/** 
+ * This is the maximal number of table access operations there can be in a 
+ * single pushed join.
+ */
+static const uint NdbMaxPushedQueryOps = 32;
+
 /**
  * This is the API interface for building a (composite) query definition,
  * possibly existing of multiple operations linked together (aka 'joined')

=== modified file 'storage/ndb/src/ndbapi/NdbQueryOperation.cpp'
--- a/storage/ndb/src/ndbapi/NdbQueryOperation.cpp	2010-07-06 13:11:29 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryOperation.cpp	2010-07-07 13:24:23 +0000
@@ -135,13 +135,16 @@ class NdbRootFragment {
 public:
   explicit NdbRootFragment();
 
+  ~NdbRootFragment()
+  { delete[] m_tupleIdx; }
+
   /**
    * Initialize object.
    * @param query Enclosing query.
    * @param fragNo This object manages state for reading from the fragNo'th 
    * fragment that the root operation accesses.
    */
-  void init(NdbQueryImpl& query, Uint32 fragNo); 
+  Uint32 init(NdbQueryImpl& query, Uint32 fragNo); 
 
   Uint32 getFragNo() const
   { return m_fragNo; }
@@ -161,16 +164,7 @@ public:
     m_outstandingResults = 0;
   }
 
-  void setConfReceived()
-  { 
-    assert(!m_confReceived);
-    m_confReceived = true; 
-  }
-
-  void setConfReceivedNoCheck()
-  {
-    m_confReceived = true;
-  }
+  void setConfReceived();
 
   /** 
    * The root operation will read from a number of fragments of a table.
@@ -206,8 +200,11 @@ public:
    * @return True if there are no more results from this root fragment (for 
    * the current batch).
    */
-  bool isEmpty() const
-  { return !m_query->getQueryOperation(0U).getReceiver(m_fragNo).nextResult(); }
+  bool isEmpty() const;
+
+  void buildResultIndex();
+
+  bool fetchScanResults();
 
 private:
   STATIC_CONST( voidFragNo = 0xffffffff);
@@ -235,6 +232,11 @@ private:
    * Each element is true iff a SCAN_TABCONF (for that fragment) or 
    * TCKEYCONF message has been received */
   bool m_confReceived;
+
+  Uint32 m_currentTuple;
+
+  Uint16 *m_tupleIdx;
+
 }; //NdbRootFragment
 
 
@@ -300,6 +302,17 @@ public:
   /** For debugging.*/
   friend NdbOut& operator<<(NdbOut& out, const NdbResultStream&);
 
+  Uint16 getParentId(Uint16 tupleNo) const
+  { return m_tupleSet[tupleNo].m_parentId; }
+
+  Uint16 getTupleId(Uint16 tupleNo) const
+  { return m_tupleSet[tupleNo].m_tupleId; }
+
+  Uint16 findTupleWithParentId(Uint16 parentId) const;
+
+  Uint16 findTupleWithId(Uint16 tupleId) const;
+
+private:
   /** This stream handles results derived from the m_rootFragNo'th 
    * fragment of the root operation.*/
   const Uint32 m_rootFragNo;
@@ -326,9 +339,9 @@ public:
    *
    *  - A HashMap on 'm_parentId' used to locate tuples correlated
    *    to a parent tuple. Indexes by hashing the parentId such that:
-   *     - [hash(parentId)].m_hash_head will then index the first
+   *     - [hash(parentId)].m_parentHashHead will then index the first
    *       TupleSet entry potential containing the parentId to locate.
-   *     - .m_hash_next in the indexed TupleSet may index the next TupleSet 
+   *     - .m_parentHashNext in the indexed TupleSet may index the next TupleSet 
    *       to considder.
    *       
    * Both the child/parent correlation set and the parentId HashMap has been
@@ -344,8 +357,12 @@ public:
     Uint16 m_parentId;  // Id of parent tuple which this tuple is correlated with
     Uint16 m_tupleId;   // Id of this tuple
 
-    Uint16 m_hash_head; // Index of first item in TupleSet[] matching a hashed parentId.
-    Uint16 m_hash_next; // 'next' index matching 
+    Uint16 m_hashHead; // Index of first item in TupleSet[] matching a parentId.
+
+    Uint16 m_hashNext; // 'next' index matching 
+
+    Uint16 m_parentHashHead; // Index of first item in TupleSet[] matching a hashed parentId.
+    Uint16 m_parentHashNext; // 'next' index matching 
 
     explicit TupleSet()
     {}
@@ -364,11 +381,6 @@ public:
                          Uint16 tupleId, 
                          Uint16 tupleNo);
 
-  Uint16 getTupleId(Uint16 tupleNo) const
-  { return m_tupleSet[tupleNo].m_tupleId; }
-
-  Uint16 findTupleWithParentId(Uint16 parentId) const;
-
   /** No copying.*/
   NdbResultStream(const NdbResultStream&);
   NdbResultStream& operator=(const NdbResultStream&);
@@ -437,7 +449,8 @@ NdbResultStream::clearParentChildMap()
   {
     m_tupleSet[i].m_parentId = tupleNotFound;
     m_tupleSet[i].m_tupleId  = tupleNotFound;
-    m_tupleSet[i].m_hash_head = m_maxRows;
+    m_tupleSet[i].m_hashHead = m_maxRows;
+    m_tupleSet[i].m_parentHashHead = m_maxRows;
   }
 }
 
@@ -450,12 +463,19 @@ NdbResultStream::setParentChildMap(Uint1
   m_tupleSet[tupleNo].m_parentId = parentId;
   m_tupleSet[tupleNo].m_tupleId  = tupleId;
 
+  /* Insert tupleId in HashMap */
+  const Uint16 hash = (tupleId % m_maxRows);
+  m_tupleSet[tupleNo].m_hashNext = 
+    m_tupleSet[hash].m_hashHead;
+  m_tupleSet[hash].m_hashHead  = tupleNo;
+
   /* Insert parentId in HashMap */
   if (parentId != tupleNotFound)
   {
-    const Uint16 hash = (parentId % m_maxRows);
-    m_tupleSet[tupleNo].m_hash_next = m_tupleSet[hash].m_hash_head;
-    m_tupleSet[hash].m_hash_head  = tupleNo;
+    const Uint16 parentHash = (parentId % m_maxRows);
+    m_tupleSet[tupleNo].m_parentHashNext = 
+      m_tupleSet[parentHash].m_parentHashHead;
+    m_tupleSet[parentHash].m_parentHashHead  = tupleNo;
   }
 }
 
@@ -465,13 +485,30 @@ NdbResultStream::findTupleWithParentId(U
   assert (m_operation.getQueryDef().isScanQuery());
 
   const Uint16 hash = (parentId % m_maxRows);
-  Uint16 tupleIx = m_tupleSet[hash].m_hash_head;
+  Uint16 tupleIx = m_tupleSet[hash].m_parentHashHead;
   while (tupleIx < m_maxRows)
   {
     if (m_tupleSet[tupleIx].m_parentId == parentId)
       return tupleIx;
 
-    tupleIx = m_tupleSet[tupleIx].m_hash_next;
+    tupleIx = m_tupleSet[tupleIx].m_parentHashNext;
+  }
+  return tupleNotFound;
+}
+
+Uint16
+NdbResultStream::findTupleWithId(Uint16 tupleId) const
+{
+  assert (m_operation.getQueryDef().isScanQuery());
+
+  const Uint16 hash = (tupleId % m_maxRows);
+  Uint16 tupleIx = m_tupleSet[hash].m_hashHead;
+  while (tupleIx < m_maxRows)
+  {
+    if (m_tupleSet[tupleIx].m_tupleId == tupleId)
+      return tupleIx;
+
+    tupleIx = m_tupleSet[tupleIx].m_hashNext;
   }
   return tupleNotFound;
 }
@@ -524,16 +561,20 @@ NdbRootFragment::NdbRootFragment():
   m_query(NULL),
   m_fragNo(voidFragNo),
   m_outstandingResults(0),
-  m_confReceived(false)
+  m_confReceived(false),
+  m_currentTuple(0),
+  m_tupleIdx(NULL)
 {
 }
 
 
-void NdbRootFragment::init(NdbQueryImpl& query, Uint32 fragNo)
+Uint32 NdbRootFragment::init(NdbQueryImpl& query, Uint32 fragNo)
 {
   assert(m_fragNo==voidFragNo);
   m_query = &query;
   m_fragNo = fragNo;
+  m_tupleIdx = new Uint16[query.getMaxBatchRows()*(query.getScanCount()+1)];
+  return m_tupleIdx == NULL ? Err_MemoryAlloc : 0; 
 }
 
 void NdbRootFragment::reset()
@@ -541,15 +582,170 @@ void NdbRootFragment::reset()
   assert(m_fragNo!=voidFragNo);
   assert(m_outstandingResults == 0);
   assert(m_confReceived);
+  m_currentTuple = 0;
   m_confReceived = false;
 }
 
+void NdbRootFragment::setConfReceived()
+{ 
+  /* For a query with a lookup root, there may be more than one TCKEYCONF
+     message. For a scan, there should only be one SCAN_TABCONF per root
+     fragment. 
+  */
+  assert(!m_query->getQueryDef().isScanQuery() || !m_confReceived);
+  m_confReceived = true; 
+}
 
 bool NdbRootFragment::finalBatchReceived() const
 {
   return getResultStream(0).getReceiver().m_tcPtrI==RNIL;
 }
 
+bool NdbRootFragment::isEmpty() const
+{ 
+  if (m_query->getQueryDef().isScanQuery())
+  {
+    /* We iterate over the tuples of the bottom scan operation.
+     * When we have consumed all of these (for this batch and root fragment),
+     * this NrbRootFragment is empty.
+     */
+    return m_currentTuple == 
+      m_query->getScan(m_query->getScanCount()-1)
+      .getResultStream(m_fragNo).getReceiver().m_result_rows;
+  }
+  else
+  {
+    return !m_query->getQueryOperation(0U).getReceiver(m_fragNo).nextResult(); 
+  }
+}
+
+int compareTupleNoArrays(const void* bufferA, const void* bufferB)
+{
+  const Uint16* const arrayA = reinterpret_cast<const Uint16*>(bufferA);
+  const Uint16* const arrayB = reinterpret_cast<const Uint16*>(bufferB);
+  const Uint32 length = arrayA[0];
+  assert(length == arrayB[0]);
+  for (Uint32 i = 1; i<length; i++)
+  {
+    if (arrayA[i] < arrayB[i])
+    {
+      return -1;
+    } 
+    else if (arrayA[i] > arrayB[i])
+    {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+void NdbRootFragment::buildResultIndex()
+{
+  const Uint32 scanCount = m_query->getScanCount();
+  assert(scanCount > 0);
+  /* The 'bottomScan' is the only scan operation that has no scan descdendants.
+   * Whever we get a new batch, there will be a new set of tuples for the 
+   * bottom scan. We should therefor make sure to consume these before asking
+   * for a new batch. (For inner joins, this is all we need to do.)
+   */
+  const NdbQueryOperationImpl& bottomScan = m_query->getScan(scanCount-1);
+  const NdbReceiver& bottomReceiver = 
+    bottomScan.getResultStream(getFragNo()).getReceiver();
+
+  /* Find operation number of the bottom scan. (Operations within a query
+   * are numbered from 0 and onwards.)
+   */
+  Uint32 bottomOpNo = 0xffffffff;
+  for (Uint32 i = 0; i<m_query->getNoOfOperations(); i++)
+  {
+    if (&bottomScan == &m_query->getQueryOperation(i))
+    {
+      bottomOpNo = i;
+    }
+  }
+
+  /* Iterate over the rows from the bottom scan and find matching rows from
+   * the other scan operations.
+   */
+  for (Uint32 bottomTupNo = 0;
+       bottomTupNo < bottomReceiver.m_result_rows;
+       bottomTupNo ++)
+  {
+    /* Add a length field, soo that the qsort compare function can know the 
+     * number of scans to consider (see compareTupleNoArrays()).
+     */
+    m_tupleIdx[bottomTupNo*(scanCount+1)] = scanCount;
+    /* Enter the rows of the bottom scan in the order in which they arrived.
+     */
+    m_tupleIdx[bottomTupNo*(scanCount+1) + scanCount] = bottomTupNo;
+    Uint32 childTupNo = bottomTupNo;
+    Uint32 scanNo = scanCount-1;
+    
+    /* For all other scans, enter the number of the row that corresponds to the
+     * current row of the bottom scan.
+     */
+    for (int opNo = bottomOpNo-1; opNo>=0; opNo--)
+    {
+      const NdbResultStream& childStream = m_query->getQueryOperation(opNo+1)
+        .getResultStream(getFragNo());
+      const NdbResultStream& stream = m_query->getQueryOperation(opNo)
+        .getResultStream(getFragNo());
+
+      const Uint16 tupleId = childStream.getParentId(childTupNo);
+      assert(tupleId != tupleNotFound);
+      const Uint16 tupleNo = stream.findTupleWithId(tupleId);
+      assert(tupleNo != tupleNotFound);
+      if (m_query->getQueryOperation(opNo).getQueryOperationDef()
+          .isScanOperation())
+      {
+        scanNo--;
+        m_tupleIdx[bottomTupNo*(scanCount+1)+scanNo+1] = tupleNo;
+      }
+      childTupNo = tupleNo;
+    }
+    assert(scanNo == 0);
+  }
+  qsort(m_tupleIdx, bottomReceiver.m_result_rows, (scanCount+1)*sizeof(Uint16),
+        compareTupleNoArrays);
+}
+
+bool 
+NdbRootFragment::fetchScanResults()
+{
+  const Uint32 scanCount = m_query->getScanCount();
+  /* Get a new row for each scan operation.*/
+  for (Uint32 scanNo = 0; scanNo < scanCount; scanNo++)
+  {
+    // m_tupleIdx tells us which row to use for each operation and iteation.
+    const Uint16 rowNo = m_tupleIdx[(scanCount+1)*m_currentTuple + scanNo + 1];
+    NdbQueryOperationImpl& scan = m_query->getScan(scanNo);
+    NdbResultStream& resultStream = scan.getResultStream(getFragNo());
+    resultStream.getReceiver().setCurrentRow(rowNo);
+    /* Call recursively for the lookup descendants of this operation.*/
+    for (Uint32 i = 0; i<scan.getNoOfChildOperations(); i++)
+    {
+      NdbQueryOperationImpl& child = scan.getChildOperation(i);
+      if (!child.getQueryOperationDef().isScanOperation())
+      {
+        const bool nullRow = 
+          !child.fetchSubLookupResults(getFragNo(), 
+                                       resultStream.getTupleId(rowNo));
+        if (nullRow &&
+            child.getQueryOperationDef().getMatchType() != NdbQueryOptions::MatchAll)
+        {
+          // If a NULL row is returned from a child which is not outer joined, 
+          // parent row may be eliminate also.
+          scan.nullifyResult();
+          return false;
+        }
+      }
+    }
+    m_query->getScan(scanNo).fetchRow(resultStream);
+  }
+  m_currentTuple++;
+  return true;
+} //NdbRootFragment::fetchScanResults
+
 ///////////////////////////////////////////
 /////////  NdbQuery API methods ///////////
 ///////////////////////////////////////////
@@ -952,7 +1148,8 @@ NdbQueryImpl::NdbQueryImpl(NdbTransactio
   m_startIndicator(false),
   m_commitIndicator(false),
   m_prunability(Prune_No),
-  m_pruneHashVal(0)
+  m_pruneHashVal(0),
+  m_scanCount(0)
 {
   // Allocate memory for all m_operations[] in a single chunk
   m_countOperations = queryDef.getNoOfOperations();
@@ -967,6 +1164,10 @@ NdbQueryImpl::NdbQueryImpl(NdbTransactio
   {
     const NdbQueryOperationDefImpl& def = queryDef.getQueryOperation(i);
     new(&m_operations[i]) NdbQueryOperationImpl(*this, def);
+    if (def.isScanOperation())
+    {
+      m_scans[m_scanCount++] = &m_operations[i];
+    }
   }
 
   // Serialized QueryTree definition is first part of ATTRINFO.
@@ -1347,12 +1548,11 @@ NdbQueryImpl::nextResult(bool fetchAllow
     }
 
     NdbQueryOperationImpl& root = getRoot();
-    NdbResultStream& rootStream = m_applFrags.getCurrent()->getResultStream(0);
     bool gotRow = false;
 
     /* Make results from root operation available to the user.*/
     if (m_queryDef.isScanQuery()) {
-      gotRow = root.fetchScanResults(rootStream.getRootFragNo(), tupleNotFound);
+      gotRow = m_applFrags.getCurrent()->fetchScanResults();
 
       /* In case we are doing an ordered index scan, reorder the root fragments
        * such that we get the next record from the right fragment.
@@ -1422,6 +1622,7 @@ NdbQueryImpl::fetchMoreResults(bool forc
 
       NdbRootFragment* frag;
       while ((frag=m_fullFrags.pop()) != NULL) {
+        frag->buildResultIndex();
         m_applFrags.add(*frag);
       }
 
@@ -1580,7 +1781,7 @@ NdbQueryImpl::execTCKEYCONF()
   assert(!getQueryDef().isScanQuery());
 
   // We will get 1 + #leaf-nodes TCKEYCONF for a lookup...
-  m_rootFrags[0].setConfReceivedNoCheck();
+  m_rootFrags[0].setConfReceived();
   m_rootFrags[0].incrOutstandingResults(-1);
 
   bool ret = false;
@@ -1771,7 +1972,13 @@ NdbQueryImpl::prepareSend()
   {
     for(Uint32 i = 0; i<m_rootFragCount; i++)
     {
-      m_rootFrags[i].init(*this, i); // Set fragment number.
+      // Set fragment number.
+      const Uint32 error = m_rootFrags[i].init(*this, i); 
+      if (error != 0) 
+      {
+        setErrorCodeAbort(error);
+        return -1;
+      }
     }
   }
 
@@ -3045,57 +3252,46 @@ NdbQueryOperationImpl::fetchRow(NdbResul
 } // NdbQueryOperationImpl::fetchRow
 
 bool 
-NdbQueryOperationImpl::fetchScanResults(Uint32 fragNo, Uint16 parentId)
+NdbQueryOperationImpl::fetchSubLookupResults(Uint32 fragNo, 
+                                             Uint16 parentId)
 {
-  NdbResultStream& resultStream = *m_resultStreams[fragNo];
-
-  /* Pick the proper row for a lookup that is a descentdant of the scan.
-   * We iterate linearly over the results of the root scan operation, but
-   * for the descendant in a scan we must use the findTupleWithParentId() 
-   * method to pick the tuple that corresponds to the current parent tuple.
+  assert(!getQueryOperationDef().isScanOperation());
+  assert(parentId != tupleNotFound);
+  /* Use random rather than sequential access on receiver, since we
+   * iterate over results being indexed by 'parentId'
    */
-  Uint32 rowNo;
-  if (parentId == tupleNotFound)
-  {
-    /* This is root operation in scan:
-     * retrieve rows in order available from scan root.
-     */
-    rowNo = resultStream.getReceiver().getCurrentRow();
-    assert(rowNo != tupleNotFound);
-  }
-  else
+  NdbResultStream& resultStream = getResultStream(fragNo);
+  const Uint32 rowNo = resultStream.findTupleWithParentId(parentId);
+  if (rowNo == tupleNotFound)
   {
-    /* Use random rather than sequential access on receiver, since we
-     * iterate over results being indexed by 'parentId'
-     */
-    rowNo = resultStream.findTupleWithParentId(parentId);
-    if (rowNo == tupleNotFound)
-    {
-      /* This operation retrieved no result for the current parent tuple.*/
-      nullifyResult();
-      return false;
-    }
-    resultStream.getReceiver().setCurrentRow(rowNo);
+    /* This operation retrieved no result for the current parent tuple.*/
+    nullifyResult();
+    return false;
   }
+  resultStream.getReceiver().setCurrentRow(rowNo);
 
   /* Call recursively for the children of this operation.*/
   for (Uint32 i = 0; i<getNoOfChildOperations(); i++)
   {
     NdbQueryOperationImpl& child = getChildOperation(i);
-    bool nullRow = !child.fetchScanResults(fragNo, resultStream.getTupleId(rowNo));
-    if (nullRow &&
-        child.m_operationDef.getMatchType() != NdbQueryOptions::MatchAll)
-    {
-      // If a NULL row is returned from a child which is not outer joined, 
-      // parent row may be eliminate also.
-      (void)resultStream.getReceiver().get_row();  // Get and throw 
-      nullifyResult();
-      return false;
-    }
+    if (!child.getQueryOperationDef().isScanOperation())
+    {
+      const bool nullRow = 
+        !child.fetchSubLookupResults(fragNo, resultStream.getTupleId(rowNo));
+        if (nullRow &&
+            child.m_operationDef.getMatchType() != NdbQueryOptions::MatchAll)
+        {
+          // If a NULL row is returned from a child which is not outer joined, 
+          // parent row may be eliminate also.
+          (void)resultStream.getReceiver().get_row();  // Get and throw 
+          nullifyResult();
+          return false;
+        }
+      }
   }
   fetchRow(resultStream);
   return true;
-} //NdbQueryOperationImpl::fetchScanResults
+}
 
 bool 
 NdbQueryOperationImpl::fetchLookupResults()
@@ -4047,7 +4243,8 @@ NdbQueryOperationImpl::prepareInterprete
   // There should be no subroutines in a filter.
   assert(m_interpretedCode->m_first_sub_instruction_pos==0);
 
-  if (unlikely(m_interpretedCode->m_flags & NdbInterpretedCode::Finalised) == 0)
+  if (unlikely((m_interpretedCode->m_flags & NdbInterpretedCode::Finalised) 
+               == 0))
   {
     //  NdbInterpretedCode::finalise() not called.
     return Err_FinaliseNotCalled;

=== modified file 'storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp	2010-07-01 14:15:49 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp	2010-07-07 13:24:23 +0000
@@ -205,6 +205,15 @@ public:
   Uint32 getRootFragCount() const
   { return m_rootFragCount; }
 
+  Uint32 getScanCount() const
+  { return m_scanCount; }
+
+  NdbQueryOperationImpl& getScan(Uint32 scanNo) const
+  {
+    assert(scanNo < m_scanCount);
+    return *m_scans[scanNo];
+  }
+
 private:
   /** Possible return values from NdbQuery::fetchMoreResults. Integer values
    * matches those returned from PoolGuard::waitScan().
@@ -422,6 +431,10 @@ private:
    * fragment that should be scanned.*/
   Uint32 m_pruneHashVal;
 
+  Uint32 m_scanCount;
+
+  NdbQueryOperationImpl* m_scans[NdbMaxPushedQueryOps];
+
   // Only constructable from factory ::buildQuery();
   explicit NdbQueryImpl(
              NdbTransaction& trans,
@@ -570,7 +583,18 @@ public:
   bool checkMagicNumber() const
   { return m_magic == MAGIC; }
 
+  bool fetchSubLookupResults(Uint32 rootFragNo, Uint16 parentId);
+
+  /** Copy NdbRecAttr and/or NdbRecord results into appl. buffers */
+  void fetchRow(NdbResultStream& src);
+
+  /** Set result for this operation and all its descendand child 
+   *  operations to NULL.
+   */
+  void nullifyResult();
+
 private:
+
   STATIC_CONST (MAGIC = 0xfade1234);
 
   /** Interface for the application developer.*/
@@ -639,17 +663,8 @@ private:
    *  Return true if a row was fetched, or false if a NULL row was
    *  produced.
    */
-  bool fetchScanResults(Uint32 rootFragNo, Uint16 parentId);
   bool fetchLookupResults();
 
-  /** Set result for this operation and all its descendand child 
-   *  operations to NULL.
-   */
-  void nullifyResult();
-
-  /** Copy NdbRecAttr and/or NdbRecord results into appl. buffers */
-  void fetchRow(NdbResultStream& src);
-
   /** Count number of descendant operations (excluding the operation itself) */
   Int32 getNoOfDescendantOperations() const;
 


Attachment: [text/bzr-bundle] bzr/jan.wedvik@sun.com-20100707132423-dsgisznr3ombflz1.bundle
Thread
bzr push into mysql-5.1-telco-7.0-spj branch (jan.wedvik:3194 to 3196) Jan Wedvik7 Jul