MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Frazer Clement Date:May 21 2008 4:40pm
Subject:bk commit into 5.1 tree (frazer:1.2598) BUG#36756
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of frazer.  When frazer does a push these changes
will be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2008-05-21 17:39:51+01:00, frazer@stripped +5 -0
  Bug#36756 IgnoreError Delete of nonexisting tuple aborts
  
  Problem was that once a transaction handled Blobs, AbortOption was treated differently.

  mysql-test/suite/ndb/r/ndb_blob.result@stripped, 2008-05-21 17:39:34+01:00, frazer@stripped +19 -0
    Testcase from bug report

  mysql-test/suite/ndb/t/ndb_blob.test@stripped, 2008-05-21 17:39:35+01:00, frazer@stripped +27 -0
    Testcase from bug report

  storage/ndb/src/ndbapi/NdbTransaction.cpp@stripped, 2008-05-21 17:39:36+01:00, frazer@stripped +23 -1
    If user supplies AbortOption at execute() time, make sure it is applied to all operations in the batch.

  storage/ndb/test/ndbapi/testBlobs.cpp@stripped, 2008-05-21 17:39:38+01:00, frazer@stripped +254 -1
    Test AbortOption and IgnoreError behaviour for Blob operations

  storage/ndb/test/run-test/daily-basic-tests.txt@stripped, 2008-05-21 17:39:40+01:00, frazer@stripped +6 -2
    Add test functionality to dailyBasic

diff -Nrup a/mysql-test/suite/ndb/r/ndb_blob.result b/mysql-test/suite/ndb/r/ndb_blob.result
--- a/mysql-test/suite/ndb/r/ndb_blob.result	2008-04-07 13:05:16 +01:00
+++ b/mysql-test/suite/ndb/r/ndb_blob.result	2008-05-21 17:39:34 +01:00
@@ -601,3 +601,22 @@ a	length(blob_nn)	length(text_nn)	blob_n
 1	0	0	NULL	NULL
 2	0	0	NULL	MySQL Cluster NDB
 drop table t1;
+create table `t1` (
+`f1` int(11) not null default -1,
+`f11` text,
+unique key `i1` (`f1`)
+) engine=ndbcluster default charset=utf8;
+insert into `t1` values (123,null);
+create table `t2` (
+`f1` int(11) not null default -1,
+unique key `i2` (`f1`)
+) engine=ndbcluster default charset=utf8;
+begin;
+delete from t2 where f1=5;
+delete from t1 where f1=123;
+delete from t2 where f1=5;
+commit;
+show warnings;
+Level	Code	Message
+drop table t1;
+drop table t2;
diff -Nrup a/mysql-test/suite/ndb/t/ndb_blob.test b/mysql-test/suite/ndb/t/ndb_blob.test
--- a/mysql-test/suite/ndb/t/ndb_blob.test	2008-04-07 13:05:16 +01:00
+++ b/mysql-test/suite/ndb/t/ndb_blob.test	2008-05-21 17:39:35 +01:00
@@ -538,3 +538,30 @@ insert into t1(a, text_nl) values (2, 'M
 select a, length(blob_nn), length(text_nn), blob_nl, text_nl from t1 order by a;
 
 drop table t1;
+
+# bug # 36756
+#  Behaviour of delete of non existing row should not be affected 
+#  by presence of Blob operations in the same transaction
+#  Specifically, transaction should not be aborted.
+create table `t1` (
+  `f1` int(11) not null default -1,
+  `f11` text,
+  unique key `i1` (`f1`)
+) engine=ndbcluster default charset=utf8;
+
+insert into `t1` values (123,null);
+
+create table `t2` (
+  `f1` int(11) not null default -1,
+  unique key `i2` (`f1`)
+) engine=ndbcluster default charset=utf8;
+
+begin;
+delete from t2 where f1=5;   # No such row, no problem
+delete from t1 where f1=123; # Blob operation
+delete from t2 where f1=5;   # No such row, no problem
+commit;
+show warnings;
+
+drop table t1;
+drop table t2;
diff -Nrup a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp
--- a/storage/ndb/src/ndbapi/NdbTransaction.cpp	2008-04-22 20:36:03 +01:00
+++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp	2008-05-21 17:39:36 +01:00
@@ -307,8 +307,30 @@ NdbTransaction::execute(ExecType aTypeOf
    * NdbBlob::postExecute() for more info.
    */
 
-  ExecType tExecType;
   NdbOperation* tPrepOp;
+
+  if (abortOption != NdbOperation::DefaultAbortOption)
+  {
+    DBUG_PRINT("info", ("Forcing operations to take execute() abortOption %d",
+                        abortOption));
+    /* For Blobs, we have to execute with DefaultAbortOption
+     * If the user supplied a non default AbortOption to execute()
+     * then we need to make sure that all of the operations in their
+     * batch are set to use the supplied AbortOption so that the 
+     * expected behaviour is obtained when executing below
+     */
+    tPrepOp= theFirstOpInList;
+    while(tPrepOp != NULL)
+    {
+      DBUG_PRINT("info", ("Changing abortOption from %d", 
+                          tPrepOp->m_abortOption));
+      tPrepOp->m_abortOption= abortOption;
+      tPrepOp= tPrepOp->next();
+    }
+  }
+
+
+  ExecType tExecType;
   NdbOperation* tCompletedFirstOp = NULL;
   NdbOperation* tCompletedLastOp = NULL;
 
diff -Nrup a/storage/ndb/test/ndbapi/testBlobs.cpp b/storage/ndb/test/ndbapi/testBlobs.cpp
--- a/storage/ndb/test/ndbapi/testBlobs.cpp	2008-02-19 15:00:28 +00:00
+++ b/storage/ndb/test/ndbapi/testBlobs.cpp	2008-05-21 17:39:38 +01:00
@@ -165,6 +165,7 @@ printusage()
     << "  -bug 4088   ndb api hang with mixed ops on index table" << endl
     << "  -bug 27018  middle partial part write clobbers rest of part" << endl
     << "  -bug 27370  Potential inconsistent blob reads for ReadCommitted reads" << endl
+    << "  -bug 36756  Handling execute(.., abortOption) and Blobs " << endl
     ;
 }
 
@@ -3020,13 +3021,265 @@ bugtest_27370()
   return 0;
 }
 
