List:Commits« Previous MessageNext Message »
From:Pekka Nousiainen Date:December 2 2010 6:10pm
Subject:bzr commit into mysql-5.1-telco-6.3 branch (pekka:3361) Bug#58277
View as plain text  
#At file:///export/space/pekka/ms/ms-bug58277-63/ based on revid:pekka@stripped

 3361 Pekka Nousiainen	2010-12-02
      bug#58277 a03_scan.diff
      fix known crashes and return error to user

    modified:
      storage/ndb/include/kernel/signaldata/AccScan.hpp
      storage/ndb/include/kernel/signaldata/NextScan.hpp
      storage/ndb/src/kernel/blocks/ERROR_codes.txt
      storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
      storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp
      storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp
      storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp
      storage/ndb/src/ndbapi/ndberror.c
      storage/ndb/test/run-test/daily-basic-tests.txt
=== modified file 'storage/ndb/include/kernel/signaldata/AccScan.hpp'
--- a/storage/ndb/include/kernel/signaldata/AccScan.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/include/kernel/signaldata/AccScan.hpp	2010-12-02 18:10:25 +0000
@@ -207,6 +207,24 @@ private:
   Uint32 flag;
 };
 
+class AccScanRef {
+  friend class Dbtux;
+  friend class Dblqh;
+
+  enum ErrorCode {
+    TuxNoFreeScanOp = 909,
+    TuxIndexNotOnline = 910
+  };
+
+public:
+  STATIC_CONST( SignalLength = 3 );
+
+private:
+  Uint32 scanPtr;
+  Uint32 accPtr;
+  Uint32 errorCode;
+};
+
 class AccCheckScan {
   friend class Dbacc;
   friend class Dbtux;

=== modified file 'storage/ndb/include/kernel/signaldata/NextScan.hpp'
--- a/storage/ndb/include/kernel/signaldata/NextScan.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/include/kernel/signaldata/NextScan.hpp	2010-12-02 18:10:25 +0000
@@ -59,4 +59,16 @@ private:
   Uint32 gci;
 };
 
+class NextScanRef {
+  friend class Dbtux;
+  friend class Dblqh;
+public:
+  STATIC_CONST( SignalLength = 4 );
+private:
+  Uint32 scanPtr;
+  Uint32 accOperationPtr;
+  Uint32 fragId;
+  Uint32 errorCode;
+};
+
 #endif

=== modified file 'storage/ndb/src/kernel/blocks/ERROR_codes.txt'
--- a/storage/ndb/src/kernel/blocks/ERROR_codes.txt	2010-11-24 12:06:52 +0000
+++ b/storage/ndb/src/kernel/blocks/ERROR_codes.txt	2010-12-02 18:10:25 +0000
@@ -10,7 +10,7 @@ Next DBTC 8088
 Next CMVMI 9000
 Next BACKUP 10041
 Next DBUTIL 11002
-Next DBTUX 12008
+Next DBTUX 12010
 Next SUMA 13047
 Next LGMAN 15001
 Next TSMAN 16001
@@ -549,6 +549,8 @@ Test routing of signals:
 Ordered index:
 --------------
 12007: Make next alloc node fail with no memory error
+12008: Fail seize scan op
+12009: Cause InvalidBounds error
 
 Dbdict:
 -------

