List:Commits« Previous MessageNext Message »
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
View as plain text  
 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 <NdbApi.hpp>
 #include <NDBT.hpp>
 
-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).
Thread
bzr push into mysql-5.1-telco-7.0 branch (pekka.nousiainen:4956 to 4957)Bug#13881465Pekka Nousiainen5 Aug