MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Ole John Aske Date:September 22 2009 9:10am
Subject:bzr push into mysql-5.1-telco-7.0-spj branch (ole.john.aske:2939 to 2940)
View as plain text  
 2940 Ole John Aske	2009-09-22
      Implemented partition pruning of NdbQuery (index) scan operations.

    modified:
      storage/ndb/ndbapi-examples/ndbapi_multi_cursor/main.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/NdbTransactionScan.cpp
 2939 Ole John Aske	2009-09-16
      Integrated usage of NdbReceiver::calculate_batch_size() into NdbQuery batch count/byte calculation. + Some more code refactoring.

    modified:
      storage/ndb/include/ndbapi/NdbReceiver.hpp
      storage/ndb/include/ndbapi/NdbScanOperation.hpp
      storage/ndb/ndbapi-examples/ndbapi_multi_cursor/main.cpp
      storage/ndb/src/ndbapi/NdbQueryOperation.cpp
      storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp
      storage/ndb/src/ndbapi/NdbReceiver.cpp
      storage/ndb/src/ndbapi/NdbScanOperation.cpp
      storage/ndb/src/ndbapi/TransporterFacade.hpp
=== modified file 'storage/ndb/ndbapi-examples/ndbapi_multi_cursor/main.cpp'
--- a/storage/ndb/ndbapi-examples/ndbapi_multi_cursor/main.cpp	2009-09-16 11:18:37 +0000
+++ b/storage/ndb/ndbapi-examples/ndbapi_multi_cursor/main.cpp	2009-09-22 09:09:13 +0000
@@ -70,6 +70,12 @@ struct ManagerRow
   Uint32 my_key;
 };
 
