From: Pekka Nousiainen Date: August 5 2012 10:09am Subject: bzr push into mysql-5.1-telco-7.0 branch (pekka.nousiainen:4956 to 4957) Bug#13881465 List-Archive: http://lists.mysql.com/commits/144511 X-Bug: 13881465 Message-Id: <20120805100934.463.61918.4957@cuda> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 4957 Pekka Nousiainen 2012-08-05 bug#13881465 a02_patch_v3.diff bug test and patch v3 modified: mysql-test/suite/ndb/r/ndb_blob.result mysql-test/suite/ndb/t/ndb_blob.test storage/ndb/include/ndbapi/NdbBlob.hpp storage/ndb/include/ndbapi/NdbTransaction.hpp storage/ndb/src/ndbapi/NdbBlob.cpp storage/ndb/src/ndbapi/NdbTransaction.cpp storage/ndb/test/run-test/daily-basic-tests.txt storage/ndb/tools/ndb_blob_tool.cpp 4956 Mauritz Sundell 2012-07-12 ndb - autotest on ndb07 need use default storage engine myisam modified: storage/ndb/test/run-test/conf-ndb07.cnf === modified file 'mysql-test/suite/ndb/r/ndb_blob.result' --- a/mysql-test/suite/ndb/r/ndb_blob.result 2010-01-28 15:16:46 +0000 +++ b/mysql-test/suite/ndb/r/ndb_blob.result 2012-08-05 10:05:54 +0000 @@ -723,3 +723,61 @@ select a, length(b) from t1 FOR UPDATE; ERROR HY000: Lock wait timeout exceeded; try restarting transaction commit; drop table t1; +# bug#13881465 +create table t1 ( +a int unsigned not null, +b text not null, +primary key using hash (a) +) engine=ndb; +set @x1 = repeat('x1',140); +set @x2 = repeat('x2',140); +set @y1 = repeat('y1',140); +set @y2 = repeat('y2',140); +insert into t1 values (0+0x1133, @x1); +insert into t1 values (0+0x2233, @x2); +delete from t1; +insert into t1 values (0+0x1133, @y1); +insert into t1 values (0+0x2233, @y2); +delete from t1; +insert into t1 values (0+0x1133, @x1); +insert into t1 values (0+0x2233, @x2); +delete from t1 where b is not null; +insert into t1 values (0+0x1133, @y1); +insert into t1 values (0+0x2233, @y2); +delete from t1; +insert into t1 values (0+0x1133, @x1); +insert into t1 values (0+0x2233, @x2); +delete from t1 where substr(b,2*140-1,1) = 'x'; +insert into t1 values (0+0x1133, @y1); +insert into t1 values (0+0x2233, @y2); +delete from t1; +drop table t1; +create table t1 ( +a int unsigned not null, +b text, +c text, +primary key using hash (a) +) engine=ndb; +set @x1 = repeat('x1',5000); +set @x2 = repeat('x2',5000); +set @y1 = repeat('y1',5000); +set @y2 = repeat('y2',5000); +insert into t1 values (0+0x1133, @x1, @y1); +insert into t1 values (0+0x2233, @x2, @y2); +select sha1(b), sha1(c) from t1 order by a; +sha1(b) sha1(c) +9e29f5566c7ee71d03e1c658545be37110484730 5c8da1b925d954b81e53dc857afac4487fcf0c13 +684c12ec8c05dee9ae57bff333034e3c14393cf0 c49f848d220fe0dfbd62a5526c2e56b923aac741 +update t1 set b=replace(b,'x','y'), c=replace(c,'y','x'); +select sha1(b), sha1(c) from t1 order by a; +sha1(b) sha1(c) +5c8da1b925d954b81e53dc857afac4487fcf0c13 9e29f5566c7ee71d03e1c658545be37110484730 +c49f848d220fe0dfbd62a5526c2e56b923aac741 684c12ec8c05dee9ae57bff333034e3c14393cf0 +delete from t1 where substr(b,2*5000-1,1) = 'y' and substr(c,2*5000-1,1) = 'x'; +select count(*) from t1; +count(*) +0 +insert into t1 values (0+0x1133, @x1, @y1); +insert into t1 values (0+0x2233, @x2, @y2); +delete from t1; +drop table t1; === modified file 'mysql-test/suite/ndb/t/ndb_blob.test' --- a/mysql-test/suite/ndb/t/ndb_blob.test 2010-01-28 15:16:46 +0000 +++ b/mysql-test/suite/ndb/t/ndb_blob.test 2012-08-05 10:05:54 +0000 @@ -680,3 +680,73 @@ commit; connection con1; drop table t1; + +--echo # bug#13881465 +# DELETE BY SCAN LEAVES ORPHANED BLOB PART ROWS + +create table t1 ( + a int unsigned not null, + b text not null, + primary key using hash (a) +) engine=ndb; + +set @x1 = repeat('x1',140); +set @x2 = repeat('x2',140); +set @y1 = repeat('y1',140); +set @y2 = repeat('y2',140); + +# ok +insert into t1 values (0+0x1133, @x1); +insert into t1 values (0+0x2233, @x2); +delete from t1; +insert into t1 values (0+0x1133, @y1); +insert into t1 values (0+0x2233, @y2); +delete from t1; + +# failed (handler uselessly reads full blob) +insert into t1 values (0+0x1133, @x1); +insert into t1 values (0+0x2233, @x2); +delete from t1 where b is not null; +# --error ER_DUP_ENTRY +insert into t1 values (0+0x1133, @y1); +insert into t1 values (0+0x2233, @y2); +delete from t1; + +# failed (make sure full blob is read) +insert into t1 values (0+0x1133, @x1); +insert into t1 values (0+0x2233, @x2); +delete from t1 where substr(b,2*140-1,1) = 'x'; +# --error ER_DUP_ENTRY +insert into t1 values (0+0x1133, @y1); +insert into t1 values (0+0x2233, @y2); +delete from t1; + +drop table t1; + +# also test multiple blobs +create table t1 ( + a int unsigned not null, + b text, + c text, + primary key using hash (a) +) engine=ndb; + +set @x1 = repeat('x1',5000); +set @x2 = repeat('x2',5000); +set @y1 = repeat('y1',5000); +set @y2 = repeat('y2',5000); + +insert into t1 values (0+0x1133, @x1, @y1); +insert into t1 values (0+0x2233, @x2, @y2); +select sha1(b), sha1(c) from t1 order by a; +# would be nice to swap values but of course MySQL cannot +# update t1 set b = c, c = b; +update t1 set b=replace(b,'x','y'), c=replace(c,'y','x'); +select sha1(b), sha1(c) from t1 order by a; +delete from t1 where substr(b,2*5000-1,1) = 'y' and substr(c,2*5000-1,1) = 'x'; +select count(*) from t1; +insert into t1 values (0+0x1133, @x1, @y1); +insert into t1 values (0+0x2233, @x2, @y2); +delete from t1; + +drop table t1; === modified file 'storage/ndb/include/ndbapi/NdbBlob.hpp' --- a/storage/ndb/include/ndbapi/NdbBlob.hpp 2011-06-30 15:59:25 +0000 +++ b/storage/ndb/include/ndbapi/NdbBlob.hpp 2012-08-05 10:05:54 +0000 @@ -499,6 +499,7 @@ private: // pending ops int executePendingBlobReads(); int executePendingBlobWrites(); + int executePendingMainOps(); // callbacks int invokeActiveHook(); // blob handle maintenance === modified file 'storage/ndb/include/ndbapi/NdbTransaction.hpp' --- a/storage/ndb/include/ndbapi/NdbTransaction.hpp 2011-06-30 15:59:25 +0000 +++ b/storage/ndb/include/ndbapi/NdbTransaction.hpp 2012-08-05 10:05:54 +0000 @@ -1173,6 +1173,8 @@ private: Uint32 theBuddyConPtr; // optim: any blobs bool theBlobFlag; + // any prepared key ops with blobs + bool theBlobPreparedKeyOpsFlag; Uint8 thePendingBlobOps; Uint32 maxPendingBlobReadBytes; Uint32 maxPendingBlobWriteBytes; === modified file 'storage/ndb/src/ndbapi/NdbBlob.cpp' --- a/storage/ndb/src/ndbapi/NdbBlob.cpp 2011-12-09 19:15:17 +0000 +++ b/storage/ndb/src/ndbapi/NdbBlob.cpp 2012-08-05 10:05:54 +0000 @@ -1443,6 +1443,13 @@ NdbBlob::readDataPrivate(char* buf, Uint DBUG_RETURN(-1); } if (len > 0) { + if (theEventBlobVersion == -1) { + // likely to executeNoBlobs + if (executePendingMainOps() == -1) + DBUG_RETURN(-1); + } + } + if (len > 0) { assert(pos >= theInlineSize); Uint32 off = (pos - theInlineSize) % thePartSize; // partial first block @@ -2020,6 +2027,39 @@ NdbBlob::executePendingBlobWrites() DBUG_RETURN(0); } +/* + * bug#13881465 + * + * Calling executeNoBlobs() can execute previous prepared main ops, + * by-passing their preExecute() triggers. This method calls normal + * execute() first if there are any prepared key ops with blobs. + * + * The scenario in bug#13881465 was a scan delete batch, where user + * at 2nd result row first did a blob read where last part was partial, + * forcing executeNoBlobs(). + */ + +int +NdbBlob::executePendingMainOps() +{ + DBUG_ENTER("NdbBlob::executePendingMainOps"); +#ifdef VM_TRACE + { + const char* p = NdbEnv_GetEnv("NDB_BLOB_BUG13881465", (char*)0, 0); + if (p != 0 && strchr("1Y", p[0]) != 0) + DBUG_RETURN(0); + } +#endif + if (theNdbCon->theBlobPreparedKeyOpsFlag) { + if (theNdbCon->execute(NdbTransaction::NoCommit) == -1) + DBUG_RETURN(-1); + assert(theNdbCon->theBlobPreparedKeyOpsFlag == false); + } else { + DBUG_PRINT("info", ("no prepared blob ops in trans")); + } + DBUG_RETURN(0); +} + // callbacks int @@ -2118,6 +2158,12 @@ NdbBlob::atPrepareCommon(NdbTransaction* theTable = anOp->m_currentTable; theAccessTable = anOp->m_accessTable; theColumn = aColumn; + DBUG_PRINT("info", ("optype=%d", theNdbOp->theOperationType)); + // mark that transaction has prepared key ops with blobs + if (isKeyOp() && !theNdbCon->theBlobPreparedKeyOpsFlag) { + DBUG_PRINT("info", ("set transaction theBlobPreparedKeyOpsFlag = true")); + theNdbCon->theBlobPreparedKeyOpsFlag = true; + } // prepare blob column and table if (prepareColumn() == -1) return -1; === modified file 'storage/ndb/src/ndbapi/NdbTransaction.cpp' --- a/storage/ndb/src/ndbapi/NdbTransaction.cpp 2011-10-22 09:38:48 +0000 +++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp 2012-08-05 10:05:54 +0000 @@ -78,6 +78,7 @@ NdbTransaction::NdbTransaction( Ndb* aNd m_scanningQuery(NULL), theBuddyConPtr(0xFFFFFFFF), theBlobFlag(false), + theBlobPreparedKeyOpsFlag(false), thePendingBlobOps(0), maxPendingBlobReadBytes(~Uint32(0)), maxPendingBlobWriteBytes(~Uint32(0)), @@ -163,6 +164,7 @@ NdbTransaction::init() theBuddyConPtr = 0xFFFFFFFF; // theBlobFlag = false; + theBlobPreparedKeyOpsFlag = false; thePendingBlobOps = 0; m_theFirstLockHandle = NULL; m_theLastLockHandle = NULL; @@ -365,7 +367,7 @@ NdbTransaction::execute(ExecType aTypeOf * operation, in case it adds extra ops */ firstSavedOp = tPrepOp->next(); // Could be NULL - lastSavedOp = theLastOpInList; + lastSavedOp = firstSavedOp != NULL ? theLastOpInList : NULL; DBUG_PRINT("info", ("Splitting ops list between %p and %p", firstSavedOp, lastSavedOp)); tPrepOp->next(NULL); @@ -555,6 +557,12 @@ NdbTransaction::execute(ExecType aTypeOf theError = firstTransError; } + if (theBlobPreparedKeyOpsFlag) + { + DBUG_PRINT("info", ("set transaction theBlobPreparedKeyOpsFlag = false")); + theBlobPreparedKeyOpsFlag = false; + } + DBUG_RETURN(ret); } === modified file 'storage/ndb/test/run-test/daily-basic-tests.txt' --- a/storage/ndb/test/run-test/daily-basic-tests.txt 2012-06-26 09:18:05 +0000 +++ b/storage/ndb/test/run-test/daily-basic-tests.txt 2012-08-05 10:05:54 +0000 @@ -1874,4 +1874,8 @@ args: -n LeakApiConnectObjects T1 max-time: 300 cmd: testScan args: -n extraNextResultBug11748194 T1 + +max-time: 300 +cmd: ndb_blob_tool +args: --bug13881465=1 -r 100 === modified file 'storage/ndb/tools/ndb_blob_tool.cpp' --- a/storage/ndb/tools/ndb_blob_tool.cpp 2012-06-05 11:49:49 +0000 +++ b/storage/ndb/tools/ndb_blob_tool.cpp 2012-08-05 10:05:54 +0000 @@ -20,7 +20,7 @@ #include #include -static const char* opt_dbname = 0; +static const char* opt_dbname = "TEST_DB"; static my_bool opt_check_orphans = false; static my_bool opt_delete_orphans = false; static const char* opt_dump_file = 0; @@ -87,6 +87,10 @@ struct Val { // attr value scanned from static Val* g_vallist = 0; static int g_valcount = 0; +// bug tests +static int opt_bug13881465 = 0; +static int opt_records = 1000; + #define CHK1(b) \ if (!(b)) { \ ret = -1; \ @@ -494,12 +498,345 @@ doall() return ret; } +/* + * bug#13881465 + */ + +// blob length bigger than inline 256 and not complete parts 2000 +static const int bug13881465_blen = 280; // not 256+2000 + +// either of these options "disables" the bug +// do not read blob value before delete (bit 2) +static bool bug13881465_optnoread = false; +// use another transaction to do deleteCurrentTuple (bit 4) +static bool bug13881465_optnewtx = false; + +// other options just for testing +// use blob ActiveHook instead of getValue (bit 8) +static bool bug13881465_optactivehook = false; + +static NdbDictionary::RecordSpecification bug13881465_recspec[2]; + +static int +bug13881465_create() +{ + DBUG_ENTER("bug13881465_create"); + int ret = 0; + do + { + (void)g_dic->dropTable(g_tabname); + NdbDictionary::Table tab; + tab.setName(g_tabname); + { + NdbDictionary::Column c; + c.setName("a"); + c.setType(NdbDictionary::Column::Int); + c.setPrimaryKey(true); + tab.addColumn(c); + } + { + NdbDictionary::Column c; + c.setName("b"); + c.setType(NdbDictionary::Column::Text); + c.setNullable(false); + c.setPartSize(2000); + tab.addColumn(c); + } + CHK2(g_dic->createTable(tab) == 0, g_dic->getNdbError()); + CHK2((g_tab = g_dic->getTable(g_tabname)) != 0, g_dic->getNdbError()); + + NdbDictionary::RecordSpecification* rs = bug13881465_recspec; + { + const NdbDictionary::Column* c = g_tab->getColumn("a"); + assert(c != 0); + rs[0].column = c; + rs[0].offset = 0; + rs[0].nullbit_byte_offset = ~(Uint32)0; + rs[0].nullbit_bit_in_byte = ~(Uint32)0; + } + { + const NdbDictionary::Column* c = g_tab->getColumn("b"); + assert(c != 0); + rs[1].column = c; + rs[1].offset = 4; + rs[1].nullbit_byte_offset = ~(Uint32)0; + rs[1].nullbit_bit_in_byte = ~(Uint32)0; + } + } + while (0); + DBUG_RETURN(ret); +} + +static int +bug13881465_drop() +{ + DBUG_ENTER("bug13881465_drop"); + int ret = 0; + do + { + CHK2(g_dic->dropTable(g_tabname) == 0, g_dic->getNdbError()); + } + while (0); + DBUG_RETURN(ret); +} + +static void +bug13881465_bval(int k, const char* xx, char* buf) +{ + const int blen = bug13881465_blen; + sprintf(buf, "%s%d", xx, k); + int n = strlen(buf); + for (int i = 0; i < blen; i++) + { + buf[i] = buf[i % n]; + } +} + +static int +bug13881465_insert(int k, const char* xx) +{ + DBUG_ENTER("bug13881465_insert"); + int ret = 0; + do + { + NdbTransaction* tx = 0; + CHK2((tx = g_ndb->startTransaction()) != 0, g_ndb->getNdbError()); + + NdbOperation* op = 0; + CHK2((op = tx->getNdbOperation(g_tab)) != 0, tx->getNdbError()); + CHK2(op->insertTuple() == 0, op->getNdbError()); + + Int32 a = k + 0x12340000; + CHK2(op->equal("a", (const char*)&a) == 0, op->getNdbError()); + + const int blen = bug13881465_blen; + char buf[blen + 1]; + bug13881465_bval(k, xx, buf); + buf[blen] = 0; + NdbBlob* bh = 0; + CHK2((bh = op->getBlobHandle("b")) != 0, op->getNdbError()); + CHK2(bh->setValue(buf, blen) == 0, bh->getNdbError()); + + CHK2(tx->execute(Commit) == 0, tx->getNdbError()); + g_ndb->closeTransaction(tx); + } + while (0); + DBUG_RETURN(ret); +} + +static int +bug13881465_insertall(const char* xx) +{ + DBUG_ENTER("bug13881465_insertall"); + int ret = 0; + for (int k = 0; k < opt_records; k++) + { + CHK1(bug13881465_insert(k, xx) == 0); + } + DBUG_RETURN(ret); +} + +static int +bug13881465_activehook(NdbBlob* bh, void* arg) +{ + DBUG_ENTER("bug13881465_activehook"); + int ret = 0; + char* buf = (char*)arg; + do + { + int isNull = -1; + CHK2(bh->getNull(isNull) == 0, bh->getNdbError()); + CHK2(isNull == 0, "blob isNull=" << isNull); + + Uint64 length64; + CHK2(bh->getLength(length64) == 0, bh->getNdbError()); + int length = (int)length64; + const int blen = bug13881465_blen; + CHK2(length == blen, "length " << length << "!=" << blen); + + Uint32 bytes32 = (Uint32)length64; + CHK2(bh->readData(buf, bytes32) == 0, bh->getNdbError()); + int bytes = (int)bytes32; + CHK2(bytes == length, "bytes " << bytes << "!=" << length); + } + while (0); + DBUG_RETURN(ret); +} + +static int +bug13881465_scandelete(const char* xx, int &rows) +{ + DBUG_ENTER("bug13881465_scandelete"); + int ret = 0; + rows = 0; + do + { + NdbTransaction* scantx = 0; + CHK2((scantx = g_ndb->startTransaction()) != 0, g_ndb->getNdbError()); + + Int32 a = -1; + NdbRecord* rec = 0; + struct { Int32 a; char b[16+256]; } recbuf; + NdbScanOperation* scanop = 0; + + const NdbOperation::LockMode lm = NdbOperation::LM_Exclusive; + + { + const NdbDictionary::RecordSpecification* rs = bug13881465_recspec; + rec = 0; + CHK2((rec = g_dic->createRecord(g_tab, rs, 2, sizeof(rs[0]))) != 0, g_dic->getNdbError()); + Uint32 len = NdbDictionary::getRecordRowLength(rec); + assert(len == sizeof(recbuf)); + CHK2((scanop = scantx->scanTable(rec, lm)) != 0, scantx->getNdbError()); + } + + NdbBlob* bh = 0; + CHK2((bh = scanop->getBlobHandle("b")) != 0, scanop->getNdbError()); + + const int blen = bug13881465_blen; + char buf1[blen+1]; + if (!bug13881465_optnoread) + { + if (!bug13881465_optactivehook) + { + CHK2(bh->getValue(buf1, blen) == 0, bh->getNdbError()); + } + else + { + CHK2(bh->setActiveHook(bug13881465_activehook, (void*)buf1) == 0, bh->getNdbError()); + } + } + + CHK2(scantx->execute(NoCommit) == 0, scantx->getNdbError()); + + NdbTransaction* tx = 0; + if (!bug13881465_optnewtx) + { + // use scantx + } + else + { + CHK2((tx = g_ndb->startTransaction()) != 0, g_ndb->getNdbError()); + } + + int res = -1; + while (1) + { + a = -1; + memset(buf1, 0xff, blen); + res = scanop->nextResultCopyOut((char*)&recbuf, true, false); + CHK2(res == 0 || res == 1, scanop->getNdbError()); + if (res == 1) + break; + + int batch = 0; + while (1) + { + a = recbuf.a; + DBUG_PRINT("info", ("scanned a=%x", a)); + g_info << "scanned a=" << hex << a << endl; + buf1[blen] = 0; + + { + int k = a - 0x12340000; + CHK2(k >= 0 && k < opt_records, "a=" << a << " k=" << k); + if (!bug13881465_optnoread) + { + char buf2[blen + 1]; + bug13881465_bval(k, xx, buf2); + buf2[blen] = 0; + CHK2(memcmp(buf1, buf2, blen) == 0, buf1 << " VS " << buf2); + } + } + + const NdbOperation* delop = 0; + if (!bug13881465_optnewtx) + { + CHK2((delop = scanop->deleteCurrentTuple(scantx, rec)) != 0, scanop->getNdbError()); + } + else + { + CHK2((delop = scanop->deleteCurrentTuple(tx, rec)) != 0, scanop->getNdbError()); + } + DBUG_PRINT("info", ("delete op=%p", delop)); + batch++; + rows++; + + a = -1; + memset(buf1, 0xff, blen); + res = scanop->nextResultCopyOut((char*)&recbuf, false, false); + CHK2(res == 0 || res == 2, scanop->getNdbError()); + if (res == 2) + break; + } + CHK1(ret == 0); + g_info << "got batch: " << batch << endl; + + if (!bug13881465_optnewtx) + { + CHK2(scantx->execute(NoCommit) == 0, scantx->getNdbError()); + } + else + { + CHK2(tx->execute(NoCommit) == 0, tx->getNdbError()); + } + } + CHK1(ret == 0); + + if (!bug13881465_optnewtx) + { + CHK2(scantx->execute(Commit) == 0, scantx->getNdbError()); + } + else + { + CHK2(tx->execute(Commit) == 0, tx->getNdbError()); + g_ndb->closeTransaction(tx); + } + g_ndb->closeTransaction(scantx); + } + while (0); + DBUG_RETURN(ret); +} + +static int +bug13881465_run() +{ + DBUG_ENTER("bug13881465_run"); + int ret = 0; + g_tabname = newstr("t1"); + + do + { + bug13881465_optnoread = (opt_bug13881465 & 2); + bug13881465_optnewtx = (opt_bug13881465 & 4); + bug13881465_optactivehook = (opt_bug13881465 & 8); + g_err << "opt:" + << " noread(2)=" << bug13881465_optnoread + << " newtx(4)=" << bug13881465_optnewtx + << " activehook(8)=" << bug13881465_optactivehook + << endl; + + CHK1(doconnect() == 0); + CHK1(bug13881465_create() == 0); + CHK1(bug13881465_insertall("x") == 0); + int rows = -1; + CHK1(bug13881465_scandelete("x", rows) == 0); + CHK2(rows == opt_records, "wrong number of rows: " << rows); + CHK1(bug13881465_insertall("y") == 0); + CHK1(bug13881465_drop() == 0); + } + while (0); + + dodisconnect(); + DBUG_RETURN(ret); +} + static struct my_option my_long_options[] = { NDB_STD_OPTS("ndb_blob_tool"), { "database", 'd', - "Name of database table is in", + "Name of database table is in (default: TEST_DB)", (uchar**) &opt_dbname, (uchar**) &opt_dbname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, { "check-orphans", NDB_OPT_NOSHORT, @@ -518,6 +855,13 @@ my_long_options[] = "Verbose messages", (uchar **)&opt_verbose, (uchar **)&opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "bug13881465", NDB_OPT_NOSHORT, + "Run only bug#13881465 ndbapi test, arg is option bits (arg 1 runs default case), uses table t1", + (uchar **)&opt_bug13881465, (uchar **)&opt_bug13881465, 0, + GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "records", 'r', "Number of rows for bug# tests", + (uchar**) &opt_records, (uchar**) &opt_records, 0, + GET_INT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, @@ -546,9 +890,6 @@ usage() static int checkopts(int argc, char** argv) { - if (opt_dbname == 0) - opt_dbname = "TEST_DB"; - if (argc < 1) { g_err << "Table name required" << endl; @@ -602,14 +943,31 @@ main(int argc, char** argv) int ret; ndb_opt_set_usage_funcs(short_usage_sub, usage); ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option); - if (ret != 0 || checkopts(argc, argv) != 0) + if (ret != 0) return NDBT_ProgramExit(NDBT_WRONGARGS); setOutputLevel(opt_verbose ? 2 : 0); + bool bugtests = opt_bug13881465; + + if (!bugtests) + { + if (ret != 0 || checkopts(argc, argv) != 0) + return NDBT_ProgramExit(NDBT_WRONGARGS); + + ret = doall(); + freeall(); + if (ret == -1) + return NDBT_ProgramExit(NDBT_FAILED); + return NDBT_ProgramExit(NDBT_OK); + } + + if (opt_bug13881465) + { + ret = bug13881465_run(); + if (ret == -1) + return NDBT_ProgramExit(NDBT_FAILED); + return NDBT_ProgramExit(NDBT_OK); + } - ret = doall(); - freeall(); - if (ret == -1) - return NDBT_ProgramExit(NDBT_FAILED); - return NDBT_ProgramExit(NDBT_OK); + return NDBT_OK; } No bundle (reason: useless for push emails).