=== modified file 'storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp	2010-11-24 12:06:52 +0000
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp	2010-12-02 18:10:25 +0000
@@ -8346,7 +8346,23 @@ void Dblqh::execACC_SCANCONF(Signal* sig
 void Dblqh::execACC_SCANREF(Signal* signal) 
 {
   jamEntry();
-  ndbrequire(false);
+  ndbrequire(refToBlock(signal->getSendersBlockRef()) == DBTUX);
+  const AccScanRef refCopy = *(const AccScanRef*)signal->getDataPtr();
+  const AccScanRef* ref = &refCopy;
+  ndbrequire(ref->errorCode != 0);
+
+  scanptr.i = ref->scanPtr;
+  c_scanRecordPool.getPtr(scanptr);
+  tcConnectptr.i = scanptr.p->scanTcrec;
+  ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+  tcConnectptr.p->errorCode = ref->errorCode;
+
+  AccScanConf* conf = (AccScanConf*)signal->getDataPtrSend();
+  conf->scanPtr = ref->scanPtr;
+  conf->accPtr = ref->accPtr;
+  conf->flag = AccScanConf::ZEMPTY_FRAGMENT;
+  sendSignal(reference(), GSN_ACC_SCANCONF,
+             signal, AccScanConf::SignalLength, JBB);
 }//Dblqh::execACC_SCANREF()
 
 /* ***************>> */
@@ -8358,7 +8374,7 @@ void Dblqh::execNEXT_SCANCONF(Signal* si
   jamEntry();
   scanptr.i = nextScanConf->scanPtr;
   c_scanRecordPool.getPtr(scanptr);
-  if (likely(nextScanConf->localKeyLength == 1)) 
+  if (likely(nextScanConf->localKeyLength == 1)) //XXX signal length ?
   {
     jam();
     scanptr.p->m_row_id.assref(nextScanConf->localKey[0]);
@@ -8416,8 +8432,24 @@ void Dblqh::execNEXT_SCANCONF(Signal* si
 void Dblqh::execNEXT_SCANREF(Signal* signal) 
 {
   jamEntry();
-  systemErrorLab(signal, __LINE__);
-  return;
+  ndbrequire(refToBlock(signal->getSendersBlockRef()) == DBTUX);
+  const NextScanRef refCopy = *(const NextScanRef*)signal->getDataPtr();
+  const NextScanRef* ref = &refCopy;
+  ndbrequire(ref->errorCode != 0);
+
+  scanptr.i = ref->scanPtr;
+  c_scanRecordPool.getPtr(scanptr);
+  tcConnectptr.i = scanptr.p->scanTcrec;
+  ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+  tcConnectptr.p->errorCode = ref->errorCode;
+
+  NextScanConf* conf = (NextScanConf*)signal->getDataPtr();
+  conf->scanPtr = ref->scanPtr;
+  // usual weird flags to indicate scan end
+  conf->accOperationPtr = RNIL;
+  conf->fragId = RNIL;
+  sendSignal(reference(), GSN_NEXT_SCANCONF,
+             signal, 3, JBB);
 }//Dblqh::execNEXT_SCANREF()
 
 /* ******************> */

=== modified file 'storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp'
--- a/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp	2010-09-24 11:06:19 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp	2010-12-02 18:10:25 +0000
@@ -389,6 +389,10 @@ private:
    * An unfinished scan is always linked to some tree node, and has
    * current position and direction (see comments at scanNext).  There
    * is also a copy of latest entry found.
+   *
+   * Error handling:  An error code (independent of scan state) is set
+   * and returned to LQH.  No more result rows are returned but normal
+   * protocol is still followed until scan close.
    */
   struct ScanOp;
   friend struct ScanOp;
@@ -402,11 +406,11 @@ private:
       Locked = 5,               // found and locked or no lock needed
       Next = 6,                 // looking for next extry
       Last = 7,                 // after last entry
-      Aborting = 8,             // lock wait at scan close
-      Invalid = 9               // cannot return REF to LQH currently
+      Aborting = 8
     };
-    Uint16 m_state;
-    Uint16 m_lockwait;
+    Uint8 m_state;
+    Uint8 m_lockwait;
+    Uint16 m_errorCode;
     Uint32 m_userPtr;           // scanptr.i in LQH
     Uint32 m_userRef;
     Uint32 m_tableId;
@@ -986,6 +990,7 @@ inline
 Dbtux::ScanOp::ScanOp(ScanBoundPool& scanBoundPool) :
   m_state(Undef),
   m_lockwait(false),
+  m_errorCode(0),
   m_userPtr(RNIL),
   m_userRef(RNIL),
   m_tableId(RNIL),

=== modified file 'storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp	2009-12-14 10:58:03 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp	2010-12-02 18:10:25 +0000
@@ -320,6 +320,7 @@ operator<<(NdbOut& out, const Dbtux::Sca
   out << "[ScanOp " << hex << &scan;
   out << " [state " << dec << scan.m_state << "]";
   out << " [lockwait " << dec << scan.m_lockwait << "]";
+  out << " [errorCode " << dec << scan.m_errorCode << "]";
   out << " [indexId " << dec << scan.m_indexId << "]";
   out << " [fragId " << dec << scan.m_fragId << "]";
   out << " [transId " << hex << scan.m_transId1 << " " << scan.m_transId2 << "]";

=== modified file 'storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp	2009-12-14 10:58:03 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp	2010-12-02 18:10:25 +0000
@@ -20,12 +20,18 @@
 #include "Dbtux.hpp"
 #include <my_sys.h>
 
+/*
+ * Error handling:  Any seized scan op is released.  ACC_SCANREF is sent
+ * to LQH.  LQH sets error code, and treats this like ZEMPTY_FRAGMENT.
+ * Therefore scan is now closed on both sides.
+ */
 void
 Dbtux::execACC_SCANREQ(Signal* signal)
 {
   jamEntry();
   const AccScanReq reqCopy = *(const AccScanReq*)signal->getDataPtr();
   const AccScanReq* const req = &reqCopy;
+  Uint32 errorCode = 0;
   ScanOpPtr scanPtr;
   scanPtr.i = RNIL;
   do {
@@ -45,6 +51,17 @@ Dbtux::execACC_SCANREQ(Signal* signal)
     }
     ndbrequire(fragPtr.i != RNIL);
     Frag& frag = *fragPtr.p;
+    // check for index not Online (i.e. Dropping)
+    if (unlikely(indexPtr.p->m_state != Index::Online)) {
+      jam();
+#ifdef VM_TRACE
+      if (debugFlags & (DebugMeta | DebugScan)) {
+        debugOut << "Index dropping at ACC_SCANREQ " << indexPtr.i << " " << *indexPtr.p << endl;
+      }
+#endif
+      errorCode = AccScanRef::TuxIndexNotOnline;
+      break;
+    }
     // must be normal DIH/TC fragment
     TreeHead& tree = frag.m_tree;
     // check for empty fragment
@@ -59,8 +76,12 @@ Dbtux::execACC_SCANREQ(Signal* signal)
       return;
     }
     // seize from pool and link to per-fragment list
-    if (! frag.m_scanList.seize(scanPtr)) {
+    if (ERROR_INSERTED(12008) ||
+        ! frag.m_scanList.seize(scanPtr)) {
+      CLEAR_ERROR_INSERT_VALUE;
       jam();
+      // should never happen but can be used to test error handling
+      errorCode = AccScanRef::TuxNoFreeScanOp;
       break;
     }
     new (scanPtr.p) ScanOp(c_scanBoundPool);
@@ -101,10 +122,14 @@ Dbtux::execACC_SCANREQ(Signal* signal)
     jam();
     releaseScanOp(scanPtr);
   }
-  // LQH does not handle REF
-  signal->theData[0] = 0x313;
+  // ref
+  ndbrequire(errorCode != 0);
+  AccScanRef* ref = (AccScanRef*)signal->getDataPtrSend();
+  ref->scanPtr = req->senderData;
+  ref->accPtr = RNIL;
+  ref->errorCode = errorCode;
   sendSignal(req->senderRef, GSN_ACC_SCANREF,
-      signal, 1, JBB);
+      signal, AccScanRef::SignalLength, JBB);
 }
 
 /*
@@ -118,6 +143,9 @@ Dbtux::execACC_SCANREQ(Signal* signal)
  * Finally save the sets of lower and upper bounds (i.e. start key and
  * end key).  Full bound type is included but only the strict bit is
  * used since lower and upper have now been separated.
+ *
+ * Error handling:  Error code is set in the scan and also returned in
+ * EXECUTE_DIRECT (the old way).
  */
 void
 Dbtux::execTUX_BOUND_INFO(Signal* signal)
@@ -154,8 +182,8 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal
     const Uint32 dataSize = ah->getDataSize();
     if (type > 4 || attrId >= index.m_numAttrs || dstPos + 2 + dataSize > dstSize) {
       jam();
-      scan.m_state = ScanOp::Invalid;
-      sig->errorCode = TuxBoundInfo::InvalidAttrInfo;
+      scan.m_errorCode = TuxBoundInfo::InvalidAttrInfo;
+      sig->errorCode = scan.m_errorCode;
       return;
     }
     // copy header
@@ -174,16 +202,16 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal
       bool ok = NdbSqlUtil::get_var_length(typeId, srcPtr, maxBytes, lb, len);
       if (! ok) {
         jam();
-        scan.m_state = ScanOp::Invalid;
-        sig->errorCode = TuxBoundInfo::InvalidCharFormat;
+        scan.m_errorCode = TuxBoundInfo::InvalidCharFormat;
+        sig->errorCode = scan.m_errorCode;
         return;
       }
       Uint32 srcBytes = lb + len;
       Uint32 srcWords = (srcBytes + 3) / 4;
       if (srcBytes != byteSize) {
         jam();
-        scan.m_state = ScanOp::Invalid;
-        sig->errorCode = TuxBoundInfo::InvalidAttrInfo;
+        scan.m_errorCode = TuxBoundInfo::InvalidAttrInfo;
+        sig->errorCode = scan.m_errorCode;
         return;
       }
       uchar* dstPtr = (uchar*)&xfrmData[dstPos + 2];
@@ -201,8 +229,8 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal
         Uint32 dstLen = xmul * (maxBytes - lb);
         if (dstLen > ((dstSize - dstPos) << 2)) {
           jam();
-          scan.m_state = ScanOp::Invalid;
-          sig->errorCode = TuxBoundInfo::TooMuchAttrInfo;
+          scan.m_errorCode = TuxBoundInfo::TooMuchAttrInfo;
+          sig->errorCode = scan.m_errorCode;
           return;
         }
         int n = NdbSqlUtil::strnxfrm_bug7284(cs, dstPtr, dstLen, srcPtr + lb, len);
@@ -236,8 +264,8 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal
             b.size != 2 + dstWords ||
             memcmp(&xfrmData[b.offset + 2], &xfrmData[dstPos + 2], dstWords << 2) != 0) {
           jam();
-          scan.m_state = ScanOp::Invalid;
-          sig->errorCode = TuxBoundInfo::InvalidBounds;
+          scan.m_errorCode = TuxBoundInfo::InvalidBounds;
+          sig->errorCode = scan.m_errorCode;
           return;
         }
       } else {
@@ -257,8 +285,8 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal
   }
   if (offset != req->boundAiLength) {
     jam();
-    scan.m_state = ScanOp::Invalid;
-    sig->errorCode = TuxBoundInfo::InvalidAttrInfo;
+    scan.m_errorCode = TuxBoundInfo::InvalidAttrInfo;
+    sig->errorCode = scan.m_errorCode;
     return;
   }
   for (unsigned j = 0; j <= 1; j++) {
@@ -269,20 +297,27 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal
       // check for gap or strict bound before last
       if (b.type2 == -1 || (i + 1 < maxAttrId[j] && (b.type2 & 0x1))) {
         jam();
-        scan.m_state = ScanOp::Invalid;
-        sig->errorCode = TuxBoundInfo::InvalidBounds;
+        scan.m_errorCode = TuxBoundInfo::InvalidBounds;
+        sig->errorCode = scan.m_errorCode;
         return;
       }
       bool ok = scan.m_bound[j]->append(&xfrmData[b.offset], b.size);
       if (! ok) {
         jam();
-        scan.m_state = ScanOp::Invalid;
-        sig->errorCode = TuxBoundInfo::OutOfBuffers;
+        scan.m_errorCode = TuxBoundInfo::OutOfBuffers;
+        sig->errorCode = scan.m_errorCode;
         return;
       }
     }
     scan.m_boundCnt[j] = maxAttrId[j];
   }