+struct ManagerPKRow
+{
+  Uint32 emp_no;
+  char   dept_no[4+1];
+};
+
 struct EmployeeRow
 {
   Uint32 emp_no;
@@ -118,11 +124,13 @@ const char* dept_managerDef = 
 "   my_key       INT             NOT NULL,"
 "   KEY         (emp_no),"
 "   KEY         (dept_no),"
-"   FOREIGN KEY (emp_no)  REFERENCES employees (emp_no)    ON DELETE CASCADE,"
-"   FOREIGN KEY (dept_no) REFERENCES departments (dept_no) ON DELETE CASCADE,"
+//"   FOREIGN KEY (emp_no)  REFERENCES employees (emp_no)    ON DELETE CASCADE,"
+//"   FOREIGN KEY (dept_no) REFERENCES departments (dept_no) ON DELETE CASCADE,"
 "   UNIQUE KEY MYINDEXNAME (my_key),"
 "   PRIMARY KEY (emp_no,dept_no))"
-" ENGINE=NDB";
+" ENGINE=NDB"
+//" PARTITION BY KEY(dept_no)"
+;
 
 const char* dept_empDef = 
 "CREATE TABLE dept_emp ("
@@ -279,8 +287,8 @@ int testQueryBuilder(Ndb &myNdb)
 {
   const NdbDictionary::Table *manager, *employee, *salary;
   int res;
-  NdbTransaction* myTransaction;
-  NdbQuery* myQuery;
+  NdbTransaction* myTransaction = NULL;
+  NdbQuery* myQuery = NULL;
 
   char* dept_no = "d005";
   Uint32 emp_no = 110567;
@@ -310,6 +318,45 @@ int testQueryBuilder(Ndb &myNdb)
    */
   NdbQueryBuilder myBuilder(myNdb);
 
+#if 0
+  printf("Compare with old API interface\n");
+
+  {
+    myTransaction= myNdb.startTransaction();
+    if (myTransaction == NULL) APIERROR(myNdb.getNdbError());
+
+    // Lookup Primary key for manager table
+    const NdbDictionary::Index *myPIndex= myDict->getIndex("PRIMARY", manager->getName());
+    if (myPIndex == NULL)
+      APIERROR(myDict->getNdbError());
+
+    NdbIndexScanOperation* ixScan = 
+      myTransaction->scanIndex(myPIndex->getDefaultRecord(),
+                               manager->getDefaultRecord());
+
+    if (ixScan == NULL)
+      APIERROR(myTransaction->getNdbError());
+
+
+    /* Add a bound
+     */
+    ManagerPKRow low={0,"d005"};
+    ManagerPKRow high={110567,"d005"};
+
+    NdbIndexScanOperation::IndexBound bound;
+    bound.low_key=(char*)&low;
+    bound.low_key_count=2;
+    bound.low_inclusive=true;
+    bound.high_key=(char*)&high;
+    bound.high_key_count=2;
+    bound.high_inclusive=false;
+    bound.range_no=0;
+
+    if (ixScan->setBound(myPIndex->getDefaultRecord(), bound))
+      APIERROR(myTransaction->getNdbError());
+  }
+#endif
+
 #if 1
   /* qt1 is 'const defined' */
   printf("q1\n");
@@ -808,21 +855,22 @@ int testQueryBuilder(Ndb &myNdb)
 
     const NdbQueryOperand* low[] =  // Manager PK index is {"emp_no","dept_no", }
     {
-//     qb->constValue(110567),      // emp_no  = 110567
        qb->paramValue(),
-//     qb->constValue("d005"),      // dept_no = "d005"
+//     qb->constValue(110567),      // emp_no  = 110567
+       qb->constValue("d005"),      // dept_no = "d005"
        0
     };
     const NdbQueryOperand* high[] =  // Manager PK index is {"emp_no","dept_no", }
-    {  qb->constValue("illegal key"),
+    {
+       qb->constValue(110567),      // emp_no  = 110567
+       qb->constValue("d005"),      // dept_no = "d005"
        0
     };
-    const NdbQueryIndexBound  bound        (low, NULL);   // emp_no = [110567, oo]
-    const NdbQueryIndexBound  bound_illegal(low, high);   // 'high' is char type -> illegal
+    const NdbQueryIndexBound  bound        (low, high);   // emp_no = [110567, oo]
     const NdbQueryIndexBound  boundEq(low);
 
     // Lookup on a single tuple with key define by 'managerKey' param. tuple
-    const NdbQueryScanOperationDef* scanManager = qb->scanIndex(myPIndex, manager, &boundEq);
+    const NdbQueryScanOperationDef* scanManager = qb->scanIndex(myPIndex, manager, &bound);
     if (scanManager == NULL) APIERROR(qb->getNdbError());
 
     // THEN: employee table is joined:

=== modified file 'storage/ndb/src/ndbapi/NdbQueryBuilder.cpp'
--- a/storage/ndb/src/ndbapi/NdbQueryBuilder.cpp	2009-09-14 08:25:25 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryBuilder.cpp	2009-09-22 09:09:13 +0000
@@ -25,11 +25,11 @@
 #include "Ndb.hpp"
 #include "NdbDictionary.hpp"
 #include "NdbDictionaryImpl.hpp"
+#include "NdbRecord.hpp"
 #include "AttributeHeader.hpp"
 #include "NdbIndexScanOperation.hpp"
 #include "NdbOut.hpp"
 
-
 /**
  * Implementation of all QueryBuilder objects are hidden from
  * both the API interface and other internals in the NDBAPI using the
@@ -178,9 +178,10 @@ class NdbQueryLookupOperationDefImpl : p
 public:
   virtual int serializeOperation(Uint32Buffer& serializedDef) const;
 
-  virtual int
-  prepareKeyInfo(Uint32Buffer& keyInfo,
-                     const constVoidPtr actualParam[]) const;
+  virtual int prepareKeyInfo(Uint32Buffer& keyInfo,
+                             const constVoidPtr actualParam[],
+                             bool&   isPruned,
+                             Uint32& hashValue) const;
 
 
 protected:
@@ -273,9 +274,11 @@ public:
   virtual Type getType() const
   { return TableScan; }
 
-  virtual int
-  prepareKeyInfo(Uint32Buffer& keyInfo,
-                     const constVoidPtr actualParam[]) const {
+  virtual int prepareKeyInfo(Uint32Buffer& keyInfo,
+                             const constVoidPtr actualParam[],
+                             bool&   isPruned,
+                             Uint32& hashValue) const {
+    isPruned = false;
     return 0;
   }
 
@@ -311,7 +314,9 @@ public:
   { return OrderedIndexScan; }
 
   virtual int prepareKeyInfo(Uint32Buffer& keyInfo,
-                             const constVoidPtr actualParam[]) const;
+                             const constVoidPtr actualParam[],
+                             bool&   isPruned,
+                             Uint32& hashValue) const;
 
 private:
   virtual ~NdbQueryIndexScanOperationDefImpl() {};
@@ -329,7 +334,7 @@ private:
   struct bound {  // Limiting 'bound ' definition
     NdbQueryOperandImpl* low[MAX_ATTRIBUTES_IN_INDEX];
     NdbQueryOperandImpl* high[MAX_ATTRIBUTES_IN_INDEX];
-    int lowKeys, highKeys;
+    Uint32 lowKeys, highKeys;
     bool lowIncl, highIncl;
     bool eqBound;  // True if 'low == high'
   } m_bound;
@@ -822,14 +827,14 @@ NdbQueryBuilder::scanIndex(const NdbDict
                                           m_pimpl->m_operations.size());
   returnErrIf(op==0, 4000);
 
-  if (unlikely(op->m_bound.lowKeys  > (int)indexImpl.getNoOfColumns() ||
-               op->m_bound.highKeys > (int)indexImpl.getNoOfColumns()))
+  if (unlikely(op->m_bound.lowKeys  > indexImpl.getNoOfColumns() ||
+               op->m_bound.highKeys > indexImpl.getNoOfColumns()))
   { m_pimpl->setErrorCode(QRY_TOO_MANY_KEY_VALUES);
     delete op;
     return NULL;
   }
 
-  int i;
+  Uint32 i;
   for (i=0; i<op->m_bound.lowKeys; ++i)
   {
     const NdbColumnImpl& col = NdbColumnImpl::getImpl(*indexImpl.getColumn(i));
@@ -1061,7 +1066,8 @@ NdbQueryIndexScanOperationDefImpl::NdbQu
 static int
 appendBound(Uint32Buffer& keyInfo,
             NdbIndexScanOperation::BoundType type, const NdbQueryOperandImpl* bound,
-            const constVoidPtr actualParam[]) 
+            const constVoidPtr actualParam[],
+            Ndb::Key_part_ptr& keyPart) 
 {
   Uint32 len = 0;
   constVoidPtr boundValue = NULL;
@@ -1099,16 +1105,22 @@ appendBound(Uint32Buffer& keyInfo,
   keyInfo.append(type);
   keyInfo.append(ah.m_value);
   keyInfo.append(boundValue,len);
+
+  keyPart.ptr = boundValue;
+  keyPart.len = len;
   return 0;
 } // appendBound()
 
 int
 NdbQueryLookupOperationDefImpl::prepareKeyInfo(
                               Uint32Buffer& keyInfo,
-                              const constVoidPtr actualParam[]) const
+                              const constVoidPtr actualParam[],
+                              bool&   isPruned,
+                              Uint32& hashValue) const  // 'hashValue' only defined if 'isPruned'
 { 
   assert(getQueryOperationIx()==0); // Should only be called for root operation.
   int startPos = keyInfo.getSize();
+  isPruned = false;
 
   const int keyCount = getIndex()==NULL ? 
     getTable().getNoOfPrimaryKeys() :
@@ -1170,39 +1182,45 @@ NdbQueryLookupOperationDefImpl::prepareK
 int
 NdbQueryIndexScanOperationDefImpl::prepareKeyInfo(
                               Uint32Buffer& keyInfo,
-                              const constVoidPtr actualParam[]) const
+                              const constVoidPtr actualParam[],
+                              bool&   isPruned,
+                              Uint32& hashValue) const  // 'hashValue' only defined if 'isPruned'
 { 
   assert(getQueryOperationIx()==0); // Should only be called for root operation.
   int startPos = keyInfo.getSize();
 
-  if (false && m_bound.eqBound)
-  {
-    assert(m_bound.low == m_bound.high);
-    assert(m_bound.lowKeys == m_bound.highKeys);
+  // Required for partition pruning calculation
+  const NdbRecord* key_record = m_index.getDefaultRecord();
 
-    for (int keyNo = 0; keyNo < m_bound.lowKeys; keyNo++)
-    {
-      appendBound(keyInfo,
-                  NdbIndexScanOperation::BoundEQ, m_bound.low[keyNo],
-                  actualParam);
-    }
-  }
-  else
+  const Uint32 index_distkeys = key_record->m_no_of_distribution_keys;
+  const Uint32 distkey_min = key_record->m_min_distkey_prefix_length;
+  const Uint32 table_distkeys = getTable().getDefaultRecord()->m_no_of_distribution_keys;
+
+  bool isPrunable = (                             // Initial prunable propert:
+            index_distkeys == table_distkeys &&   // Index has all base table d-keys
+            m_bound.lowKeys >= distkey_min &&     // Low bounds have all d-keys
+            m_bound.highKeys >= distkey_min);     // High bounds have all d-keys
+
+  Ndb::Key_part_ptr lowKey[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY];
+  Ndb::Key_part_ptr highKey;
+
+  const Uint32 key_count = 
+     (m_bound.lowKeys >= m_bound.highKeys) ? m_bound.lowKeys : m_bound.highKeys;
+
+  for (Uint32 keyNo = 0; keyNo < key_count; keyNo++)
   {
-    int key_count = (m_bound.lowKeys >= m_bound.highKeys) ? m_bound.lowKeys : m_bound.highKeys;
+    NdbIndexScanOperation::BoundType bound_type;
 
-    for (int keyNo = 0; keyNo < key_count; keyNo++)
+    /* If upper and lower limit is equal, a single BoundEQ is sufficient */
+    if (m_bound.low[keyNo] == m_bound.high[keyNo])
     {
-      NdbIndexScanOperation::BoundType bound_type;
+      /* Inclusive if defined, or matching rows can include this value */
+      bound_type= NdbIndexScanOperation::BoundEQ;
+      int error = appendBound(keyInfo, bound_type, m_bound.low[keyNo], actualParam, lowKey[keyNo]);
+      if (unlikely(error))
+        return error;
 
-      /* If upper and lower limit is equal, a single BoundEQ is sufficient */
-      if (m_bound.low[keyNo] == m_bound.high[keyNo])
-      {
-        /* Inclusive if defined, or matching rows can include this value */
-        bound_type= NdbIndexScanOperation::BoundEQ;
-        appendBound(keyInfo, bound_type, m_bound.low[keyNo], actualParam);
-        continue;
-      }
+    } else {
 
       /* If key is part of lower bound */
       if (keyNo < m_bound.lowKeys)
@@ -1211,8 +1229,11 @@ NdbQueryIndexScanOperationDefImpl::prepa
         bound_type= m_bound.lowIncl  || keyNo+1 < m_bound.lowKeys ?
             NdbIndexScanOperation::BoundLE : NdbIndexScanOperation::BoundLT;
 
-        appendBound(keyInfo, bound_type, m_bound.low[keyNo], actualParam);
+        int error = appendBound(keyInfo, bound_type, m_bound.low[keyNo], actualParam, lowKey[keyNo]);
+        if (unlikely(error))
+          return error;
       }
+
       /* If key is part of upper bound */
       if (keyNo < m_bound.highKeys)
       {
@@ -1220,11 +1241,29 @@ NdbQueryIndexScanOperationDefImpl::prepa
         bound_type= m_bound.highIncl  || keyNo+1 < m_bound.highKeys ?
             NdbIndexScanOperation::BoundGE : NdbIndexScanOperation::BoundGT;
 
-        appendBound(keyInfo, bound_type, m_bound.high[keyNo], actualParam);
+        int error = appendBound(keyInfo, bound_type, m_bound.high[keyNo], actualParam, highKey);
+        if (unlikely(error))
+          return error;
+      }
+
+      // Aggregate prunable propert:
+      // All hi/low keys within 'distkey_min' must be equal
+      if (isPrunable  &&  keyNo < distkey_min) 
+      {
+        const NdbColumnImpl& column = NdbColumnImpl::getImpl(*m_index.getColumn(keyNo));
+        const NdbRecord::Attr& recAttr = key_record->columns[column.m_keyInfoPos];
+        const int res=
+          (*recAttr.compare_function)(recAttr.charset_info,
+                                       lowKey[keyNo].ptr, lowKey[keyNo].len,
+                                       highKey.ptr, highKey.len, true);
+        if (res!=0) {  // Not equal
+          assert(res != NdbSqlUtil::CmpUnknown);
+          isPrunable = false;
+        }
       }
     }
   }
-  
+
   if(unlikely(keyInfo.isMaxSizeExceeded())) {
     return QRY_DEFINITION_TOO_LARGE; // Query definition too large.
   }
@@ -1235,7 +1274,24 @@ NdbQueryIndexScanOperationDefImpl::prepa
     keyInfo.put(startPos, keyInfo.get(startPos) | (length << 16));
   }
 
