MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Frazer Clement Date:May 23 2008 10:20am
Subject:bk commit into 5.1 tree (frazer:1.2601) BUG#36851
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-23 11:20:10+01:00, frazer@stripped +2 -0
  Bug#36851 : Insert, IgnoreError and Blobs
  
  Where an Insert has IgnoreError abortOption, don't initiate head and part update operations until 
  Insert operation has succeeded.
  This avoids trampling the Blob head or unwanted transaction abort on the Blob part operations.

  storage/ndb/src/ndbapi/NdbBlob.cpp@stripped, 2008-05-23 11:20:00+01:00, frazer@stripped +101 -23
    Bug#36851 : Insert, IgnoreError and Blobs
    
    Where an Insert has IgnoreError abortOption, don't initiate head and part update operations until 
    Insert operation has succeeded.
    This avoids corrupting the Blob head or unwanted transaction abort on the Blob part operations.

  storage/ndb/test/ndbapi/testBlobs.cpp@stripped, 2008-05-23 11:20:02+01:00, frazer@stripped +316 -251
    Bug#36851 : Insert, IgnoreError and Blobs
    
    Testcase re-enabled and improved.  Code moved to avoid DBG re-#define.

diff -Nrup a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp
--- a/storage/ndb/src/ndbapi/NdbBlob.cpp	2008-04-08 09:29:10 +01:00
+++ b/storage/ndb/src/ndbapi/NdbBlob.cpp	2008-05-23 11:20:00 +01:00
@@ -2419,33 +2419,62 @@ NdbBlob::preExecute(NdbTransaction::Exec
     }
   }
   if (isInsertOp() && theSetFlag) {
-    /* Add operations to insert parts and update the
-     * Blob head+inline in the main tables
+    /* If the main operation uses AbortOnError then
+     * we can add operations to insert parts and update
+     * the Blob head+inline here.
+     * If the main operation uses IgnoreError then
+     * we have to wait until we are sure that the main
+     * insert succeeded before performing any other
+     * operations (Otherwise we may perform duplicate insert,
+     * and the transaction can fail on the AbortOnError 
+     * part operations or corrupt the head with the 
+     * post-update operation)
      */
-    if (theGetSetBytes > theInlineSize) {
-      // add ops to write rest of a setValue
-      assert(theSetBuf != NULL);
-      const char* buf = theSetBuf + theInlineSize;
-      Uint32 bytes = theGetSetBytes - theInlineSize;
-      assert(thePos == theInlineSize);
-      if (writeDataPrivate(buf, bytes) == -1)
-        DBUG_RETURN(-1);
-    }
-    
-    if (theHeadInlineUpdateFlag)
+    bool performExtraInsertOpsInPreExec= 
+      (theNdbOp->m_abortOption != NdbOperation::AO_IgnoreError);
+
+    if (performExtraInsertOpsInPreExec)
     {
-      NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
-      if (tOp == NULL ||
-          tOp->updateTuple() == -1 ||
-          setTableKeyValue(tOp) == -1 ||
-          setHeadInlineValue(tOp) == -1) {
-        setErrorCode(NdbBlobImpl::ErrAbort);
-        DBUG_RETURN(-1);
+      DBUG_PRINT("info", 
+                 ("Insert abortError - extra ops added in preExecute"));
+      /* Add operations to insert parts and update the
+       * Blob head+inline in the main tables
+       */
+      if (theGetSetBytes > theInlineSize) {
+        // add ops to write rest of a setValue
+        assert(theSetBuf != NULL);
+        const char* buf = theSetBuf + theInlineSize;
+        Uint32 bytes = theGetSetBytes - theInlineSize;
+        assert(thePos == theInlineSize);
+        if (writeDataPrivate(buf, bytes) == -1)
+          DBUG_RETURN(-1);
       }
-      if (thePartitionId != noPartitionId()) {
-        tOp->setPartitionId(thePartitionId);
+      
+      if (theHeadInlineUpdateFlag)
+      {
+        NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
+        if (tOp == NULL ||
+            tOp->updateTuple() == -1 ||
+            setTableKeyValue(tOp) == -1 ||
+            setHeadInlineValue(tOp) == -1) {
+          setErrorCode(NdbBlobImpl::ErrAbort);
+          DBUG_RETURN(-1);
+        }
+        if (thePartitionId != noPartitionId()) {
+          tOp->setPartitionId(thePartitionId);
+        }
+        DBUG_PRINT("info", ("Insert : added op to update head+inline in preExecute"));
       }
-      DBUG_PRINT("info", ("Insert : added op to update head+inline"));
+    }
+    else
+    {
+      DBUG_PRINT("info", 
+                 ("Insert ignoreError - waiting for Blob head insert"));
+      /* Require that this insert op is completed 
+       * before beginning more user ops - avoid interleave
+       * with delete etc.
+       */
+      batch= true;
     }
   }
 
@@ -2687,6 +2716,55 @@ NdbBlob::postExecute(NdbTransaction::Exe
         DBUG_RETURN(-1);
     }
   }
+  if (isInsertOp() && theSetFlag) {
+    /* For Inserts where the main table operation is IgnoreError, 
+     * we perform extra operations on the head and inline parts
+     * now
+     */
+    bool performDelayedInsertOpsInPostExec= 
+      (theNdbOp->m_abortOption == NdbOperation::AO_IgnoreError);
+
+    if (performDelayedInsertOpsInPostExec)
+    {
+      DBUG_PRINT("info", ("Insert IgnoreError adding extra ops"));
+      /* Check the main table op for an error (don't proceed if 
+       * it failed) 
+       */
+      if (theNdbOp->theError.code == 0)
+      {
+        /* Add operations to insert parts and update the
+         * Blob head+inline in the main table
+         */
+        if (theGetSetBytes > theInlineSize) {
+          // add ops to write rest of a setValue
+          assert(theSetBuf != NULL);
+          const char* buf = theSetBuf + theInlineSize;
+          Uint32 bytes = theGetSetBytes - theInlineSize;
+          assert(thePos == theInlineSize);
+          if (writeDataPrivate(buf, bytes) == -1)
+            DBUG_RETURN(-1);
+        }
+        
+        if (theHeadInlineUpdateFlag)
+        {
+          NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
+          if (tOp == NULL ||
+              tOp->updateTuple() == -1 ||
+              setTableKeyValue(tOp) == -1 ||
+              setHeadInlineValue(tOp) == -1) {
+            setErrorCode(NdbBlobImpl::ErrAbort);
+            DBUG_RETURN(-1);
+          }
+          if (thePartitionId != noPartitionId()) {
+            tOp->setPartitionId(thePartitionId);
+          }
+          DBUG_PRINT("info", ("Insert : added op to update head+inline"));
+        }
+      }
+      // NOTE : Could map IgnoreError insert error onto Blob here
+    }
+  }
+
   if (isUpdateOp()) {
     assert(anExecType == NdbTransaction::NoCommit);
     getHeadFromRecAttr();
diff -Nrup a/storage/ndb/test/ndbapi/testBlobs.cpp b/storage/ndb/test/ndbapi/testBlobs.cpp
--- a/storage/ndb/test/ndbapi/testBlobs.cpp	2008-05-21 17:39:38 +01:00
+++ b/storage/ndb/test/ndbapi/testBlobs.cpp	2008-05-23 11:20:02 +01:00
@@ -2199,6 +2199,322 @@ deleteScan(int api, bool idx)
   return 0;
 }
 
+
+enum OpTypes { 
+  PkRead,
+  PkInsert,
+  PkUpdate,
+  PkWrite,
+  PkDelete,
+  UkRead,
+  UkUpdate,
+  UkWrite,
+  UkDelete};
+
+static const char*
+operationName(OpTypes optype)
+{
+  switch(optype){
+  case PkRead:
+    return "Pk Read";
+  case PkInsert:
+    return "Pk Insert";
+  case PkUpdate:
+    return "Pk Update";
+  case PkWrite:
+    return "Pk Write";
+  case PkDelete:
+    return "Pk Delete";
+  case UkRead:
+    return "Uk Read";
+  case UkUpdate:
+    return "Uk Update";
+  case UkWrite:
+    return "Uk Write";
+  case UkDelete:
+    return "Uk Delete";
+  default:
+    return "Bad operation type";
+  }
+}
+
+static const char*
+aoName(int abortOption)
+{
+  if (abortOption == 0)
+    return "AbortOnError";
+  return "IgnoreError";
+}
+
+static int
+setupOperation(NdbOperation*& op, OpTypes optype, Tup& tup)
+{
+  bool pkop;
+  switch(optype){
+  case PkRead: case PkInsert : case PkUpdate: 
+  case PkWrite : case PkDelete :
+    pkop=true;
+    break;
+  default:
+    pkop= false;
+  }
+  
+  if (pkop)
+    CHK((op= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+  else
+    CHK((op = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+
+  switch(optype){
+  case PkRead:
+  case UkRead:
+    CHK(op->readTuple() == 0);
+    break;
+  case PkInsert:
+    CHK(op->insertTuple() == 0);
+    break;
+  case PkUpdate:
+  case UkUpdate:
+    CHK(op->updateTuple() == 0);
+    break;
+  case PkWrite:
+  case UkWrite:
+    CHK(op->writeTuple() == 0);
+    break;
+  case PkDelete:
+  case UkDelete:
+    CHK(op->deleteTuple() == 0);
+    break;
+  default:
+    CHK(false);
+    return -1;
+  }
+  
+  if (pkop)
+  {
+    CHK(op->equal("PK1", tup.m_pk1) == 0);
+    if (g_opt.m_pk2chr.m_len != 0)
+    {
+      CHK(op->equal("PK2", tup.m_pk2) == 0);
+      CHK(op->equal("PK3", tup.m_pk3) == 0);
+    }
+  }
+  else
+  {
+    CHK(op->equal("PK2", tup.m_pk2) == 0);
+    CHK(op->equal("PK3", tup.m_pk3) == 0);
+  }
+  
+  CHK(getBlobHandles(op) == 0);
+  
+  switch(optype){
+  case PkRead:
+  case UkRead:
+    CHK(getBlobValue(tup) == 0);
+    break;
+  case PkInsert:
+  case PkUpdate:
+  case UkUpdate:
+    /* Fall through */
+  case PkWrite:
+  case UkWrite:
+    CHK(setBlobValue(tup) == 0);
+    break;
+  case PkDelete:
+  case UkDelete:
+    /* 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         AbortOnError             IgnoreError
+   * PkRead            NoDataFound*             NoDataFound
+   * PkInsert          Duplicate key            Duplicate key*
+   * PkUpdate          NoDataFound              NoDataFound*
+   * PkWrite           NoDataFound              NoDataFound*
+   * PkDelete          NoDataFound              NoDataFound*
+   * UkRead            NoDataFound*             NoDataFound
+   * UkUpdate          NoDataFound              NoDataFound*
+   * UkWrite           NoDataFound              NoDataFound*
+   * UkDelete          NoDataFound              NoDataFound*
+   * 
+   * * Are interesting, where non-default behaviour is requested.
+   */
+  
+  struct ExpectedOutcome
+  {
+    int executeRc;
+    int transactionErrorCode;
+    int opr1ErrorCode;
+    int opr2ErrorCode;
+    int commitStatus;
+  };
+
+  /* Generally, AbortOnError sets the transaction error
+   * but not the Operation error codes
+   * IgnoreError sets the transaction error and the
+   * failing operation error code(s)
+   * Odd cases : 
+   *   Pk Write : Can't fail due to key presence, just
+   *              incorrect NULLs etc.
+   *   Uk Write : Key must exist, so not really different
+   *              to Update?
+   */
+  ExpectedOutcome outcomes[9][2]=
+  {
+    // PkRead
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // PkInsert
+    // Note operation order reversed for insert
+    {{-1, 630, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 630, 0, 630, NdbTransaction::Started}}, // IE
+    // PkUpdate
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // PkWrite
+    {{0, 0, 0, 0, NdbTransaction::Started},      // AE
+     {0, 0, 0, 0, NdbTransaction::Started}},     // IE
+    // PkDelete
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // UkRead
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // UkUpdate
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // UkWrite
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}, // IE
+    // UkDelete
+    {{-1, 626, 0, 0, NdbTransaction::Aborted},   // AE
+     {0, 626, 0, 626, NdbTransaction::Started}}  // IE
+  };
+
+  DBG("bugtest_36756 : IgnoreError Delete of nonexisting tuple aborts");
+  DBG("                Also 36851 : Insert IgnoreError of existing 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=PkRead; optype <= UkDelete; optype++)
+    {
+      DBG("  " << operationName((OpTypes)optype));
+
+      Tup* tup1= &tupExists;
+      Tup* tup2= &tupDoesNotExist;
+
+      if (optype == PkInsert)
+      {
+        /* Inserts - we want the failing operation to be second
+         * rather than first to avoid hitting bugs with IgnoreError
+         * and the first DML in a transaction
+         * So we swap them
+         */
+        tup1= &tupDoesNotExist; // (Insert succeeds)
+        tup2= &tupExists; //(Insert fails)
+      }
+
+      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 */
+        CHK(setupOperation(opr1, (OpTypes)optype, *tup1) == 0);
+        
+        /* Operation2 */
+        CHK(setupOperation(opr2, (OpTypes)optype, *tup2) == 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());
+
+        CHK(rc == eo.executeRc);        
+        CHK(g_con->getNdbError().code == eo.transactionErrorCode);
+        CHK(opr1->getNdbError().code == eo.opr1ErrorCode);
+        CHK(opr2->getNdbError().code == eo.opr2ErrorCode);
+        CHK(g_con->commitStatus() == eo.commitStatus);
+        
+        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;
+}
+
 // main
 
 // from here on print always
@@ -3018,257 +3334,6 @@ bugtest_27370()
   g_con= NULL;
   g_const_opr= NULL;
   g_bh1= NULL;
-  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;
 }
 
Thread
bk commit into 5.1 tree (frazer:1.2601) BUG#36851Frazer Clement23 May