+enum OpTypes { 
+  Read,
+  Insert,
+  Update,
+  Write,
+  Delete };
+
+static const char*
+operationName(OpTypes optype)
+{
+  switch(optype){
+  case Read:
+    return "Read";
+  case Insert:
+    return "Insert";
+  case Update:
+    return "Update";
+  case Write:
+    return "Write";
+  case Delete:
+    return "Delete";
+  default:
+    return "Bad operation type";
+  }
+}
+
+static const char*
+aoName(int abortOption)
+{
+  if (abortOption == 0)
+    return "AbortOnError";
+  return "IgnoreError";
+}
+
+static int
+setOperationType(NdbOperation* op, OpTypes optype)
+{
+  switch(optype){
+  case Read:
+    CHK(op->readTuple() == 0);
+    break;
+  case Insert:
+    CHK(op->insertTuple() == 0);
+    break;
+  case Update:
+    CHK(op->updateTuple() == 0);
+    break;
+  case Write:
+    CHK(op->writeTuple() == 0);
+    break;
+  case Delete:
+    CHK(op->deleteTuple() == 0);
+    break;
+  default:
+    CHK(false);
+    return -1;
+  }
+  return 0;
+}
+
+static int
+performOpSpecifics(NdbOperation* op, OpTypes optype, Tup& tup)
+{
+  CHK(getBlobHandles(op) == 0);
+
+  switch(optype){
+  case Read:
+    CHK(getBlobValue(tup) == 0);
+    break;
+  case Insert:
+  case Update:
+    /* Fall through */
+  case Write:
+    CHK(setBlobValue(tup) == 0);
+    break;
+  case Delete:
+    /* Nothing */
+    break;
+  default:
+    CHK(false);
+    return -1;
+  }
+  return 0;
+}
+
+static int
+bugtest_36756()
+{
+  /* Transaction which had accessed a Blob table was ignoring
+   * abortOption passed in the execute() call.
+   * Check that option passed in execute() call overrides 
+   * Default / manually set operation abortOption, even in the
+   * presence of Blobs in the transaction
+   */
+
+  /* Operation
+   *                   Read       Insert        Update    Write    Delete
+   * AbortOnError      Missing*   Duplicate     Missing     -      Missing
+   * IgnoreError       Missing    Duplicate*    Missing*    -      Missing*
+   * 
+   * * Are interesting, where non-default behaviour is requested.
+   */
+  
+  struct ExpectedOutcome
+  {
+    int executeRc;
+    int transactionErrorCode;
+    int opr1ErrorCode;
+    int opr2ErrorCode;
+    int commitStatus;
+  };
+
+  ExpectedOutcome outcomes[5][2]=
+  {
+    // Read
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // Insert
+    {{-1, 630, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 630, 630, 0, NdbTransaction::Aborted}}, // IE
+    // Update
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // Write
+    {{0, 0, 0, 0, NdbTransaction::Started},      // AE
+     {0, 0, 0, 0, NdbTransaction::Started}},     // IE
+    // Delete
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}  // IE
+  };
+
+  DBG("bugtest_36756 : IgnoreError Delete of nonexisting tuple aborts");
+
+  for (int iterations=0; iterations < 50; iterations++)
+  {
+    /* Recalculate and insert different tuple every time to 
+     * get different keys(and therefore nodes), and
+     * different length Blobs, including zero length
+     * and NULL
+     */
+    calcTups(true);
+    
+    Tup& tupExists = g_tups[0];
+    Tup& tupDoesNotExist = g_tups[1];
+    
+    /* Setup table with just 1 row present */
+    CHK((g_con= g_ndb->startTransaction()) != 0);
+    CHK((g_opr= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+    CHK(g_opr->insertTuple() == 0);
+    CHK(g_opr->equal("PK1", tupExists.m_pk1) == 0);
+    if (g_opt.m_pk2chr.m_len != 0)
+    {
+      CHK(g_opr->equal("PK2", tupExists.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", tupExists.m_pk3) == 0);
+    }
+    CHK(getBlobHandles(g_opr) == 0);
+    
+    CHK(setBlobValue(tupExists) == 0);
+    
+    CHK(g_con->execute(Commit) == 0);
+    g_con->close();
+    
+    //DBG("Iteration : " << iterations);
+    for (int optype=Read; optype <= Delete; optype++)
+    {
+      //DBG("  " << operationName((OpTypes)optype));
+      for (int abortOption=0; abortOption < 2; abortOption++)
+      {
+        //DBG("    " << aoName(abortOption));
+        NdbOperation *opr1, *opr2;
+        NdbOperation::AbortOption ao= (abortOption==0)?
+          NdbOperation::AbortOnError : 
+          NdbOperation::AO_IgnoreError;
+        
+        CHK((g_con= g_ndb->startTransaction()) != 0);
+        
+        /* Operation 1 on existing tuple */
+        CHK((opr1= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+        
+        CHK(setOperationType(opr1, (OpTypes)optype) == 0);
+        
+        CHK(opr1->equal("PK1", tupExists.m_pk1) == 0);
+        if (g_opt.m_pk2chr.m_len != 0)
+        {
+          CHK(opr1->equal("PK2", tupExists.m_pk2) == 0);
+          CHK(opr1->equal("PK3", tupExists.m_pk3) == 0);
+        }
+        CHK(performOpSpecifics(opr1, (OpTypes)optype, tupExists) == 0);
+        
+        /* Operation2 on non existing tuple */
+        CHK((opr2= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+        
+        CHK(setOperationType(opr2, (OpTypes)optype) == 0);
+        
+        CHK(opr2->equal("PK1", tupDoesNotExist.m_pk1) == 0);
+        if (g_opt.m_pk2chr.m_len != 0)
+        {
+          CHK(opr2->equal("PK2", tupDoesNotExist.m_pk2) == 0);
+          CHK(opr2->equal("PK3", tupDoesNotExist.m_pk3) == 0);
+        }
+        CHK(performOpSpecifics(opr2, (OpTypes)optype, tupDoesNotExist) == 0);
+
+        ExpectedOutcome eo= outcomes[optype][abortOption];
+        
+        int rc = g_con->execute(NdbTransaction::NoCommit, ao);
+
+        //DBG("execute returned " << rc <<
+        //    " Trans err " << g_con->getNdbError().code <<
+        //    " Opr1 err " << opr1->getNdbError().code <<
+        //    " Opr2 err " << opr2->getNdbError().code <<
+        //    " CommitStatus " << g_con->commitStatus());
+
+        /* TODO : Bug#36851 Insert does not behave correctly with 
+         * IgnoreError */
+        if (! ((optype == 1) &&
+               (abortOption == 1)))
+        {
+          CHK(rc == eo.executeRc);        
+          CHK(opr1->getNdbError().code == eo.opr1ErrorCode);
+          CHK(opr2->getNdbError().code == eo.opr2ErrorCode);
+          CHK(g_con->commitStatus() == eo.commitStatus);
+        }
+
+        CHK(g_con->getNdbError().code == eo.transactionErrorCode);
+        
+        g_con->close();
+      }
+    }
+    
+    /* Now delete the 'existing'row */
+    CHK((g_con= g_ndb->startTransaction()) != 0);
+    CHK((g_opr= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+    CHK(g_opr->deleteTuple() == 0);
+    CHK(g_opr->equal("PK1", tupExists.m_pk1) == 0);
+    if (g_opt.m_pk2chr.m_len != 0)
+    {
+      CHK(g_opr->equal("PK2", tupExists.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", tupExists.m_pk3) == 0);
+    }
+
+    CHK(g_con->execute(Commit) == 0);
+    g_con->close();
+  }
+
+  g_opr= 0;
+  g_con= 0;
+  g_bh1= 0;
+
+  return 0;
+}
+
 static struct {
   int m_bug;
   int (*m_test)();
 } g_bugtest[] = {
   { 4088, bugtest_4088 },
   { 27018, bugtest_27018 },
-  { 27370, bugtest_27370 }
+  { 27370, bugtest_27370 },
+  { 36756, bugtest_36756 }
 };
 
 NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
diff -Nrup a/storage/ndb/test/run-test/daily-basic-tests.txt b/storage/ndb/test/run-test/daily-basic-tests.txt
--- a/storage/ndb/test/run-test/daily-basic-tests.txt	2008-04-28 15:17:18 +01:00
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt	2008-05-21 17:39:40 +01:00
@@ -794,11 +794,15 @@ args:
 
 max-time: 600
 cmd: testBlobs
-args: -bug 27018
+args: -bug 27018 -skip p
 
 max-time: 600
 cmd: testBlobs
-args: -bug 27370
+args: -bug 27370 -skip p
+
+max-time: 600
+cmd: testBlobs
+args: -bug 36756 -skip p
 
 max-time: 5000
 cmd: testOIBasic
Thread
bk commit into 5.1 tree (frazer:1.2598) BUG#36756Frazer Clement21 May