#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#58277 | Pekka Nousiainen | 2 Dec |