-  // TODO: Partition pruning not handled yet.
+  // Scan is pruned, calculate hashValue
+  isPruned = isPrunable;
+  if (isPrunable) {
+    Ndb::Key_part_ptr distKey[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY+1];
+
+    // hi/low is equal and prunable bounds, remember key for later 
+    // hashValue calculation.
+    for (Uint32 i = 0; i<key_record->distkey_index_length; i++)  {
+      // Revers lookup the index column with the value of this distrubution key.
+      Uint32 keyPos = NdbColumnImpl::getImpl(*m_index.getColumn(key_record->distkey_indexes[i])).m_keyInfoPos;
+      distKey[i] = lowKey[keyPos];
+    }
+    distKey[key_record->distkey_index_length].ptr = NULL;
+
+    int error = Ndb::computeHash(&hashValue, &getTable(), distKey, NULL, 0);
+    if (unlikely(error))
+      return error;
+  }
 
 #ifdef TRACE_SERIALIZATION
   ndbout << "Serialized KEYINFO w/ bounds for scan root : ";

=== modified file 'storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp	2009-09-12 23:42:33 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp	2009-09-22 09:09:13 +0000
@@ -251,13 +251,17 @@ public:
 
   /**
    * Expand keys and bounds for the root operation into the KEYINFO section.
-   * @param ndbOperation The operation that the query is piggy backed on.
+   * @param keyInfo Actuall KEYINFO section the key / bounds are 
+   *                put into
    * @param actualParam Instance values for NdbParamOperands.
+   * @param isPruned 'true' for a scan of pruned to single partition.
+   * @param hashValue Valid only if 'isPruned'.
    * Returns: 0 if OK, or possible an errorcode.
    */