+  if (ERROR_INSERTED(12009)) {
+    jam();
+    CLEAR_ERROR_INSERT_VALUE;
+    scan.m_errorCode = TuxBoundInfo::InvalidBounds;
+    sig->errorCode = scan.m_errorCode;
+    return;
+  }
   // no error
   sig->errorCode = 0;
 }
@@ -428,6 +463,18 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal
         signal, signalLength, JBB);
     return;     // stop
   }
+  // check index online
+  const Index& index = *c_indexPool.getPtr(frag.m_indexId);
+  if (unlikely(index.m_state != Index::Online) &&
+      scanPtr.p->m_errorCode == 0) {
+    jam();
+#ifdef VM_TRACE
+    if (debugFlags & (DebugMeta | DebugScan)) {
+      debugOut << "Index dropping at execACC_CHECK_SCAN " << scanPtr.i << " " << *scanPtr.p << endl;
+    }
+#endif
+    scanPtr.p->m_errorCode = AccScanRef::TuxIndexNotOnline;
+  }
   if (scan.m_state == ScanOp::First) {
     jam();
     // search is done only once in single range scan
@@ -570,8 +617,7 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal
     return;
   }
   // XXX in ACC this is checked before req->checkLcpStop
-  if (scan.m_state == ScanOp::Last ||
-      scan.m_state == ScanOp::Invalid) {
+  if (scan.m_state == ScanOp::Last) {
     jam();
     NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
     conf->scanPtr = scan.m_userPtr;
@@ -665,7 +711,10 @@ Dbtux::execACCKEYREF(Signal* signal)
     // scan position should already have been moved (assert only)
     if (scan.m_state == ScanOp::Blocked) {
       jam();
-      ndbassert(false);
+      // can happen when Dropping
+      const Frag& frag = *c_fragPool.getPtr(scan.m_fragId);
+      const Index& index = *c_indexPool.getPtr(frag.m_indexId);
+      ndbassert(index.m_state != Index::Online);
       scan.m_state = ScanOp::Next;
     }
     // LQH has the ball
@@ -963,11 +1012,19 @@ Dbtux::scanNext(ScanOpPtr scanPtr, bool
 
 /*
  * Check end key.  Return true if scan is still within range.
+ *
+ * Error handling:  If scan error code has been set, return false at
+ * once.  This terminates the scan and also avoids kernel crash on
+ * invalid data.
  */
 bool
 Dbtux::scanCheck(ScanOpPtr scanPtr, TreeEnt ent)
 {
   ScanOp& scan = *scanPtr.p;
+  if (unlikely(scan.m_errorCode != 0)) {
+    jam();
+    return false;
+  }
   Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
   const unsigned idir = scan.m_descending;
   const int jdir = 1 - 2 * (int)idir;
@@ -988,11 +1045,19 @@ Dbtux::scanCheck(ScanOpPtr scanPtr, Tree
  * There is a special check to never accept same tuple twice in a row.
  * This is faster than asking TUP.  It also fixes some special cases
  * which are not analyzed or handled yet.
+ *
+ * Error handling:  If scan error code has been set, return false since
+ * no new result can be returned to LQH.  The scan will then look for
+ * next result and terminate wia scanCheck():
  */
 bool
 Dbtux::scanVisible(ScanOpPtr scanPtr, TreeEnt ent)
 {
   const ScanOp& scan = *scanPtr.p;
+  if (unlikely(scan.m_errorCode != 0)) {
+    jam();
+    return false;
+  }
   const Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
   Uint32 tableFragPtrI = frag.m_tupTableFragPtrI;
   Uint32 pageId = ent.m_tupLoc.getPageId();
@@ -1016,6 +1081,9 @@ Dbtux::scanVisible(ScanOpPtr scanPtr, Tr
 /*
  * Finish closing of scan and send conf.  Any lock wait has been done
  * already.
+ *
+ * Error handling:  Every scan ends here.  If error code has been set,
+ * send a REF.
  */
 void
 Dbtux::scanClose(Signal* signal, ScanOpPtr scanPtr)
@@ -1027,14 +1095,26 @@ Dbtux::scanClose(Signal* signal, ScanOpP
     jam();
     abortAccLockOps(signal, scanPtr);
   }
-  // send conf
-  NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
-  conf->scanPtr = scanPtr.p->m_userPtr;
-  conf->accOperationPtr = RNIL;
-  conf->fragId = RNIL;
-  unsigned signalLength = 3;
-  sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF,
-      signal, signalLength, JBB);
+  if (scanPtr.p->m_errorCode == 0) {
+    jam();
+    // send conf
+    NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
+    conf->scanPtr = scanPtr.p->m_userPtr;
+    conf->accOperationPtr = RNIL;
+    conf->fragId = RNIL;
+    unsigned signalLength = 3;
+    sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF,
+        signal, signalLength, JBB);
+  } else {
+    // send ref
+    NextScanRef* ref = (NextScanRef*)signal->getDataPtr();
+    ref->scanPtr = scanPtr.p->m_userPtr;
+    ref->accOperationPtr = RNIL;
+    ref->fragId = RNIL;
+    ref->errorCode = scanPtr.p->m_errorCode;
+    sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANREF,
+               signal, NextScanRef::SignalLength, JBB);
+  }
   releaseScanOp(scanPtr);
 }
 

=== modified file 'storage/ndb/src/ndbapi/ndberror.c'
--- a/storage/ndb/src/ndbapi/ndberror.c	2010-03-01 14:03:57 +0000
+++ b/storage/ndb/src/ndbapi/ndberror.c	2010-12-02 18:10:25 +0000
@@ -418,6 +418,8 @@ ErrorBundle ErrorCodes[] = {
   { 906,  DMEC, SE, "Unsupported attribute type in index" },
   { 907,  DMEC, SE, "Unsupported character set in table or index" },
   { 908,  DMEC, IS, "Invalid ordered index tree node size" },
+  { 909,  DMEC, IE, "No free index scan op" },
+  { 910, HA_ERR_NO_SUCH_TABLE, SE, "Index is being dropped" },
   { 1224, HA_WRONG_CREATE_OPTION, SE, "Too many fragments" },
   { 1225, DMEC, SE, "Table not defined in local query handler" },
   { 1226, DMEC, SE, "Table is being dropped" },

=== modified file 'storage/ndb/test/run-test/daily-basic-tests.txt'
--- a/storage/ndb/test/run-test/daily-basic-tests.txt	2010-11-24 12:06:52 +0000
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt	2010-12-02 18:10:25 +0000
@@ -1495,3 +1495,7 @@ max-time: 300
 cmd: testIndex
 args: -n Bug56829 T1
 
+max-time: 300
+cmd: testDict
+args: -n Bug58277 T1
+


Attachment: [text/bzr-bundle] bzr/pekka@mysql.com-20101202181025-35gq9pck2foputey.bundle
Thread
bzr commit into mysql-5.1-telco-6.3 branch (pekka:3361) Bug#58277Pekka Nousiainen2 Dec