-  virtual int
-  prepareKeyInfo(Uint32Buffer& keyInfo,
-                     const constVoidPtr actualParam[]) const = 0;
+  virtual int prepareKeyInfo(Uint32Buffer& keyInfo,
+                             const constVoidPtr actualParam[],
+                             bool&   isPruned,
+                             Uint32& hashValue) const = 0;
 
   virtual ~NdbQueryOperationDefImpl() = 0;
 

=== modified file 'storage/ndb/src/ndbapi/NdbQueryOperation.cpp'
--- a/storage/ndb/src/ndbapi/NdbQueryOperation.cpp	2009-09-16 11:18:37 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryOperation.cpp	2009-09-22 09:09:13 +0000
@@ -224,6 +224,8 @@ NdbQueryImpl::NdbQueryImpl(NdbTransactio
   m_queryDef(queryDef),
   m_parallelism(0),
   m_maxBatchRows(0),
+  m_isPruned(false),
+  m_hashValue(0),
   m_signal(0),
   m_attrInfo(),
   m_keyInfo()
@@ -285,16 +287,20 @@ NdbQueryImpl::assignParameters(const con
   /**
    * Immediately build the serialize parameter representation in order 
    * to avoid storing param values elsewhere until query is executed.
+   * Also calculate prunable property, and possibly its hashValue.
    */
   // Build explicit key/filter/bounds for root operation, possibly refering paramValues
-  getRoot().getQueryOperationDef().prepareKeyInfo(m_keyInfo, paramValues);
+  const int error = getRoot().getQueryOperationDef()
+      .prepareKeyInfo(m_keyInfo, paramValues, m_isPruned, m_hashValue);
+  if (unlikely(error != 0))
+    return error;
 
   //TODO: No need to serialize for root (i==0) as root key is part of keyInfo above
   for (Uint32 i=0; i<getNoOfOperations(); ++i)
   {
     if (getQueryDef().getQueryOperation(i).getNoOfParameters() > 0)
     {
-      int error = getQueryOperation(i).serializeParams(paramValues);
+      const int error = getQueryOperation(i).serializeParams(paramValues);
       if (unlikely(error != 0))
         return error;
     }
@@ -421,10 +427,10 @@ NdbQueryImpl::prepareSend()
     m_pendingStreams = getRoot().getQueryOperationDef().getTable().getFragmentCount();
     m_tcKeyConfReceived = true;
 
-    if (m_parallelism == 0)  // -> Use 'max' as default value
-      m_parallelism = m_pendingStreams;
-    else if (m_parallelism > m_pendingStreams)
+    // Parallelism may be user specified, else(==0) use default
+    if (m_parallelism == 0 || m_parallelism > m_pendingStreams) {
       m_parallelism = m_pendingStreams;
+    }
 
     Ndb* const ndb = getNdbTransaction()->getNdb();
     TransporterFacade *tp = ndb->theImpl->m_transporter_facade;
@@ -581,25 +587,32 @@ NdbQueryImpl::doSend(int nodeId, bool la
     ScanTabReq::setLockMode(reqInfo, false);  // not exclusive
     ScanTabReq::setHoldLockFlag(reqInfo, false);
     ScanTabReq::setReadCommittedFlag(reqInfo, true);
-    scanTabReq->requestInfo = reqInfo;
 
 //  m_keyInfo = (scan_flags & NdbScanOperation::SF_KeyInfo) ? 1 : 0;
 
-    assert(m_signal->next() == NULL);
-    m_signal->setLength(ScanTabReq::StaticLength);
+    // If scan is pruned, use optional 'distributionKey' to hold hashvalue
+    // TODO: Use hashValue to also select TC where to ::startTransaction
+    if (m_isPruned)
+    {
+//    printf("Build pruned SCANREQ, w/ hashValue:%d\n", m_hashValue);
+      ScanTabReq::setDistributionKeyFlag(reqInfo, 1);
+      scanTabReq->distributionKey= m_hashValue;
+      m_signal->setLength(ScanTabReq::StaticLength + 1);
+    } else {
+      m_signal->setLength(ScanTabReq::StaticLength);
+    }
+    scanTabReq->requestInfo = reqInfo;
 
     /**
      * Then send the signal:
-     */
-
-    /* SCANTABREQ always has 2 mandatory sections and an optional
+     *
+     * SCANTABREQ always has 2 mandatory sections and an optional
      * third section
      * Section 0 : List of receiver Ids NDBAPI has allocated 
      *             for the scan
      * Section 1 : ATTRINFO section
      * Section 2 : Optional KEYINFO section
      */
-
     GenericSectionPtr secs[3];
     Uint32 prepared_receivers[64];  // TODO: 64 is a temp hack
  
@@ -671,7 +684,6 @@ NdbQueryImpl::doSend(int nodeId, bool la
     TcKeyReq::setSimpleFlag(reqInfo, true);
     tcKeyReq->requestInfo = reqInfo;
 
-    assert(m_signal->next() == NULL);
     m_signal->setLength(TcKeyReq::StaticLength);
 
 /****

=== modified file 'storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp	2009-09-16 11:18:37 +0000
+++ b/storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp	2009-09-22 09:09:13 +0000
@@ -151,6 +151,10 @@ private:
   /** Max rows (per resultStream) in a scan batch.*/
   Uint32 m_maxBatchRows;
 
+  /** Prunable property, and optional hashValue, valid for scans, */
+  bool m_isPruned;
+  Uint32 m_hashValue;
+
   /**
    * Signal building section:
    */

=== modified file 'storage/ndb/src/ndbapi/NdbTransactionScan.cpp'
--- a/storage/ndb/src/ndbapi/NdbTransactionScan.cpp	2009-09-14 12:08:07 +0000
+++ b/storage/ndb/src/ndbapi/NdbTransactionScan.cpp	2009-09-22 09:09:13 +0000
@@ -71,6 +71,10 @@ NdbTransaction::receiveSCAN_TABREF(NdbAp
 #endif //TODO_SPJ_NEED_SCAN_TABREF
 
   if(checkState_TransId(&ref->transId1)){
+    if (theScanningOp == NULL) {
+      printf("FIXME: No active Scanning op, 'REF' ignored - likely a NdbQuery\n");
+      return 0;
+    }
     theScanningOp->setErrorCode(ref->errorCode);
     theScanningOp->execCLOSE_SCAN_REP();
     if(!ref->closeNeeded){


Attachment: [text/bzr-bundle] bzr/ole.john.aske@sun.com-20090922090913-01fi2cqq19mmndqp.bundle
Thread
bzr push into mysql-5.1-telco-7.0-spj branch (ole.john.aske:2939 to 2940) Ole John Aske22 Sep