List:Commits« Previous MessageNext Message »
From:jonas oreland Date:April 5 2011 7:12pm
Subject:bzr commit into mysql-5.1-telco-7.1 branch (jonas:3946)
View as plain text  
#At file:///home/jonas/src/719-deferred/ based on revid:jonas@stripped

 3946 jonas oreland	2011-04-05
      ndb - backport deferred constraints to 7.1.9 - commit to test

    modified:
      mysql-test/suite/ndb/r/ndb_basic.result
      sql/ha_ndbcluster.cc
      storage/ndb/include/kernel/GlobalSignalNumbers.h
      storage/ndb/include/kernel/signaldata/FireTrigOrd.hpp
      storage/ndb/include/kernel/signaldata/LqhKey.hpp
      storage/ndb/include/kernel/signaldata/PackedSignal.hpp
      storage/ndb/include/kernel/signaldata/TcContinueB.hpp
      storage/ndb/include/kernel/signaldata/TcKeyReq.hpp
      storage/ndb/include/kernel/signaldata/TupKey.hpp
      storage/ndb/include/kernel/trigger_definitions.h
      storage/ndb/include/ndbapi/NdbOperation.hpp
      storage/ndb/src/common/debugger/signaldata/LqhKey.cpp
      storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp
      storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp
      storage/ndb/src/kernel/blocks/ERROR_codes.txt
      storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
      storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp
      storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
      storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
      storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp
      storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
      storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
      storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp
      storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
      storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp
      storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
      storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp
      storage/ndb/src/ndbapi/NdbOperation.cpp
      storage/ndb/src/ndbapi/NdbOperationDefine.cpp
      storage/ndb/src/ndbapi/NdbOperationExec.cpp
      storage/ndb/test/include/HugoCalculator.hpp
      storage/ndb/test/ndbapi/testIndex.cpp
      storage/ndb/test/run-test/daily-basic-tests.txt
      storage/ndb/test/src/HugoCalculator.cpp
=== modified file 'mysql-test/suite/ndb/r/ndb_basic.result'
--- a/mysql-test/suite/ndb/r/ndb_basic.result	2010-11-01 09:13:06 +0000
+++ b/mysql-test/suite/ndb/r/ndb_basic.result	2011-04-05 19:12:29 +0000
@@ -28,6 +28,7 @@ ndb_batch_size	#
 ndb_cache_check_time	#
 ndb_cluster_connection_pool	#
 ndb_connectstring	#
+ndb_deferred_constraints	#
 ndb_distribution	#
 ndb_extra_logging	#
 ndb_force_send	#

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-10-22 14:25:43 +0000
+++ b/sql/ha_ndbcluster.cc	2011-04-05 19:12:29 +0000
@@ -221,6 +221,17 @@ static MYSQL_THDVAR_BOOL(
   FALSE                              /* default */
 );
 
+static MYSQL_THDVAR_UINT(
+  deferred_constraints,              /* name */
+  PLUGIN_VAR_RQCMDARG,
+  "Specified that constraints should be checked deferred (when supported)",
+  NULL,                              /* check func */
+  NULL,                              /* update func */
+  0,                                 /* default */
+  0,                                 /* min */
+  1,                                 /* max */
+  0                                  /* block */
+);
 
 /*
   Default value for max number of transactions createable against NDB from
@@ -3830,6 +3841,12 @@ int ha_ndbcluster::ndb_write_row(uchar *
     options.extraSetValues= sets;
     options.numExtraSetValues= num_sets;
   }
+  if (thd->slave_thread || THDVAR(thd, deferred_constraints))
+  {
+    options.optionsPresent |=
+      NdbOperation::OperationOptions::OO_DEFERRED_CONSTAINTS;
+  }
+
   if (options.optionsPresent != 0)
     poptions=&options;
 
@@ -4577,6 +4594,12 @@ int ha_ndbcluster::ndb_update_row(const 
   
   bool need_flush= add_row_check_if_batch_full(thd_ndb);
 
+  if (thd->slave_thread || THDVAR(thd, deferred_constraints))
+  {
+    options.optionsPresent |=
+      NdbOperation::OperationOptions::OO_DEFERRED_CONSTAINTS;
+  }
+
   if (cursor)
   {
     /*
@@ -4793,6 +4816,12 @@ int ha_ndbcluster::ndb_delete_row(const 
   uint delete_size= 12 + (m_bytes_per_write >> 2);
   bool need_flush= add_row_check_if_batch_full_size(thd_ndb, delete_size);
 
+  if (thd->slave_thread || THDVAR(thd, deferred_constraints))
+  {
+    options.optionsPresent |=
+      NdbOperation::OperationOptions::OO_DEFERRED_CONSTAINTS;
+  }
+
   if (cursor)
   {
     if (options.optionsPresent != 0)
@@ -14616,7 +14645,7 @@ static struct st_mysql_sys_var* system_v
   MYSQL_SYSVAR(connectstring),
   MYSQL_SYSVAR(mgmd_host),
   MYSQL_SYSVAR(nodeid),
-
+  MYSQL_SYSVAR(deferred_constraints),
   NULL
 };
 

=== modified file 'storage/ndb/include/kernel/GlobalSignalNumbers.h'
--- a/storage/ndb/include/kernel/GlobalSignalNumbers.h	2010-10-20 07:12:58 +0000
+++ b/storage/ndb/include/kernel/GlobalSignalNumbers.h	2011-04-05 19:12:29 +0000
@@ -338,9 +338,11 @@ extern const GlobalSignalNumber NO_OF_SI
 /* 233 unused */
 /* 234 unused */
 #define GSN_DISCONNECT_REP              235
-/* 236 unused */
-/* 237 unused */
-/* 238 unused */
+
+#define GSN_FIRE_TRIG_REQ               236
+#define GSN_FIRE_TRIG_REF               237
+#define GSN_FIRE_TRIG_CONF              238
+
 #define GSN_DIVERIFYCONF                239
 #define GSN_DIVERIFYREF                 240
 #define GSN_DIVERIFYREQ                 241

=== modified file 'storage/ndb/include/kernel/signaldata/FireTrigOrd.hpp'
--- a/storage/ndb/include/kernel/signaldata/FireTrigOrd.hpp	2009-05-27 15:21:45 +0000
+++ b/storage/ndb/include/kernel/signaldata/FireTrigOrd.hpp	2011-04-05 19:12:29 +0000
@@ -225,5 +225,47 @@ void FireTrigOrd::setAnyValue(Uint32 any
   m_any_value = any_value;
 }
 
+struct FireTrigReq
+{
+  STATIC_CONST( SignalLength = 4 );
+
+  Uint32 tcOpRec;
+  Uint32 transId[2];
+  Uint32 pass;
+};
+
+struct FireTrigRef
+{
+  STATIC_CONST( SignalLength = 4 );
+
+  Uint32 tcOpRec;
+  Uint32 transId[2];
+  Uint32 errCode;
+
+  enum ErrorCode
+  {
+    FTR_UnknownOperation = 1235
+    ,FTR_IncorrectState = 1236
+  };
+};
+
+struct FireTrigConf
+{
+  STATIC_CONST( SignalLength = 4 );
+
+  Uint32 tcOpRec;
+  Uint32 transId[2];
+  Uint32 noFiredTriggers; // bit 31 defered trigger
+
+  static Uint32 getFiredCount(Uint32 v) {
+    return NoOfFiredTriggers::getFiredCount(v);
+  }
+  static Uint32 getDeferredBit(Uint32 v) {
+    return NoOfFiredTriggers::getDeferredBit(v);
+  }
+  static void setDeferredBit(Uint32 & v) {
+    NoOfFiredTriggers::setDeferredBit(v);
+  }
+};
 
 #endif

=== modified file 'storage/ndb/include/kernel/signaldata/LqhKey.hpp'
--- a/storage/ndb/include/kernel/signaldata/LqhKey.hpp	2010-11-01 16:11:10 +0000
+++ b/storage/ndb/include/kernel/signaldata/LqhKey.hpp	2011-04-05 19:12:29 +0000
@@ -20,6 +20,7 @@
 #define LQH_KEY_H
 
 #include "SignalData.hpp"
+#include <trigger_definitions.h>
 
 class LqhKeyReq {
   /**
@@ -151,6 +152,9 @@ private:
 
   static UintR getNrCopyFlag(const UintR & requestInfo);
   static void setNrCopyFlag(UintR & requestInfo, UintR val);
+
+  static UintR getDeferredConstraints(const UintR & requestInfo);
+  static void setDeferredConstraints(UintR & requestInfo, UintR val);
 };
 
 /**
@@ -214,6 +218,7 @@ private:
 #define RI_ROWID_SHIFT       (31)
 #define RI_GCI_SHIFT         (12)
 #define RI_NR_COPY_SHIFT     (13)
+#define RI_DEFERRED_CONSTAINTS (26)
 
 /**
  * Scan Info
@@ -578,6 +583,19 @@ LqhKeyReq::getNrCopyFlag(const UintR & r
 }
 
 inline
+void
+LqhKeyReq::setDeferredConstraints(UintR & requestInfo, UintR val){
+  ASSERT_BOOL(val, "LqhKeyReq::setDeferredConstraints");
+  requestInfo |= (val << RI_DEFERRED_CONSTAINTS);
+}
+
+inline
+UintR
+LqhKeyReq::getDeferredConstraints(const UintR & requestInfo){
+  return (requestInfo >> RI_DEFERRED_CONSTAINTS) & 1;
+}
+
+inline
 Uint32
 table_version_major_lqhkeyreq(Uint32 x)
 {
@@ -626,7 +644,17 @@ private:
   };
   Uint32 transId1;
   Uint32 transId2;
-  Uint32 noFiredTriggers;
+  Uint32 noFiredTriggers; // bit 31 defered trigger
+
+  static Uint32 getFiredCount(Uint32 v) {
+    return NoOfFiredTriggers::getFiredCount(v);
+  }
+  static Uint32 getDeferredBit(Uint32 v) {
+    return NoOfFiredTriggers::getDeferredBit(v);
+  }
+  static void setDeferredBit(Uint32 & v) {
+    NoOfFiredTriggers::setDeferredBit(v);
+  }
 };
 
 class LqhKeyRef {

=== modified file 'storage/ndb/include/kernel/signaldata/PackedSignal.hpp'
--- a/storage/ndb/include/kernel/signaldata/PackedSignal.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/include/kernel/signaldata/PackedSignal.hpp	2011-04-05 19:12:29 +0000
@@ -28,6 +28,8 @@
 #define ZCOMPLETED 3
 #define ZLQHKEYCONF 4
 #define ZREMOVE_MARKER 5
+#define ZFIRE_TRIG_REQ 6
+#define ZFIRE_TRIG_CONF 7
 
 class PackedSignal {
 

=== modified file 'storage/ndb/include/kernel/signaldata/TcContinueB.hpp'
--- a/storage/ndb/include/kernel/signaldata/TcContinueB.hpp	2009-05-27 15:21:45 +0000
+++ b/storage/ndb/include/kernel/signaldata/TcContinueB.hpp	2011-04-05 19:12:29 +0000
@@ -46,7 +46,8 @@ private:
     TRIGGER_PENDING                        = 17,
     
     DelayTCKEYCONF = 18,
-    ZNF_CHECK_TRANSACTIONS = 19
+    ZNF_CHECK_TRANSACTIONS = 19,
+    ZSEND_FIRE_TRIG_REQ = 20
   };
 };
 

=== modified file 'storage/ndb/include/kernel/signaldata/TcKeyReq.hpp'
--- a/storage/ndb/include/kernel/signaldata/TcKeyReq.hpp	2010-01-28 15:16:46 +0000
+++ b/storage/ndb/include/kernel/signaldata/TcKeyReq.hpp	2011-04-05 19:12:29 +0000
@@ -200,6 +200,13 @@ private:
 
   static void setReorgFlag(UintR & requestInfo, UintR val);
   static UintR getReorgFlag(const UintR & requestInfo);
+
+  /**
+   * Check constraints deferred
+   */
+  static UintR getDeferredConstraints(const UintR & requestInfo);
+  static void setDeferredConstraints(UintR & requestInfo, UintR val);
+
   /**
    * Set:ers for scanInfo
    */
@@ -259,6 +266,8 @@ private:
 
 #define TC_REORG_SHIFT     (19)
 
+#define TC_DEFERRED_CONSTAINTS_SHIFT (17)
+
 /**
  * Scan Info
  *
@@ -574,4 +583,18 @@ TcKeyReq::setReorgFlag(UintR & requestIn
   requestInfo |= (flag << TC_REORG_SHIFT);
 }
 
+inline
+void
+TcKeyReq::setDeferredConstraints(UintR & requestInfo, UintR val){
+  ASSERT_BOOL(val, "TcKeyReq::setDeferredConstraints");
+  requestInfo |= (val << TC_DEFERRED_CONSTAINTS_SHIFT);
+}
+
+inline
+UintR
+TcKeyReq::getDeferredConstraints(const UintR & requestInfo){
+  return (requestInfo >> TC_DEFERRED_CONSTAINTS_SHIFT) & 1;
+}
+
+
 #endif

=== modified file 'storage/ndb/include/kernel/signaldata/TupKey.hpp'
--- a/storage/ndb/include/kernel/signaldata/TupKey.hpp	2009-05-27 15:21:45 +0000
+++ b/storage/ndb/include/kernel/signaldata/TupKey.hpp	2011-04-05 19:12:29 +0000
@@ -38,7 +38,7 @@ class TupKeyReq {
   friend bool printTUPKEYREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo);
 
 public:
-  STATIC_CONST( SignalLength = 19 );
+  STATIC_CONST( SignalLength = 20 );
 
 private:
 
@@ -64,6 +64,7 @@ private:
   Uint32 m_row_id_page_no;
   Uint32 m_row_id_page_idx;
   Uint32 attrInfoIVal;
+  Uint32 deferred_constraints;
 };
 
 class TupKeyConf {

=== modified file 'storage/ndb/include/kernel/trigger_definitions.h'
--- a/storage/ndb/include/kernel/trigger_definitions.h	2009-05-27 15:21:45 +0000
+++ b/storage/ndb/include/kernel/trigger_definitions.h	2011-04-05 19:12:29 +0000
@@ -196,4 +196,19 @@ struct TriggerInfo {
   }
 };
 
+struct NoOfFiredTriggers
+{
+  STATIC_CONST( DeferredBit = (Uint32(1) << 31) );
+
+  static Uint32 getFiredCount(Uint32 v) {
+    return v & ~(Uint32(DeferredBit));
+  }
+  static Uint32 getDeferredBit(Uint32 v) {
+    return (v & Uint32(DeferredBit)) != 0;
+  }
+  static void setDeferredBit(Uint32 & v) {
+    v |= Uint32(DeferredBit);
+  }
+};
+
 #endif

=== modified file 'storage/ndb/include/ndbapi/NdbOperation.hpp'
--- a/storage/ndb/include/ndbapi/NdbOperation.hpp	2010-10-13 09:33:02 +0000
+++ b/storage/ndb/include/ndbapi/NdbOperation.hpp	2011-04-05 19:12:29 +0000
@@ -1039,7 +1039,9 @@ public:
                  OO_INTERPRETED  = 0x10,
                  OO_ANYVALUE     = 0x20,
                  OO_CUSTOMDATA   = 0x40,
-                 OO_LOCKHANDLE   = 0x80 };
+                 OO_LOCKHANDLE   = 0x80,
+                 OO_DEFERRED_CONSTAINTS = 0x400
+    };
 
     /* An operation-specific abort option.
      * Only necessary if the default abortoption behaviour
@@ -1511,6 +1513,7 @@ protected:
                               * to LM_Read?
                               */
 
+  bool m_deferred_constraints;
 private:
   NdbOperation(const NdbOperation&); // Not impl.
   NdbOperation&operator=(const NdbOperation&);

=== modified file 'storage/ndb/src/common/debugger/signaldata/LqhKey.cpp'
--- a/storage/ndb/src/common/debugger/signaldata/LqhKey.cpp	2009-05-27 15:21:45 +0000
+++ b/storage/ndb/src/common/debugger/signaldata/LqhKey.cpp	2011-04-05 19:12:29 +0000
@@ -59,6 +59,8 @@ printLQHKEYREQ(FILE * output, const Uint
     fprintf(output, "NrCopy ");
   if(LqhKeyReq::getGCIFlag(reqInfo))
     fprintf(output, "GCI ");
+  if(LqhKeyReq::getDeferredConstraints(reqInfo))
+    fprintf(output, "Deferred-constraints ");
   
   fprintf(output, "ScanInfo/noFiredTriggers: H\'%x\n", sig->scanInfo);
   

=== modified file 'storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp'
--- a/storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp	2011-04-05 19:12:29 +0000
@@ -97,6 +97,24 @@ printPACKED_SIGNAL(FILE * output, const 
       fprintf(output,"\n");
       break;
     }
+    case ZFIRE_TRIG_REQ: {
+      Uint32 signalLength = 3;
+
+      fprintf(output, "--------------- Signal ----------------\n");
+      fprintf(output, "r.bn: %u \"%s\", length: %u \"FIRE_TRIG_REQ\"\n",
+	      receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+      i += signalLength;
+      break;
+    }
+    case ZFIRE_TRIG_CONF: {
+      Uint32 signalLength = 4;
+
+      fprintf(output, "--------------- Signal ----------------\n");
+      fprintf(output, "r.bn: %u \"%s\", length: %u \"FIRE_TRIG_CONF\"\n",
+	      receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+      i += signalLength;
+      break;
+    }
     default:
       fprintf(output, "Unknown signal type\n");
       i = len; // terminate printing

=== modified file 'storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp'
--- a/storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp	2010-01-28 15:16:46 +0000
+++ b/storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp	2011-04-05 19:12:29 +0000
@@ -74,6 +74,8 @@ printTCKEYREQ(FILE * output, const Uint3
     if(sig->getDistributionKeyFlag(sig->requestInfo)){
       fprintf(output, " d-key");
     }
+    if(sig->getDeferredConstraints(sig->requestInfo))
+      fprintf(output, "Deferred-constraints ");
     fprintf(output, "\n");
   }
   

=== modified file 'storage/ndb/src/kernel/blocks/ERROR_codes.txt'
--- a/storage/ndb/src/kernel/blocks/ERROR_codes.txt	2010-10-28 12:59:31 +0000
+++ b/storage/ndb/src/kernel/blocks/ERROR_codes.txt	2011-04-05 19:12:29 +0000
@@ -3,10 +3,10 @@ Next NDBCNTR 1002
 Next NDBFS 2000
 Next DBACC 3002
 Next DBTUP 4035
-Next DBLQH 5061
+Next DBLQH 5072
 Next DBDICT 6026
 Next DBDIH 7229
-Next DBTC 8088
+Next DBTC 8092
 Next CMVMI 9000
 Next BACKUP 10042
 Next DBUTIL 11002

=== modified file 'storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp'
--- a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp	2010-10-28 12:59:31 +0000
+++ b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp	2011-04-05 19:12:29 +0000
@@ -2044,10 +2044,12 @@ public:
     Uint8 m_disk_table;
     Uint8 m_use_rowid;
     Uint8 m_dealloc;
+    Uint8 m_fire_trig_pass;
     enum op_flags {
       OP_ISLONGREQ              = 0x1,
       OP_SAVEATTRINFO           = 0x2,
-      OP_SCANKEYINFOPOSSAVED    = 0x4
+      OP_SCANKEYINFOPOSSAVED    = 0x4,
+      OP_DEFERRED_CONSTRAINTS   = 0x8
     };
     Uint32 m_flags;
     Uint32 m_log_part_ptr_i;
@@ -2238,6 +2240,8 @@ private:
   void execBUILD_INDX_IMPL_REF(Signal* signal);
   void execBUILD_INDX_IMPL_CONF(Signal* signal);
 
+  void execFIRE_TRIG_REQ(Signal*);
+
   // Statement blocks
 
   void init_acc_ptr_list(ScanRecord*);
@@ -3187,6 +3191,9 @@ public:
   void suspendFile(Signal* signal, Ptr<LogFileRecord> logFile, Uint32 millis);
 
   void send_runredo_event(Signal*, LogPartRecord *, Uint32 currgci);
+
+  void sendFireTrigConfTc(Signal* signal, BlockReference ref, Uint32 Tdata[]);
+  bool check_fire_trig_pass(Uint32 op, Uint32 pass);
 };
 
 inline

=== modified file 'storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp'
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp	2010-03-10 07:43:06 +0000
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp	2011-04-05 19:12:29 +0000
@@ -419,6 +419,8 @@ Dblqh::Dblqh(Block_context& ctx, Uint32 
                &Dblqh::execFSWRITEREQ);
   addRecSignal(GSN_DBINFO_SCANREQ, &Dblqh::execDBINFO_SCANREQ);
 
+  addRecSignal(GSN_FIRE_TRIG_REQ, &Dblqh::execFIRE_TRIG_REQ);
+
   initData();
 
 #ifdef VM_TRACE

=== modified file 'storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp	2010-11-01 16:11:10 +0000
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp	2011-04-05 19:12:29 +0000
@@ -74,6 +74,7 @@
 #include <signaldata/SignalDroppedRep.hpp>
 #include <signaldata/FsReadWriteReq.hpp>
 #include <signaldata/DbinfoScan.hpp>
+#include <signaldata/FireTrigOrd.hpp>
 #include <NdbEnv.h>
 
 #include "../suma/Suma.hpp"
@@ -3094,6 +3095,7 @@ void Dblqh::execPACKED_SIGNAL(Signal* si
 
   jamEntry();
   Tlength = signal->length();
+  Uint32 TsenderRef = signal->getSendersBlockRef();
   Uint32 TcommitLen = 5;
   Uint32 Tgci_lo_mask = ~(Uint32)0;
 
@@ -3138,8 +3140,7 @@ void Dblqh::execPACKED_SIGNAL(Signal* si
       break;
     case ZLQHKEYCONF: {
       jam();
-      LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
-
+      LqhKeyConf * lqhKeyConf = CAST_PTR(LqhKeyConf, signal->theData);
       sig0 = TpackedData[Tstep + 0] & 0x0FFFFFFF;
       sig1 = TpackedData[Tstep + 1];
       sig2 = TpackedData[Tstep + 2];
@@ -3168,6 +3169,22 @@ void Dblqh::execPACKED_SIGNAL(Signal* si
       execREMOVE_MARKER_ORD(signal);
       Tstep += 3;
       break;
+    case ZFIRE_TRIG_REQ:
+      jam();
+      ndbassert(FireTrigReq::SignalLength == 4);
+      sig0 = TpackedData[Tstep + 0] & 0x0FFFFFFF;
+      sig1 = TpackedData[Tstep + 1];
+      sig2 = TpackedData[Tstep + 2];
+      sig3 = TpackedData[Tstep + 3];
+      signal->theData[0] = sig0;
+      signal->theData[1] = sig1;
+      signal->theData[2] = sig2;
+      signal->theData[3] = sig3;
+      signal->header.theLength = FireTrigReq::SignalLength;
+      signal->header.theSendersBlockRef = TsenderRef;
+      execFIRE_TRIG_REQ(signal);
+      Tstep += FireTrigReq::SignalLength;
+      break;
     default:
       ndbrequire(false);
       return;
@@ -4411,6 +4428,13 @@ void Dblqh::execLQHKEYREQ(Signal* signal
     nextPos++;
   }//if
 
+  Uint32 Tdeferred = LqhKeyReq::getDeferredConstraints(Treqinfo);
+  if (isLongReq && Tdeferred)
+  {
+    regTcPtr->m_flags |= TcConnectionrec::OP_DEFERRED_CONSTRAINTS;
+    regTcPtr->m_fire_trig_pass = 0;
+  }
+
   UintR TitcKeyLen = 0;
   Uint32 keyLenWithLQHReq = 0;
   UintR TreclenAiLqhkey   = 0;
@@ -5751,6 +5775,7 @@ Dblqh::acckeyconf_tupkeyreq(Signal* sign
   Uint32 page_idx = local_key & MAX_TUPLES_PER_PAGE;
   Uint32 page_no = local_key >> MAX_TUPLES_BITS;
   Uint32 Ttupreq = regTcPtr->dirtyOp;
+  Uint32 flags = regTcPtr->m_flags;
   Ttupreq = Ttupreq + (regTcPtr->opSimple << 1);
   Ttupreq = Ttupreq + (op << 6);
   Ttupreq = Ttupreq + (regTcPtr->opExec << 10);
@@ -5813,6 +5838,8 @@ Dblqh::acckeyconf_tupkeyreq(Signal* sign
   regTcPtr->m_row_id.m_page_idx = page_idx;
   
   tupKeyReq->attrInfoIVal= RNIL;
+  tupKeyReq->deferred_constraints =
+    (flags & TcConnectionrec::OP_DEFERRED_CONSTRAINTS) != 0;
 
   /* Pass AttrInfo section if available in the TupKeyReq signal
    * We are still responsible for releasing it, TUP is just
@@ -7116,6 +7143,151 @@ void Dblqh::errorReport(Signal* signal, 
   return;
 }//Dblqh::errorReport()
 
+void
+Dblqh::execFIRE_TRIG_REQ(Signal* signal)
+{
+  Uint32 tcOprec = signal->theData[0];
+  Uint32 transid1 = signal->theData[1];
+  Uint32 transid2 = signal->theData[2];
+  Uint32 pass = signal->theData[3];
+  Uint32 senderRef = signal->getSendersBlockRef();
+
+  jamEntry();
+
+  if (ERROR_INSERTED_CLEAR(5064))
+  {
+    // throw away...should cause timeout in TC
+    return;
+  }
+
+  CRASH_INSERTION(5072);
+
+  Uint32 err;
+  if (findTransaction(transid1, transid2, tcOprec, 0) == ZOK &&
+      !ERROR_INSERTED_CLEAR(5065) &&
+      !ERROR_INSERTED(5070) &&
+      !ERROR_INSERTED(5071))
+  {
+    TcConnectionrec * const regTcPtr = tcConnectptr.p;
+
+    if (unlikely(regTcPtr->transactionState != TcConnectionrec::PREPARED ||
+                 ERROR_INSERTED_CLEAR(5067)))
+    {
+      err = FireTrigRef::FTR_IncorrectState;
+      goto do_err;
+    }
+
+    /**
+     *
+     */
+    signal->theData[0] = regTcPtr->tupConnectrec;
+    signal->theData[1] = regTcPtr->tcBlockref;
+    signal->theData[2] = regTcPtr->tcOprec;
+    signal->theData[3] = transid1;
+    signal->theData[4] = transid2;
+    signal->theData[5] = pass;
+    Uint32 tup = refToMain(regTcPtr->tcTupBlockref);
+    EXECUTE_DIRECT(tup, GSN_FIRE_TRIG_REQ, signal, 6);
+
+    err = signal->theData[0];
+    Uint32 cnt = signal->theData[1];
+
+    if (ERROR_INSERTED_CLEAR(5066))
+    {
+      err = 5066;
+    }
+
+    if (ERROR_INSERTED_CLEAR(5068))
+      tcOprec++;
+    if (ERROR_INSERTED_CLEAR(5069))
+      transid1++;
+
+    if (err == 0)
+    {
+      jam();
+      Uint32 Tdata[FireTrigConf::SignalLength];
+      FireTrigConf * conf = CAST_PTR(FireTrigConf, Tdata);
+      conf->tcOpRec = tcOprec;
+      conf->transId[0] = transid1;
+      conf->transId[1] = transid2;
+      conf->noFiredTriggers = cnt;
+      sendFireTrigConfTc(signal, regTcPtr->tcBlockref, Tdata);
+      return;
+    }
+  }
+  else
+  {
+    jam();
+    err = FireTrigRef::FTR_UnknownOperation;
+  }
+
+do_err:
+  if (ERROR_INSERTED_CLEAR(5070))
+    tcOprec++;
+
+  if (ERROR_INSERTED_CLEAR(5071))
+    transid1++;
+
+  FireTrigRef * ref = CAST_PTR(FireTrigRef, signal->getDataPtrSend());
+  ref->tcOpRec = tcOprec;
+  ref->transId[0] = transid1;
+  ref->transId[1] = transid2;
+  ref->errCode = err;
+  sendSignal(senderRef, GSN_FIRE_TRIG_REF,
+             signal, FireTrigRef::SignalLength, JBB);
+
+  return;
+}
+
+void
+Dblqh::sendFireTrigConfTc(Signal* signal,
+                          BlockReference atcBlockref,
+                          Uint32 Tdata[])
+{
+  HostRecordPtr Thostptr;
+  Uint32 len = FireTrigConf::SignalLength;
+
+  Thostptr.i = refToNode(atcBlockref);
+  ptrCheckGuard(Thostptr, chostFileSize, hostRecord);
+
+  if (Thostptr.p->noOfPackedWordsTc > (25 - len))
+  {
+    jam();
+    sendPackedSignalTc(signal, Thostptr.p);
+  }
+  else
+  {
+    jam();
+    updatePackedList(signal, Thostptr.p, Thostptr.i);
+  }
+
+  ndbassert(FireTrigConf::SignalLength == 4);
+  Uint32 * dst = &Thostptr.p->packedWordsTc[Thostptr.p->noOfPackedWordsTc];
+  Thostptr.p->noOfPackedWordsTc += len;
+  dst[0] = Tdata[0] | (ZFIRE_TRIG_CONF << 28);
+  dst[1] = Tdata[1];
+  dst[2] = Tdata[2];
+  dst[3] = Tdata[3];
+}
+
+bool
+Dblqh::check_fire_trig_pass(Uint32 opId, Uint32 pass)
+{
+  /**
+   * Check that trigger only fires once per pass
+   *   (per primary key)
+   */
+  TcConnectionrecPtr regTcPtr;
+  regTcPtr.i= opId;
+  ptrCheckGuard(regTcPtr, ctcConnectrecFileSize, tcConnectionrec);
+  if (regTcPtr.p->m_fire_trig_pass <= pass)
+  {
+    regTcPtr.p->m_fire_trig_pass = pass + 1;
+    return true;
+  }
+  return false;
+}
+
 /* ************************************************************************>>
  *  COMMIT: Start commit request from TC. This signal is originally sent as a
  *  packed signal and this function is called from execPACKED_SIGNAL.

=== modified file 'storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp	2010-03-26 07:13:06 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp	2011-04-05 19:12:29 +0000
@@ -171,7 +171,17 @@ public:
     CS_FAIL_COMMITTING = 22,
     CS_FAIL_COMMITTED = 23,
     CS_FAIL_COMPLETED = 24,
-    CS_START_SCAN = 25
+    CS_START_SCAN = 25,
+
+    /**
+     * Sending FIRE_TRIG_REQ
+     */
+    CS_SEND_FIRE_TRIG_REQ = 26,
+
+    /**
+     * Waiting for FIRE_TRIG_CONF/REF (or operations generated by this)
+     */
+    CS_WAIT_FIRE_TRIG_REQ = 27
   };
 
   enum OperationState {
@@ -192,7 +202,9 @@ public:
     OS_WAIT_COMMIT_CONF = 15,
     OS_WAIT_ABORT_CONF = 16,
     OS_WAIT_COMPLETE_CONF = 17,
-    OS_WAIT_SCAN = 18
+    OS_WAIT_SCAN = 18,
+
+    OS_FIRE_TRIG_REQ = 19,
   };
 
   enum AbortState {
@@ -238,8 +250,7 @@ public:
     IOS_NOOP = 0,
     IOS_INDEX_ACCESS = 1,
     IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF = 2,
-    IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI = 3,
-    IOS_INDEX_OPERATION = 4
+    IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI = 3
   };
   
   enum IndexState {
@@ -695,7 +706,10 @@ public:
     //---------------------------------------------------
     UintR lastTcConnect;
     UintR lqhkeyreqrec;
-    Uint32 buddyPtr;
+    union {
+      Uint32 buddyPtr;
+      Int32 pendingTriggers; // For deferred triggers
+    };
     union {
       UintR apiScanRec;
       UintR commitAckMarker;
@@ -705,15 +719,22 @@ public:
     ReturnSignal returnsignal;
     AbortState abortState;
 
-    Uint8 indexOpReturn;
-    Uint8 triggerPending; // Used to mark waiting for a CONTINUEB
+    enum TransactionFlags
+    {
+      TF_INDEX_OP_RETURN = 1,
+      TF_TRIGGER_PENDING = 2, // Used to mark waiting for a CONTINUEB
+      TF_EXEC_FLAG       = 4,
+      TF_COMMIT_ACK_MARKER_RECEIVED = 8,
+      TF_DEFERRED_CONSTRAINTS = 16, // check constraints in deferred fashion
+      TF_DEFERRED_TRIGGERS = 32, // trans has deferred triggers
+      TF_END = 0
+    };
+    Uint32 m_flags;
 
-    Uint8 m_exec_flag;
     Uint8 m_special_op_flags; // Used to mark on-going TcKeyReq as indx table
 
     Uint8 takeOverRec;
     Uint8 currentReplicaNo;
-    Uint8 m_commit_ack_marker_received;
 
     Uint8 tckeyrec; // Changed from R
 
@@ -776,6 +797,12 @@ public:
 #ifdef ERROR_INSERT
     Uint32 continueBCount;  // ERROR_INSERT 8082
 #endif
+    Uint8 m_pre_commit_pass;
+
+    bool isExecutingDeferredTriggers() const {
+      return apiConnectstate == CS_SEND_FIRE_TRIG_REQ ||
+        apiConnectstate == CS_WAIT_FIRE_TRIG_REQ ;
+    }
   };
   
   typedef Ptr<ApiConnectRecord> ApiConnectRecordPtr;
@@ -840,7 +867,8 @@ public:
       SOF_REORG_MOVING = 8,           // A record that should be moved
       SOF_TRIGGER = 16,               // A trigger
       SOF_REORG_COPY = 32,
-      SOF_REORG_DELETE = 64
+      SOF_REORG_DELETE = 64,
+      SOF_DEFERRED_TRIGGER = 128      // Op has deferred trigger
     };
     
     static inline bool isIndexOp(Uint8 flags) {
@@ -863,12 +891,12 @@ public:
     Uint16 lqhInstanceKey;
     
     // Trigger data
-    FiredTriggerPtr accumulatingTriggerData;
-    UintR noFiredTriggers;
-    UintR noReceivedTriggers;
-    UintR triggerExecutionCount;
-    UintR triggeringOperation;
+    UintR noFiredTriggers;      // As reported by lqhKeyConf
+    UintR noReceivedTriggers;   // FIRE_TRIG_ORD
+    UintR triggerExecutionCount;// No of outstanding op due to triggers
     UintR savedState[LqhKeyConf::SignalLength];
+
+    UintR triggeringOperation;  // Which operation was "cause" of this op
     
     // Index data
     UintR indexOp;
@@ -956,8 +984,6 @@ public:
     UintR packedWordsLqh[26];
     UintR noOfWordsTCKEYCONF;
     UintR packedWordsTCKEYCONF[30];
-    UintR noOfWordsTCINDXCONF;
-    UintR packedWordsTCINDXCONF[30];
     BlockReference hostLqhBlockRef;
 
     enum NodeFailBits
@@ -1361,6 +1387,8 @@ private:
   void execALTER_INDX_IMPL_REQ(Signal* signal);
   void execSIGNAL_DROPPED_REP(Signal* signal);
 
+  void execFIRE_TRIG_REF(Signal*);
+  void execFIRE_TRIG_CONF(Signal*);
 
   // Index table lookup
   void execTCKEYCONF(Signal* signal);
@@ -1395,14 +1423,15 @@ private:
   void sendPackedTCKEYCONF(Signal* signal,
                            HostRecord * ahostptr,
                            UintR hostId);
-  void sendPackedTCINDXCONF(Signal* signal,
-                            HostRecord * ahostptr,
-                            UintR hostId);
   void sendPackedSignalLqh(Signal* signal, HostRecord * ahostptr);
   Uint32 sendCommitLqh(Signal* signal,
                        TcConnectRecord * const regTcPtr);
   Uint32 sendCompleteLqh(Signal* signal,
                          TcConnectRecord * const regTcPtr);
+
+  void sendFireTrigReq(Signal*, Ptr<ApiConnectRecord>, Uint32 firstTcConnect);
+  Uint32 sendFireTrigReqLqh(Signal*, Ptr<TcConnectRecord>, Uint32 pass);
+
   void sendTCKEY_FAILREF(Signal* signal, ApiConnectRecord *);
   void sendTCKEY_FAILCONF(Signal* signal, ApiConnectRecord *);
   void routeTCKEY_FAILREFCONF(Signal* signal, const ApiConnectRecord *, 
@@ -1495,7 +1524,6 @@ private:
                      BlockReference TBRef);
   void sendSystemError(Signal* signal, int line);
   void sendtckeyconf(Signal* signal, UintR TcommitFlag);
-  void sendTcIndxConf(Signal* signal, UintR TcommitFlag);
   void unlinkApiConnect(Ptr<GcpRecord>, Ptr<ApiConnectRecord>);
   void unlinkGcp(Ptr<GcpRecord>);
   void unlinkReadyTcCon(Signal* signal);
@@ -1540,11 +1568,10 @@ private:
 			     TcConnectRecord* trigOp);
   void restoreTriggeringOpState(Signal* signal, 
 				TcConnectRecord* trigOp);
-  void continueTriggeringOp(Signal* signal, 
-			    TcConnectRecord* trigOp);
+  void trigger_op_finished(Signal* signal, ApiConnectRecordPtr,
+                           TcConnectRecord* triggeringOp);
+  void continueTriggeringOp(Signal* signal, TcConnectRecord* trigOp);
 
-  void scheduleFiredTrigger(ApiConnectRecordPtr* transPtr, 
-                            TcConnectRecordPtr* opPtr);
   void executeTriggers(Signal* signal, ApiConnectRecordPtr* transPtr);
   void executeTrigger(Signal* signal,
                       TcFiredTriggerData* firedTriggerData,
@@ -1564,14 +1591,12 @@ private:
                             TcFiredTriggerData* firedTriggerData, 
                             ApiConnectRecordPtr* transPtr,
                             TcConnectRecordPtr* opPtr,
-                            TcIndexData* indexData,
-                            bool holdOperation = false);
+                            TcIndexData* indexData);
   void deleteFromIndexTable(Signal* signal, 
                             TcFiredTriggerData* firedTriggerData, 
                             ApiConnectRecordPtr* transPtr,
                             TcConnectRecordPtr* opPtr,
-                            TcIndexData* indexData,
-                            bool holdOperation = false);
+                            TcIndexData* indexData);
 
   void executeReorgTrigger(Signal* signal,
                            TcDefinedTriggerData* definedTriggerData,

=== modified file 'storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp	2010-02-22 14:05:33 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp	2011-04-05 19:12:29 +0000
@@ -297,7 +297,10 @@ Dbtc::Dbtc(Block_context& ctx):
   addRecSignal(GSN_ALTER_TAB_REQ, &Dbtc::execALTER_TAB_REQ);
   addRecSignal(GSN_ROUTE_ORD, &Dbtc::execROUTE_ORD);
   addRecSignal(GSN_TCKEY_FAILREFCONF_R, &Dbtc::execTCKEY_FAILREFCONF_R);
-  
+
+  addRecSignal(GSN_FIRE_TRIG_REF, &Dbtc::execFIRE_TRIG_REF);
+  addRecSignal(GSN_FIRE_TRIG_CONF, &Dbtc::execFIRE_TRIG_CONF);
+
   cacheRecord = 0;
   apiConnectRecord = 0;
   tcConnectRecord = 0;

=== modified file 'storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp	2010-10-28 07:11:34 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp	2011-04-05 19:12:29 +0000
@@ -186,6 +186,22 @@ Dbtc::updateBuddyTimer(ApiConnectRecordP
   }//if
 }
 
+static
+inline
+bool
+tc_testbit(Uint32 flags, Uint32 flag)
+{
+  return (flags & flag) != 0;
+}
+
+static
+inline
+void
+tc_clearbit(Uint32 & flags, Uint32 flag)
+{
+  flags &= ~(Uint32)flag;
+}
+
 void Dbtc::execCONTINUEB(Signal* signal) 
 {
   UintR tcase;
@@ -314,8 +330,9 @@ void Dbtc::execCONTINUEB(Signal* signal)
     if (likely((transPtr.p->transid[0] == Tdata1) &&
                (transPtr.p->transid[1] == Tdata2)))
     {
-      ndbrequire(transPtr.p->triggerPending);
-      transPtr.p->triggerPending = false;
+      ndbrequire(tc_testbit(transPtr.p->m_flags,
+                            ApiConnectRecord::TF_TRIGGER_PENDING));
+      tc_clearbit(transPtr.p->m_flags, ApiConnectRecord::TF_TRIGGER_PENDING);
       /* Try executing triggers now */
       executeTriggers(signal, &transPtr);
     }
@@ -326,6 +343,19 @@ void Dbtc::execCONTINUEB(Signal* signal)
     ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
     sendtckeyconf(signal, Tdata1);
     return;
+  case TcContinueB::ZSEND_FIRE_TRIG_REQ:
+    jam();
+    apiConnectptr.i = Tdata0;
+    ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+    if (unlikely(! (apiConnectptr.p->transid[0] == Tdata1 &&
+                    apiConnectptr.p->transid[1] == Tdata2 &&
+                    apiConnectptr.p->apiConnectstate == CS_SEND_FIRE_TRIG_REQ)))
+    {
+      warningReport(signal, 29);
+      return;
+    }
+    sendFireTrigReq(signal, apiConnectptr, signal->theData[4]);
+    return;
   default:
     ndbrequire(false);
   }//switch
@@ -1764,7 +1794,7 @@ start_failure:
   {
     jam();
     initApiConnectRec(signal, apiConnectptr.p, true);
-    apiConnectptr.p->m_exec_flag = 1;
+    apiConnectptr.p->m_flags |= ApiConnectRecord::TF_EXEC_FLAG;
     goto start_failure;
   }
   case 61:
@@ -1899,7 +1929,6 @@ void Dbtc::execKEYINFO(Signal* signal) 
   }//switch
 }//Dbtc::execKEYINFO()
 
-
 /**
  * sendKeyInfoTrain
  * Method to send a KeyInfo signal train from KeyInfo in the supplied
@@ -2299,7 +2328,7 @@ void Dbtc::initApiConnectRec(Signal* sig
   UintR Ttransid0 = tcKeyReq->transId1;
   UintR Ttransid1 = tcKeyReq->transId2;
 
-  regApiPtr->m_exec_flag = 0;
+  tc_clearbit(regApiPtr->m_flags, ApiConnectRecord::TF_EXEC_FLAG);
   regApiPtr->returncode = 0;
   regApiPtr->returnsignal = RS_TCKEYCONF;
   ndbassert(regApiPtr->firstTcConnect == RNIL);
@@ -2310,7 +2339,8 @@ void Dbtc::initApiConnectRec(Signal* sig
   regApiPtr->lqhkeyreqrec = 0;
   regApiPtr->tckeyrec = 0;
   regApiPtr->tcindxrec = 0;
-  regApiPtr->m_commit_ack_marker_received = 0;
+  tc_clearbit(regApiPtr->m_flags,
+              ApiConnectRecord::TF_COMMIT_ACK_MARKER_RECEIVED);
   regApiPtr->no_commit_ack_markers = 0;
   regApiPtr->failureNr = TfailureNr;
   regApiPtr->transid[0] = Ttransid0;
@@ -2320,15 +2350,19 @@ void Dbtc::initApiConnectRec(Signal* sig
   regApiPtr->currSavePointId = 0;
   regApiPtr->m_transaction_nodes.clear();
   regApiPtr->singleUserMode = 0;
+  regApiPtr->m_pre_commit_pass = 0;
   // Trigger data
   releaseFiredTriggerData(&regApiPtr->theFiredTriggers),
   // Index data
-  regApiPtr->indexOpReturn = false;
+  tc_clearbit(regApiPtr->m_flags,
+              ApiConnectRecord::TF_INDEX_OP_RETURN);
   regApiPtr->noIndexOp = 0;
   if(releaseIndexOperations)
     releaseAllSeizedIndexOperations(regApiPtr);
   regApiPtr->immediateTriggerId = RNIL;
 
+  tc_clearbit(regApiPtr->m_flags,
+              ApiConnectRecord::TF_DEFERRED_CONSTRAINTS);
   c_counters.ctransCount++;
 
 #ifdef ERROR_INSERT
@@ -2369,8 +2403,6 @@ Dbtc::seizeTcRecord(Signal* signal)
 
   regTcPtr->prevTcConnect = TlastTcConnect;
   regTcPtr->nextTcConnect = RNIL;
-  regTcPtr->accumulatingTriggerData.i = RNIL;  
-  regTcPtr->accumulatingTriggerData.p = NULL;  
   regTcPtr->noFiredTriggers = 0;
   regTcPtr->noReceivedTriggers = 0;
   regTcPtr->triggerExecutionCount = 0;
@@ -2479,13 +2511,15 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
   apiConnectptr.p = regApiPtr;
 
   Uint32 TstartFlag = TcKeyReq::getStartFlag(Treqinfo);
-  Uint32 TexecFlag = TcKeyReq::getExecuteFlag(Treqinfo);
+  Uint32 TexecFlag =
+    TcKeyReq::getExecuteFlag(Treqinfo) ? ApiConnectRecord::TF_EXEC_FLAG : 0;
 
   Uint8 Tspecial_op_flags = regApiPtr->m_special_op_flags;
-  bool isIndexOpReturn = regApiPtr->indexOpReturn;
+  bool isIndexOpReturn = tc_testbit(regApiPtr->m_flags,
+                                    ApiConnectRecord::TF_INDEX_OP_RETURN);
   bool isExecutingTrigger = Tspecial_op_flags & TcConnectRecord::SOF_TRIGGER;
   regApiPtr->m_special_op_flags = 0; // Reset marker
-  regApiPtr->m_exec_flag |= TexecFlag;
+  regApiPtr->m_flags |= TexecFlag;
   TableRecordPtr localTabptr;
   localTabptr.i = TtabIndex;
   localTabptr.p = &tableRecord[TtabIndex];
@@ -2497,7 +2531,7 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
       //---------------------------------------------------------------------
       jam();
       initApiConnectRec(signal, regApiPtr);
-      regApiPtr->m_exec_flag = TexecFlag;
+      regApiPtr->m_flags |= TexecFlag;
     } else {
       releaseSections(handle);
       if(getAllowStartTransaction(sendersNodeId, localTabptr.p->singleUserMode) == true){
@@ -2536,7 +2570,7 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
         return;
       }
       initApiConnectRec(signal, regApiPtr);
-      regApiPtr->m_exec_flag = TexecFlag;
+      regApiPtr->m_flags |= TexecFlag;
     } else { 
       //----------------------------------------------------------------------
       // Transaction is started already. 
@@ -2567,7 +2601,7 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
 	//--------------------------------------------------------------------
         jam();
         initApiConnectRec(signal, regApiPtr);
-	regApiPtr->m_exec_flag = TexecFlag;
+	regApiPtr->m_flags |= TexecFlag;
       } else if(TexecFlag) {
         releaseSections(handle);
 	TCKEY_abort(signal, 59);
@@ -2608,6 +2642,8 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
     }//if
     break;
   case CS_START_COMMITTING:
+  case CS_SEND_FIRE_TRIG_REQ:
+  case CS_WAIT_FIRE_TRIG_REQ:
     jam();
     if(isIndexOpReturn || isExecutingTrigger){
       break;
@@ -2730,6 +2766,11 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
     SegmentedSectionPtr attrInfoSec;
     if (handle.getSection(attrInfoSec, TcKeyReq::AttrInfoSectionNum))
       TattrLen= attrInfoSec.sz;
+
+    if (TcKeyReq::getDeferredConstraints(Treqinfo))
+    {
+      regApiPtr->m_flags |= ApiConnectRecord::TF_DEFERRED_CONSTRAINTS;
+    }
   }
   else
   {
@@ -2754,6 +2795,7 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
 
   if (isExecutingTrigger)
   {
+    ndbassert(tcConnectptr.i != TsenderData);
     // Save the TcOperationPtr for fireing operation
     regTcPtr->triggeringOperation = TsenderData;
     // Grab trigger Id from ApiConnectRecord
@@ -2934,7 +2976,8 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
   else
   {
     /* Insert, Update, Write, Delete */
-    if (!regApiPtr->m_commit_ack_marker_received)
+    if (!tc_testbit(regApiPtr->m_flags,
+                    ApiConnectRecord::TF_COMMIT_ACK_MARKER_RECEIVED))
     {
       if(regApiPtr->commitAckMarker != RNIL)
         regTcPtr->commitAckMarker = regApiPtr->commitAckMarker;
@@ -3021,7 +3064,7 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
       jam();
       // Trigger execution at commit
       regApiPtr->apiConnectstate = CS_REC_COMMITTING;
-    } else {
+    } else if (!regApiPtr->isExecutingDeferredTriggers()) {
       jam();
       regApiPtr->apiConnectstate = CS_RECEIVING;
     }//if
@@ -3317,6 +3360,10 @@ void Dbtc::tckeyreq050Lab(Signal* signal
       jam();
       regApiPtr->apiConnectstate = CS_START_COMMITTING;
       break;
+    case CS_SEND_FIRE_TRIG_REQ:
+    case CS_WAIT_FIRE_TRIG_REQ:
+      jam();
+      break;
     default:
       jam();
       systemErrorLab(signal, __LINE__);
@@ -3391,16 +3438,7 @@ void Dbtc::attrinfoDihReceivedLab(Signal
       TcConnectRecordPtr opPtr;
       opPtr.i = trigOp;
       ptrCheckGuard(opPtr, ctcConnectFilesize, tcConnectRecord);
-      opPtr.p->triggerExecutionCount--;
-      if (opPtr.p->triggerExecutionCount == 0)
-      {
-        /**
-         * We have completed current trigger execution
-         * Continue triggering operation
-         */
-        jam();
-        continueTriggeringOp(signal, opPtr.p);
-      }
+      trigger_op_finished(signal, apiConnectptr, opPtr.p);
       return;
     }
     else
@@ -3481,6 +3519,8 @@ void Dbtc::sendlqhkeyreq(Signal* signal,
     }//if
   }//if
 #endif
+  Uint32 Tdeferred = tc_testbit(regApiPtr->m_flags,
+                                ApiConnectRecord::TF_DEFERRED_CONSTRAINTS);
   Uint32 reorg = 0;
   Uint32 Tspecial_op = regTcPtr->m_special_op_flags;
   if (Tspecial_op == 0)
@@ -3546,6 +3586,7 @@ void Dbtc::sendlqhkeyreq(Signal* signal,
   LqhKeyReq::setSimpleFlag(Tdata10, sig0);
   LqhKeyReq::setOperation(Tdata10, sig1);
   LqhKeyReq::setNoDiskFlag(Tdata10, regCachePtr->m_no_disk_flag);
+  LqhKeyReq::setDeferredConstraints(Tdata10, Tdeferred);
 
   /* ----------------------------------------------------------------------- 
    * If we are sending a short LQHKEYREQ, then there will be some AttrInfo
@@ -3626,8 +3667,6 @@ void Dbtc::sendlqhkeyreq(Signal* signal,
   }//if
 
   // Reset trigger count
-  regTcPtr->accumulatingTriggerData.i = RNIL;  
-  regTcPtr->accumulatingTriggerData.p = NULL;  
   regTcPtr->noFiredTriggers = 0;
   regTcPtr->triggerExecutionCount = 0;
 
@@ -3951,6 +3990,13 @@ void Dbtc::execPACKED_SIGNAL(Signal* sig
       execLQHKEYCONF(signal);
       Tstep += LqhKeyConf::SignalLength;
       break;
+    case ZFIRE_TRIG_CONF:
+      jam();
+      signal->header.theLength = 4;
+      signal->theData[3] = TpackDataPtr[3];
+      execFIRE_TRIG_CONF(signal);
+      Tstep += 4;
+      break;
     default:
       systemErrorLab(signal, __LINE__);
       return;
@@ -4025,10 +4071,13 @@ void Dbtc::execSIGNAL_DROPPED_REP(Signal
     apiConnectptr.p->returncode = ZGET_DATAREC_ERROR;
 
     /* Set m_exec_flag according to the dropped request */
-    apiConnectptr.p->m_exec_flag = 
-      TcKeyReq::getExecuteFlag(truncatedTcKeyReq->requestInfo);
-
-    DEBUG("  Execute flag set to " << apiConnectptr.p->m_exec_flag);
+    apiConnectptr.p->m_flags |=
+      TcKeyReq::getExecuteFlag(truncatedTcKeyReq->requestInfo) ?
+      ApiConnectRecord::TF_EXEC_FLAG : 0;
+
+    DEBUG(" Execute flag set to " << tc_testbit(apiConnectptr.p->m_flags,
+                                                ApiConnectRecord::TF_EXEC_FLAG)
+          );
 
     abortErrorLab(signal);
 
@@ -4085,7 +4134,8 @@ void Dbtc::execSIGNAL_DROPPED_REP(Signal
 
 void Dbtc::execLQHKEYCONF(Signal* signal) 
 {
-  const LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+  const LqhKeyConf * lqhKeyConf = CAST_CONSTPTR(LqhKeyConf,
+                                                signal->getDataPtr());
   UintR compare_transid1, compare_transid2;
   BlockReference tlastLqhBlockref;
   UintR tlastLqhConnect;
@@ -4134,7 +4184,8 @@ void Dbtc::execLQHKEYCONF(Signal* signal
   UintR TapiConnectFilesize = capiConnectFilesize;
   UintR Ttrans1 = lqhKeyConf->transId1;
   UintR Ttrans2 = lqhKeyConf->transId2;
-  Uint32 noFired = lqhKeyConf->noFiredTriggers;
+  Uint32 noFired = LqhKeyConf::getFiredCount(lqhKeyConf->noFiredTriggers);
+  Uint32 deferred = LqhKeyConf::getDeferredBit(lqhKeyConf->noFiredTriggers);
 
   if (TapiConnectptrIndex >= TapiConnectFilesize) {
     TCKEY_abort(signal, 29);
@@ -4190,6 +4241,10 @@ void Dbtc::execLQHKEYCONF(Signal* signal
   regTcPtr->lastLqhCon = tlastLqhConnect;
   regTcPtr->lastLqhNodeId = refToNode(tlastLqhBlockref);
   regTcPtr->noFiredTriggers = noFired;
+  regTcPtr->m_special_op_flags |= (deferred) ?
+    TcConnectRecord::SOF_DEFERRED_TRIGGER : 0;
+  regApiPtr.p->m_flags |= (deferred) ?
+    ApiConnectRecord::TF_DEFERRED_TRIGGERS : 0;
 
   UintR Ttckeyrec = (UintR)regApiPtr.p->tckeyrec;
   UintR TclientData = regTcPtr->clientData;
@@ -4197,10 +4252,7 @@ void Dbtc::execLQHKEYCONF(Signal* signal
   Uint32 TopSimple = regTcPtr->opSimple;
   Uint32 Toperation = regTcPtr->operation;
   ConnectionState TapiConnectstate = regApiPtr.p->apiConnectstate;
-  if (Ttckeyrec > (ZTCOPCONF_SIZE - 2)) {
-    TCKEY_abort(signal, 30);
-    return;
-  }
+
   if (TapiConnectstate == CS_ABORTING) {
     warningReport(signal, 27);
     return;
@@ -4225,7 +4277,7 @@ void Dbtc::execLQHKEYCONF(Signal* signal
     const Uint32 noOfLqhs = regTcPtr->noOfNodes;
     CommitAckMarker * tmp = m_commitAckMarkerHash.getPtr(commitAckMarker);
     jam();
-    regApiPtr.p->m_commit_ack_marker_received = TRUE;
+    regApiPtr.p->m_flags |= ApiConnectRecord::TF_COMMIT_ACK_MARKER_RECEIVED;
     /**
      * Populate LQH array
      */
@@ -4240,6 +4292,12 @@ void Dbtc::execLQHKEYCONF(Signal* signal
   } else {
     if (noFired == 0 && regTcPtr->triggeringOperation == RNIL) {
       jam();
+
+      if (Ttckeyrec > (ZTCOPCONF_SIZE - 2)) {
+        TCKEY_abort(signal, 30);
+        return;
+      }
+
       /*
        * Skip counting triggering operations the first round
        * since they will enter execLQHKEYCONF a second time
@@ -4353,23 +4411,17 @@ void Dbtc::execLQHKEYCONF(Signal* signal
   /**
    * And now decide what to do next
    */
-  if (regTcPtr->triggeringOperation != RNIL) {
+  if (regTcPtr->triggeringOperation != RNIL &&
+      !regApiPtr.p->isExecutingDeferredTriggers()) {
     jam();
     // This operation was created by a trigger execting operation
     // Restart it if we have executed all it's triggers
     TcConnectRecordPtr opPtr;
 
     opPtr.i = regTcPtr->triggeringOperation;
+    ndbassert(opPtr.i != TtcConnectptrIndex);
     ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
-    opPtr.p->triggerExecutionCount--;
-    if (opPtr.p->triggerExecutionCount == 0) {
-      /*
-      We have completed current trigger execution
-      Continue triggering operation
-      */
-      jam();
-      continueTriggeringOp(signal, opPtr.p);
-    }
+    trigger_op_finished(signal, regApiPtr, opPtr.p);
   } else if (noFired == 0) {
     // This operation did not fire any triggers, finish operation
     jam();
@@ -4395,7 +4447,7 @@ void Dbtc::execLQHKEYCONF(Signal* signal
 void Dbtc::setupIndexOpReturn(ApiConnectRecord* regApiPtr,
 			      TcConnectRecord* regTcPtr) 
 {
-  regApiPtr->indexOpReturn = true;
+  regApiPtr->m_flags |= ApiConnectRecord::TF_INDEX_OP_RETURN;
   regApiPtr->indexOp = regTcPtr->indexOp;
   regApiPtr->clientData = regTcPtr->clientData;
   regApiPtr->attrInfoLen = regTcPtr->attrInfoLen;
@@ -4426,6 +4478,7 @@ Dbtc::lqhKeyConf_checkTransactionState(S
   UintR Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
   int TnoOfOutStanding = Tlqhkeyreqrec - Tlqhkeyconfrec;
 
+  apiConnectptr = regApiPtr;
   switch (TapiConnectstate) {
   case CS_START_COMMITTING:
     if (TnoOfOutStanding == 0) {
@@ -4437,7 +4490,10 @@ Dbtc::lqhKeyConf_checkTransactionState(S
         jam();
         sendtckeyconf(signal, 0);
         return;
-      } else if (regApiPtr.p->indexOpReturn) {
+      }
+      else if (tc_testbit(regApiPtr.p->m_flags,
+                          ApiConnectRecord::TF_INDEX_OP_RETURN))
+      {
 	jam();
         sendtckeyconf(signal, 0);
         return;
@@ -4460,11 +4516,14 @@ Dbtc::lqhKeyConf_checkTransactionState(S
         jam();
         sendtckeyconf(signal, 0);
         return;
-      } else if (regApiPtr.p->indexOpReturn) {
+      }
+      else if (tc_testbit(regApiPtr.p->m_flags,
+                          ApiConnectRecord::TF_INDEX_OP_RETURN))
+      {
 	jam();
         sendtckeyconf(signal, 0);
         return;
-	}//if
+      }//if
       jam();
     }//if
     return;
@@ -4474,7 +4533,10 @@ Dbtc::lqhKeyConf_checkTransactionState(S
         jam();
         sendtckeyconf(signal, 0);
         return;
-      } else if (regApiPtr.p->indexOpReturn) {
+      }
+      else if (tc_testbit(regApiPtr.p->m_flags,
+                          ApiConnectRecord::TF_INDEX_OP_RETURN))
+      {
         jam();
         sendtckeyconf(signal, 0);
         return;
@@ -4493,6 +4555,17 @@ Dbtc::lqhKeyConf_checkTransactionState(S
 /*---------------------------------------------------------------*/
     regApiPtr.p->tckeyrec = 0;
     return;
+  case CS_SEND_FIRE_TRIG_REQ:
+    return;
+  case CS_WAIT_FIRE_TRIG_REQ:
+    if (TnoOfOutStanding == 0 && regApiPtr.p->pendingTriggers == 0)
+    {
+      jam();
+      regApiPtr.p->apiConnectstate = CS_START_COMMITTING;
+      diverify010Lab(signal);
+      return;
+    }
+    return;
   default:
     TCKEY_abort(signal, 46);
     return;
@@ -4526,7 +4599,8 @@ void Dbtc::sendtckeyconf(Signal* signal,
   const UintR TpacketLen = 6 + TopWords;
   regApiPtr->tckeyrec = 0;
 
-  if (regApiPtr->indexOpReturn) {
+  if (tc_testbit(regApiPtr->m_flags, ApiConnectRecord::TF_INDEX_OP_RETURN))
+  {
     jam();
     // Return internally generated TCKEY
     TcKeyConf * const tcKeyConf = (TcKeyConf *)signal->getDataPtrSend();
@@ -4543,7 +4617,7 @@ void Dbtc::sendtckeyconf(Signal* signal,
     Uint32 sigLen = 1 /** gci_lo */ +
       TcKeyConf::StaticLength + TcKeyConf::OperationLength;
     EXECUTE_DIRECT(DBTC, GSN_TCKEYCONF, signal, sigLen);
-    regApiPtr->indexOpReturn = false;
+    tc_clearbit(regApiPtr->m_flags, ApiConnectRecord::TF_INDEX_OP_RETURN);
     if (TopWords == 0) {
       jam();
       return; // No queued TcKeyConf
@@ -4551,7 +4625,7 @@ void Dbtc::sendtckeyconf(Signal* signal,
   }//if
   if(TcommitFlag){
     jam();
-    regApiPtr->m_exec_flag = 0;
+    tc_clearbit(regApiPtr->m_flags, ApiConnectRecord::TF_EXEC_FLAG);
   }
   TcKeyConf::setNoOfOperations(confInfo, (TopWords >> 1));
   if ((TpacketLen + 1 /** gci_lo */ > 25) || !is_api){
@@ -4651,7 +4725,6 @@ void Dbtc::execSEND_PACKED(Signal* signa
     arrGuard(Thostptr.i - 1, MAX_NODES - 1);
     UintR TnoOfPackedWordsLqh = Thostptr.p->noOfPackedWordsLqh;
     UintR TnoOfWordsTCKEYCONF = Thostptr.p->noOfWordsTCKEYCONF;
-    UintR TnoOfWordsTCINDXCONF = Thostptr.p->noOfWordsTCINDXCONF;
     jam();
     if (TnoOfPackedWordsLqh > 0) {
       jam();
@@ -4661,10 +4734,6 @@ void Dbtc::execSEND_PACKED(Signal* signa
       jam();
       sendPackedTCKEYCONF(signal, Thostptr.p, (Uint32)Thostptr.i);
     }//if
-    if (TnoOfWordsTCINDXCONF > 0) {
-      jam();
-      sendPackedTCINDXCONF(signal, Thostptr.p, (Uint32)Thostptr.i);
-    }//if
     Thostptr.p->inPackedList = false;
   }//for
   cpackedListIndex = 0;
@@ -4726,27 +4795,6 @@ void Dbtc::sendPackedTCKEYCONF(Signal* s
   sendSignal(TBref, GSN_TCKEYCONF, signal, TnoOfWords, JBB);
 }//Dbtc::sendPackedTCKEYCONF()
 
-void Dbtc::sendPackedTCINDXCONF(Signal* signal,
-                                HostRecord * ahostptr,
-                                UintR hostId)
-{
-  UintR Tj;
-  UintR TnoOfWords = ahostptr->noOfWordsTCINDXCONF;
-  BlockReference TBref = numberToRef(API_PACKED, hostId);
-  for (Tj = 0; Tj < ahostptr->noOfWordsTCINDXCONF; Tj += 4) {
-    UintR sig0 = ahostptr->packedWordsTCINDXCONF[Tj + 0];
-    UintR sig1 = ahostptr->packedWordsTCINDXCONF[Tj + 1];
-    UintR sig2 = ahostptr->packedWordsTCINDXCONF[Tj + 2];
-    UintR sig3 = ahostptr->packedWordsTCINDXCONF[Tj + 3];
-    signal->theData[Tj + 0] = sig0;
-    signal->theData[Tj + 1] = sig1;
-    signal->theData[Tj + 2] = sig2;
-    signal->theData[Tj + 3] = sig3;
-  }//for
-  ahostptr->noOfWordsTCINDXCONF = 0;
-  sendSignal(TBref, GSN_TCINDXCONF, signal, TnoOfWords, JBB);
-}//Dbtc::sendPackedTCINDXCONF()
-
 /*
 4.3.11 DIVERIFY 
 ---------------
@@ -4765,6 +4813,19 @@ void Dbtc::diverify010Lab(Signal* signal
     systemErrorLab(signal, __LINE__);
   }//if
 
+  if (tc_testbit(regApiPtr->m_flags, ApiConnectRecord::TF_DEFERRED_TRIGGERS))
+  {
+    jam();
+    /**
+     * If trans has deferred triggers, let them fire just before
+     *   transaction starts to commit
+     */
+    regApiPtr->pendingTriggers = 0;
+    tc_clearbit(regApiPtr->m_flags, ApiConnectRecord::TF_DEFERRED_TRIGGERS);
+    sendFireTrigReq(signal, apiConnectptr, regApiPtr->firstTcConnect);
+    return;
+  }
+
   if (regApiPtr->lqhkeyreqrec)
   {
     if (TfirstfreeApiConnectCopy != RNIL) {
@@ -4823,7 +4884,8 @@ void Dbtc::seizeApiConnectCopy(Signal* s
   cfirstfreeApiConnectCopy = locApiConnectptr.p->nextApiConnect;
   locApiConnectptr.p->nextApiConnect = RNIL;
   regApiPtr->apiCopyRecord = locApiConnectptr.i;
-  regApiPtr->triggerPending = false;
+  tc_clearbit(regApiPtr->m_flags,
+              ApiConnectRecord::TF_TRIGGER_PENDING);
   regApiPtr->m_special_op_flags = 0;
 }//Dbtc::seizeApiConnectCopy()
 
@@ -5536,6 +5598,237 @@ Dbtc::sendCompleteLqh(Signal* signal,
 }
 
 void
+Dbtc::sendFireTrigReq(Signal* signal,
+                      Ptr<ApiConnectRecord> regApiPtr,
+                      Uint32 TopPtrI)
+{
+  UintR TtcConnectFilesize = ctcConnectFilesize;
+  TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+  TcConnectRecordPtr localTcConnectptr;
+
+  setApiConTimer(regApiPtr.i, ctcTimer, __LINE__);
+  regApiPtr.p->apiConnectstate = CS_SEND_FIRE_TRIG_REQ;
+
+  localTcConnectptr.i = TopPtrI;
+  ndbassert(TopPtrI != RNIL);
+  Uint32 Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
+  Uint32 pass = regApiPtr.p->m_pre_commit_pass;
+  for (Uint32 i = 0; localTcConnectptr.i != RNIL && i < 16; i++)
+  {
+    ptrCheckGuard(localTcConnectptr,
+                  TtcConnectFilesize, localTcConnectRecord);
+
+    const Uint32 nextTcConnect = localTcConnectptr.p->nextTcConnect;
+    Uint32 flags = localTcConnectptr.p->m_special_op_flags;
+    if (flags & TcConnectRecord::SOF_DEFERRED_TRIGGER)
+    {
+      jam();
+      tc_clearbit(flags, TcConnectRecord::SOF_DEFERRED_TRIGGER);
+      ndbrequire(localTcConnectptr.p->tcConnectstate == OS_PREPARED);
+      localTcConnectptr.p->tcConnectstate = OS_FIRE_TRIG_REQ;
+      localTcConnectptr.p->m_special_op_flags = flags;
+      i += sendFireTrigReqLqh(signal, localTcConnectptr, pass);
+      Tlqhkeyreqrec++;
+    }
+    localTcConnectptr.i = nextTcConnect;
+  }
+
+  regApiPtr.p->lqhkeyreqrec = Tlqhkeyreqrec;
+  if (localTcConnectptr.i == RNIL)
+  {
+    /**
+     * Now wait for FIRE_TRIG_CONF
+     */
+    jam();
+    regApiPtr.p->apiConnectstate = CS_WAIT_FIRE_TRIG_REQ;
+    ndbrequire(pass < 255);
+    regApiPtr.p->m_pre_commit_pass = (Uint8)(pass + 1);
+    return;
+  }
+  else
+  {
+    jam();
+    signal->theData[0] = TcContinueB::ZSEND_FIRE_TRIG_REQ;
+    signal->theData[1] = regApiPtr.i;
+    signal->theData[2] = regApiPtr.p->transid[0];
+    signal->theData[3] = regApiPtr.p->transid[1];
+    signal->theData[4] = localTcConnectptr.i;
+    if (ERROR_INSERTED_CLEAR(8090))
+    {
+      sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 5000, 5);
+    }
+    else
+    {
+      sendSignal(cownref, GSN_CONTINUEB, signal, 5, JBB);
+    }
+  }
+}
+
+Uint32
+Dbtc::sendFireTrigReqLqh(Signal* signal,
+                         Ptr<TcConnectRecord> regTcPtr,
+                         Uint32 pass)
+{
+  HostRecordPtr Thostptr;
+  UintR ThostFilesize = chostFilesize;
+  ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+  Thostptr.i = regTcPtr.p->tcNodedata[0];
+  ptrCheckGuard(Thostptr, ThostFilesize, hostRecord);
+
+  Uint32 Tnode = Thostptr.i;
+  Uint32 self = getOwnNodeId();
+  Uint32 ret = (Tnode == self) ? 4 : 1;
+
+  Uint32 Tdata[FireTrigReq::SignalLength];
+  FireTrigReq * req = CAST_PTR(FireTrigReq, Tdata);
+  req->tcOpRec = regTcPtr.i;
+  req->transId[0] = regApiPtr->transid[0];
+  req->transId[1] = regApiPtr->transid[1];
+  req->pass = pass;
+  Uint32 len = FireTrigReq::SignalLength;
+
+  // currently packed signal cannot address specific instance
+  const bool send_unpacked = getNodeInfo(Thostptr.i).m_lqh_workers > 1;
+  if (send_unpacked) {
+    memcpy(signal->theData, Tdata, len << 2);
+    Uint32 instanceKey = regTcPtr.p->lqhInstanceKey;
+    BlockReference lqhRef = numberToRef(DBLQH, instanceKey, Tnode);
+    sendSignal(lqhRef, GSN_FIRE_TRIG_REQ, signal, len, JBB);
+    return ret;
+  }
+
+  if (Thostptr.p->noOfPackedWordsLqh > 25 - len) {
+    jam();
+    sendPackedSignalLqh(signal, Thostptr.p);
+  } else {
+    jam();
+    ret = 1;
+    updatePackedList(signal, Thostptr.p, Thostptr.i);
+  }
+
+  Tdata[0] |= (ZFIRE_TRIG_REQ << 28);
+  UintR Tindex = Thostptr.p->noOfPackedWordsLqh;
+  UintR* TDataPtr = &Thostptr.p->packedWordsLqh[Tindex];
+  memcpy(TDataPtr, Tdata, len << 2);
+  Thostptr.p->noOfPackedWordsLqh = Tindex + len;
+  return ret;
+}
+
+void
+Dbtc::execFIRE_TRIG_CONF(Signal* signal)
+{
+  TcConnectRecordPtr localTcConnectptr;
+  ApiConnectRecordPtr regApiPtr;
+
+  UintR TtcConnectFilesize = ctcConnectFilesize;
+  UintR TapiConnectFilesize = capiConnectFilesize;
+  TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+  ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+  const FireTrigConf * conf = CAST_CONSTPTR(FireTrigConf, signal->theData);
+  localTcConnectptr.i = conf->tcOpRec;
+  jamEntry();
+  ptrCheckGuard(localTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+  regApiPtr.i = localTcConnectptr.p->apiConnect;
+  if (localTcConnectptr.p->tcConnectstate != OS_FIRE_TRIG_REQ)
+  {
+    warningReport(signal, 28);
+    return;
+  }//if
+  ptrCheckGuard(regApiPtr, TapiConnectFilesize,
+                localApiConnectRecord);
+
+  Uint32 Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
+  Uint32 TapiConnectstate = regApiPtr.p->apiConnectstate;
+  UintR Tdata1 = regApiPtr.p->transid[0] - conf->transId[0];
+  UintR Tdata2 = regApiPtr.p->transid[1] - conf->transId[1];
+  Uint32 TcheckCondition =
+    (TapiConnectstate - CS_SEND_FIRE_TRIG_REQ) &
+    (TapiConnectstate - CS_WAIT_FIRE_TRIG_REQ);
+
+  Tdata1 = Tdata1 | Tdata2 | TcheckCondition;
+
+  if (Tdata1 != 0) {
+    warningReport(signal, 28);
+    return;
+  }//if
+
+  if (ERROR_INSERTED_CLEAR(8091))
+  {
+    jam();
+    return;
+  }
+
+  CRASH_INSERTION(8092);
+
+  setApiConTimer(regApiPtr.i, ctcTimer, __LINE__);
+  ndbassert(Tlqhkeyreqrec > 0);
+  regApiPtr.p->lqhkeyreqrec = Tlqhkeyreqrec - 1;
+  localTcConnectptr.p->tcConnectstate = OS_PREPARED;
+
+  Uint32 noFired  = FireTrigConf::getFiredCount(conf->noFiredTriggers);
+  Uint32 deferred = FireTrigConf::getDeferredBit(conf->noFiredTriggers);
+
+  regApiPtr.p->pendingTriggers += noFired;
+  regApiPtr.p->m_flags |= (deferred) ?
+    ApiConnectRecord::TF_DEFERRED_TRIGGERS : 0;
+  localTcConnectptr.p->m_special_op_flags |= (deferred) ?
+    TcConnectRecord::SOF_DEFERRED_TRIGGER : 0;
+
+  if (regApiPtr.p->pendingTriggers == 0)
+  {
+    jam();
+    lqhKeyConf_checkTransactionState(signal, regApiPtr);
+  }
+}
+
+void
+Dbtc::execFIRE_TRIG_REF(Signal* signal)
+{
+  TcConnectRecordPtr localTcConnectptr;
+  ApiConnectRecordPtr regApiPtr;
+
+  UintR TtcConnectFilesize = ctcConnectFilesize;
+  UintR TapiConnectFilesize = capiConnectFilesize;
+  TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+  ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+  const FireTrigRef * ref = CAST_CONSTPTR(FireTrigRef, signal->theData);
+  localTcConnectptr.i = ref->tcOpRec;
+  jamEntry();
+  ptrCheckGuard(localTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+  regApiPtr.i = localTcConnectptr.p->apiConnect;
+  if (localTcConnectptr.p->tcConnectstate != OS_FIRE_TRIG_REQ)
+  {
+    warningReport(signal, 28);
+    return;
+  }//if
+  ptrCheckGuard(regApiPtr, TapiConnectFilesize,
+                localApiConnectRecord);
+
+  apiConnectptr = regApiPtr;
+
+  UintR Tdata1 = regApiPtr.p->transid[0] - ref->transId[0];
+  UintR Tdata2 = regApiPtr.p->transid[1] - ref->transId[1];
+  Tdata1 = Tdata1 | Tdata2;
+  if (Tdata1 != 0) {
+    warningReport(signal, 28);
+    return;
+  }//if
+
+  if (regApiPtr.p->apiConnectstate != CS_SEND_FIRE_TRIG_REQ &&
+      regApiPtr.p->apiConnectstate != CS_WAIT_FIRE_TRIG_REQ)
+  {
+    jam();
+    warningReport(signal, 28);
+    return;
+  }
+
+  terrorCode = ref->errCode;
+  abortErrorLab(signal);
+}
+
+void
 Dbtc::execTC_COMMIT_ACK(Signal* signal){
   jamEntry();
 
@@ -5844,6 +6137,7 @@ void Dbtc::execLQHKEYREF(Signal* signal)
 	TcConnectRecord *localTcConnectRecord = tcConnectRecord;
 
         opPtr.i = triggeringOp;
+        ndbassert(opPtr.i != tcConnectptr.i);
         ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
 
         const Uint32 opType = regTcPtr->operation;
@@ -5919,19 +6213,16 @@ void Dbtc::execLQHKEYREF(Signal* signal)
          */
         regApiPtr->lqhkeyreqrec--;
 
+        /**
+         * An failing op in LQH, never leaves the commit ack marker around
+         * TODO: This can be bug in ordinary code too!!!
+         */
+        clearCommitAckMarker(regApiPtr, regTcPtr);
+
         unlinkReadyTcCon(signal);
         releaseTcCon();
 
-        opPtr.p->triggerExecutionCount--;
-        if (opPtr.p->triggerExecutionCount == 0)
-        {
-          /**
-           * We have completed current trigger execution
-           * Continue triggering operation
-           */
-          jam();
-          continueTriggeringOp(signal, opPtr.p);
-        }
+        trigger_op_finished(signal, apiConnectptr, opPtr.p);
         return;
       }
       
@@ -5996,7 +6287,10 @@ void Dbtc::execLQHKEYREF(Signal* signal)
           jam();
           diverify010Lab(signal);
 	  return;
-	} else if (regApiPtr->tckeyrec > 0 || regApiPtr->m_exec_flag) {
+	}
+        else if (regApiPtr->tckeyrec > 0 ||
+                 tc_testbit(regApiPtr->m_flags, ApiConnectRecord::TF_EXEC_FLAG))
+        {
 	  jam();
 	  sendtckeyconf(signal, 2);
 	  return;
@@ -6032,7 +6326,8 @@ void Dbtc::clearCommitAckMarker(ApiConne
     if (regApiPtr->no_commit_ack_markers == 0)
     {
       regApiPtr->commitAckMarker = RNIL;
-      regApiPtr->m_commit_ack_marker_received = FALSE;
+      tc_clearbit(regApiPtr->m_flags,
+                  ApiConnectRecord::TF_COMMIT_ACK_MARKER_RECEIVED);
       m_commitAckMarkerHash.release(commitAckMarker);
     }
   }
@@ -6080,7 +6375,7 @@ void Dbtc::execTC_COMMITREQ(Signal* sign
     const Uint32 transId2      = regApiPtr->transid[1];
     Uint32 errorCode           = 0;
 
-    regApiPtr->m_exec_flag = 1;
+    regApiPtr->m_flags |= ApiConnectRecord::TF_EXEC_FLAG;
     switch (regApiPtr->apiConnectstate) {
     case CS_STARTED:
       tcConnectptr.i = regApiPtr->firstTcConnect;
@@ -6232,7 +6527,7 @@ void Dbtc::execTCROLLBACKREQ(Signal* sig
     return;
   }//if
 
-  apiConnectptr.p->m_exec_flag = 1;
+  apiConnectptr.p->m_flags |= ApiConnectRecord::TF_EXEC_FLAG;
   switch (apiConnectptr.p->apiConnectstate) {
   case CS_STARTED:
   case CS_RECEIVING:
@@ -6518,6 +6813,18 @@ void Dbtc::warningReport(Signal* signal,
     ndbout << "Received LQHKEYCONF in wrong api-state in Dbtc" << endl;
 #endif
     break;
+  case 28:
+    jam();
+#ifdef ABORT_TRACE
+    ndbout << "Discarding FIRE_TRIG_REF/CONF in Dbtc" << endl;
+#endif
+    break;
+  case 29:
+    jam();
+#ifdef ABORT_TRACE
+    ndbout << "Discarding TcContinueB::ZSEND_FIRE_TRIG_REQ in Dbtc" << endl;
+#endif
+    break;
   default:
     jam();
     break;
@@ -6768,6 +7075,8 @@ ABORT020:
     jam();
   case OS_OPERATING:
     jam();
+  case OS_FIRE_TRIG_REQ:
+    jam();
     /*----------------------------------------------------------------------
      * WE HAVE SENT LQHKEYREQ AND ARE IN SOME STATE OF EITHER STILL       
      * SENDING THE OPERATION, WAITING FOR REPLIES, WAITING FOR MORE       
@@ -7071,7 +7380,8 @@ void Dbtc::timeOutFoundLab(Signal* signa
 	<< " H'" << apiConnectptr.p->transid[1] << "] " << dec 
 	<< "Time-out in state = " << apiConnectptr.p->apiConnectstate
 	<< " apiConnectptr.i = " << apiConnectptr.i 
-	<< " - exec: " << apiConnectptr.p->m_exec_flag
+	<< " - exec: "
+        << tc_testbit(apiConnectptr.p->m_flags, ApiConnectRecord::TF_EXEC_FLAG)
 	<< " - place: " << c_apiConTimer_line[apiConnectptr.i]
 	<< " code: " << errCode);
   switch (apiConnectptr.p->apiConnectstate) {
@@ -7097,6 +7407,8 @@ void Dbtc::timeOutFoundLab(Signal* signa
   case CS_RECEIVING:
   case CS_REC_COMMITTING:
   case CS_START_COMMITTING:
+  case CS_WAIT_FIRE_TRIG_REQ:
+  case CS_SEND_FIRE_TRIG_REQ:
     jam();
     /*------------------------------------------------------------------*/
     /*       WE ARE STILL IN THE PREPARE PHASE AND THE TRANSACTION HAS  */
@@ -9390,7 +9702,7 @@ void Dbtc::initTcConnectFail(Signal* sig
   tcConnectptr.p->lastReplicaNo = LqhTransConf::getLastReplicaNo(treqinfo);
   tcConnectptr.p->dirtyOp = LqhTransConf::getDirtyFlag(treqinfo);
   tcConnectptr.p->lqhInstanceKey = instanceKey;
-
+  tcConnectptr.p->triggeringOperation = RNIL;
 }//Dbtc::initTcConnectFail()
 
 /*----------------------------------------------------------*/
@@ -11637,7 +11949,7 @@ void Dbtc::initApiConnect(Signal* signal
     apiConnectptr.p->commitAckMarker = RNIL;
     apiConnectptr.p->firstTcConnect = RNIL;
     apiConnectptr.p->lastTcConnect = RNIL;
-    apiConnectptr.p->triggerPending = false;
+    apiConnectptr.p->m_flags = 0;
     apiConnectptr.p->m_special_op_flags = 0;
     apiConnectptr.p->accumulatingIndexOp = RNIL;
     apiConnectptr.p->executingIndexOp = RNIL;
@@ -11666,7 +11978,7 @@ void Dbtc::initApiConnect(Signal* signal
       apiConnectptr.p->commitAckMarker = RNIL;
       apiConnectptr.p->firstTcConnect = RNIL;
       apiConnectptr.p->lastTcConnect = RNIL;
-      apiConnectptr.p->triggerPending = false;
+      apiConnectptr.p->m_flags = 0;
       apiConnectptr.p->m_special_op_flags = 0;
       apiConnectptr.p->accumulatingIndexOp = RNIL;
       apiConnectptr.p->executingIndexOp = RNIL;
@@ -11695,7 +12007,7 @@ void Dbtc::initApiConnect(Signal* signal
     apiConnectptr.p->commitAckMarker = RNIL;
     apiConnectptr.p->firstTcConnect = RNIL;
     apiConnectptr.p->lastTcConnect = RNIL;
-    apiConnectptr.p->triggerPending = false;
+    apiConnectptr.p->m_flags = 0;
     apiConnectptr.p->m_special_op_flags = 0;
     apiConnectptr.p->accumulatingIndexOp = RNIL;
     apiConnectptr.p->executingIndexOp = RNIL;
@@ -11737,7 +12049,6 @@ void Dbtc::inithost(Signal* signal) 
     hostptr.p->inPackedList = false;
     hostptr.p->lqhTransStatus = LTS_IDLE;
     hostptr.p->noOfWordsTCKEYCONF = 0;
-    hostptr.p->noOfWordsTCINDXCONF = 0;
     hostptr.p->noOfPackedWordsLqh = 0;
     hostptr.p->hostLqhBlockRef = calcLqhBlockRef(hostptr.i);
     hostptr.p->m_nf_bits = 0;
@@ -12002,13 +12313,15 @@ void Dbtc::releaseAbortResources(Signal*
   apiConnectptr.p->abortState = AS_IDLE;
   releaseAllSeizedIndexOperations(apiConnectptr.p);
 
-  if(apiConnectptr.p->m_exec_flag || apiConnectptr.p->apiFailState == ZTRUE){
+  if (tc_testbit(apiConnectptr.p->m_flags, ApiConnectRecord::TF_EXEC_FLAG) ||
+      apiConnectptr.p->apiFailState == ZTRUE)
+  {
     jam();
     bool ok = false;
     Uint32 blockRef = apiConnectptr.p->ndbapiBlockref;
     ReturnSignal ret = apiConnectptr.p->returnsignal;
     apiConnectptr.p->returnsignal = RS_NO_RETURN;
-    apiConnectptr.p->m_exec_flag = 0;
+    tc_clearbit(apiConnectptr.p->m_flags, ApiConnectRecord::TF_EXEC_FLAG);
     switch(ret){
     case RS_TCROLLBACKCONF:
       jam();
@@ -12110,7 +12423,8 @@ void Dbtc::seizeApiConnect(Signal* signa
     apiConnectptr.p->nextApiConnect = RNIL;
     setApiConTimer(apiConnectptr.i, 0, __LINE__);
     apiConnectptr.p->apiConnectstate = CS_CONNECTED; /* STATE OF CONNECTION */
-    apiConnectptr.p->triggerPending = false;
+    tc_clearbit(apiConnectptr.p->m_flags,
+                ApiConnectRecord::TF_TRIGGER_PENDING);
     apiConnectptr.p->m_special_op_flags = 0;
   } else {
     jam();
@@ -13325,11 +13639,12 @@ void Dbtc::execFIRE_TRIG_ORD(Signal* sig
     {
       jam();      
       opPtr.p->noReceivedTriggers++;
-      opPtr.p->triggerExecutionCount++;
+      opPtr.p->triggerExecutionCount++; // Default 1 LQHKEYREQ per trigger
 
       // Insert fired trigger in execution queue
       transPtr.p->theFiredTriggers.add(trigPtr);
-      if (opPtr.p->noReceivedTriggers == opPtr.p->noFiredTriggers) {
+      if (opPtr.p->noReceivedTriggers == opPtr.p->noFiredTriggers ||
+          transPtr.p->isExecutingDeferredTriggers()) {
 	executeTriggers(signal, &transPtr);
       }
       return;
@@ -13528,7 +13843,9 @@ void Dbtc::execTCINDXREQ(Signal* signal)
     jam();
     releaseSections(handle);
     terrorCode = ZCLUSTER_IN_SINGLEUSER_MODE;
-    regApiPtr->m_exec_flag |= TcKeyReq::getExecuteFlag(tcIndxRequestInfo);
+    regApiPtr->m_flags |=
+      TcKeyReq::getExecuteFlag(tcIndxRequestInfo) ?
+      ApiConnectRecord::TF_EXEC_FLAG : 0;
     apiConnectptr = transPtr;
     abortErrorLab(signal);
     return;
@@ -13539,7 +13856,9 @@ void Dbtc::execTCINDXREQ(Signal* signal)
     releaseSections(handle);
     // Failed to allocate index operation
     terrorCode = 288;
-    regApiPtr->m_exec_flag |= TcKeyReq::getExecuteFlag(tcIndxRequestInfo);
+    regApiPtr->m_flags |=
+      TcKeyReq::getExecuteFlag(tcIndxRequestInfo) ?
+      ApiConnectRecord::TF_EXEC_FLAG : 0;
     apiConnectptr = transPtr;
     abortErrorLab(signal);
     return;
@@ -13630,94 +13949,6 @@ void Dbtc::execTCINDXREQ(Signal* signal)
   }
 }
 
-
-void Dbtc::sendTcIndxConf(Signal* signal, UintR TcommitFlag) 
-{
-  HostRecordPtr localHostptr;
-  ApiConnectRecord * const regApiPtr = apiConnectptr.p;
-  const UintR TopWords = (UintR)regApiPtr->tcindxrec;
-  localHostptr.i = refToNode(regApiPtr->ndbapiBlockref);
-  const Uint32 type = getNodeInfo(localHostptr.i).m_type;
-  const bool is_api = (type >= NodeInfo::API && type <= NodeInfo::MGM);
-  const BlockNumber TblockNum = refToBlock(regApiPtr->ndbapiBlockref);
-  const Uint32 Tmarker = (regApiPtr->commitAckMarker == RNIL ? 0 : 1);
-  ptrAss(localHostptr, hostRecord);
-  UintR TcurrLen = localHostptr.p->noOfWordsTCINDXCONF;
-  UintR confInfo = 0;
-  TcIndxConf::setNoOfOperations(confInfo, (TopWords >> 1));
-  TcIndxConf::setCommitFlag(confInfo, TcommitFlag == 1);
-  TcIndxConf::setMarkerFlag(confInfo, Tmarker);
-  const UintR TpacketLen = 6 + TopWords;
-  regApiPtr->tcindxrec = 0;
-
-  if(TcommitFlag || (regApiPtr->lqhkeyreqrec == regApiPtr->lqhkeyconfrec)){
-    jam();
-    regApiPtr->m_exec_flag = 0;
-  }
-
-  if ((TpacketLen + 1 /** gci_lo */ > 25) || !is_api){
-    TcIndxConf * const tcIndxConf = (TcIndxConf *)signal->getDataPtrSend();
-    
-    jam();
-    tcIndxConf->apiConnectPtr = regApiPtr->ndbapiConnect;
-    tcIndxConf->gci_hi = Uint32(regApiPtr->globalcheckpointid >> 32);
-    Uint32 *gci_lo = (Uint32*)&tcIndxConf->operations[TopWords >> 1];
-    * gci_lo = Uint32(regApiPtr->globalcheckpointid);
-    tcIndxConf->confInfo = confInfo;
-    tcIndxConf->transId1 = regApiPtr->transid[0];
-    tcIndxConf->transId2 = regApiPtr->transid[1];
-    copyFromToLen(&regApiPtr->tcIndxSendArray[0],
-                  (UintR*)&tcIndxConf->operations,
-                  (UintR)ZTCOPCONF_SIZE);
-    sendSignal(regApiPtr->ndbapiBlockref,
-               GSN_TCINDXCONF, signal,
-               (TpacketLen - 1) + 1 /** gci_lo */, JBB);
-    return;
-  } else if (((TcurrLen + TpacketLen + 1 /** gci_lo */) > 25) &&
-             (TcurrLen > 0)) {
-    jam();
-    sendPackedTCINDXCONF(signal, localHostptr.p, localHostptr.i);
-    TcurrLen = 0;
-  } else {
-    jam();
-    updatePackedList(signal, localHostptr.p, localHostptr.i);
-  }//if
-// -------------------------------------------------------------------------
-// The header contains the block reference of receiver plus the real signal
-// length - 3, since we have the real signal length plus one additional word
-// for the header we have to do - 4.
-// -------------------------------------------------------------------------
-  UintR Tpack0 = (TblockNum << 16) + (TpacketLen - 4 + 1 /** gci_lo */);
-  UintR Tpack1 = regApiPtr->ndbapiConnect;
-  UintR Tpack2 = Uint32(regApiPtr->globalcheckpointid >> 32);
-  UintR Tpack3 = confInfo;
-  UintR Tpack4 = regApiPtr->transid[0];
-  UintR Tpack5 = regApiPtr->transid[1];
-  UintR Tpack6 = Uint32(regApiPtr->globalcheckpointid);
-
-  localHostptr.p->noOfWordsTCINDXCONF = TcurrLen + TpacketLen + 1 /* gci_lo */;
-
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 0] = Tpack0;
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 1] = Tpack1;
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 2] = Tpack2;
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 3] = Tpack3;
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 4] = Tpack4;
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 5] = Tpack5;
-
-  UintR Ti;
-  for (Ti = 6; Ti < TpacketLen; Ti++) {
-    localHostptr.p->packedWordsTCINDXCONF[TcurrLen + Ti] = 
-          regApiPtr->tcIndxSendArray[Ti - 6];
-  }//for
-  localHostptr.p->packedWordsTCINDXCONF[TcurrLen + TpacketLen] = Tpack6;
-
-  if (unlikely(!ndb_check_micro_gcp(getNodeInfo(localHostptr.i).m_version)))
-  {
-    jam();
-    ndbassert(Tpack6 == 0 || getNodeInfo(localHostptr.i).m_version == 0);
-  }
-}//Dbtc::sendTcIndxConf()
-
 void Dbtc::execINDXKEYINFO(Signal* signal)
 {
   jamEntry();
@@ -13842,7 +14073,7 @@ Dbtc::saveINDXKEYINFO(Signal* signal,
     releaseIndexOperation(apiConnectptr.p, indexOp);
     terrorCode = 289;
     if(TcKeyReq::getExecuteFlag(indexOp->tcIndxReq.requestInfo))
-      apiConnectptr.p->m_exec_flag= 1;
+      apiConnectptr.p->m_flags |= ApiConnectRecord::TF_EXEC_FLAG;
     abortErrorLab(signal);
     return -1;
   }
@@ -13884,7 +14115,7 @@ Dbtc::saveINDXATTRINFO(Signal* signal,
     releaseIndexOperation(apiConnectptr.p, indexOp);
     terrorCode = 289;
     if(TcKeyReq::getExecuteFlag(indexOp->tcIndxReq.requestInfo))
-      apiConnectptr.p->m_exec_flag= 1;
+      apiConnectptr.p->m_flags |= ApiConnectRecord::TF_EXEC_FLAG;
     abortErrorLab(signal);
     return -1;
   }
@@ -14071,7 +14302,7 @@ void Dbtc::execTCKEYCONF(Signal* signal)
   /**
    * Check on TCKEYCONF whether the the transaction was committed
    */
-  Uint32 Tcommit = TcKeyConf::getCommitFlag(confInfo);
+  ndbassert(TcKeyConf::getCommitFlag(confInfo) == false);
 
   indexOpPtr.p = indexOp;
   if (!indexOp) {
@@ -14124,28 +14355,6 @@ void Dbtc::execTCKEYCONF(Signal* signal)
     executeIndexOperation(signal, regApiPtr, indexOp);
     break;
   }
-  case(IOS_INDEX_OPERATION): {
-    // We are done, send TCINDXCONF
-    jam();    
-    Uint32 Ttcindxrec = regApiPtr->tcindxrec;
-    // Copy reply from TcKeyConf
-
-    ndbassert(regApiPtr->noIndexOp);
-    regApiPtr->noIndexOp--; // Decrease count
-    regApiPtr->tcIndxSendArray[Ttcindxrec] = indexOp->tcIndxReq.senderData;
-    regApiPtr->tcIndxSendArray[Ttcindxrec + 1] = 
-      tcKeyConf->operations[0].attrInfoLen;
-    regApiPtr->tcindxrec = Ttcindxrec + 2;
-    if (regApiPtr->noIndexOp == 0) {
-      jam();
-      sendTcIndxConf(signal, Tcommit);
-    } else if (regApiPtr->tcindxrec == ZTCOPCONF_SIZE) {
-      jam();
-      sendTcIndxConf(signal, 0);
-    }
-    releaseIndexOperation(regApiPtr, indexOp);
-    break;
-  }
   }
 }
 
@@ -14176,23 +14385,11 @@ void Dbtc::execTCKEYREF(Signal* signal)
   case(IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI):
   case(IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF): {
     jam();    
-    /**
-     * Increase count as it will be decreased below...
-     *   (and the code is written to handle failing lookup on "real" table
-     *    not lookup on index table)
-     */
-    regApiPtr->noIndexOp++;
-    // else continue
-  }
-  case(IOS_INDEX_OPERATION): {
     // Send TCINDXREF 
     
-    jam();
     TcKeyReq * const tcIndxReq = &indexOp->tcIndxReq;
     TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
     
-    ndbassert(regApiPtr->noIndexOp);
-    regApiPtr->noIndexOp--; // Decrease count
     tcIndxRef->connectPtr = tcIndxReq->senderData;
     tcIndxRef->transId[0] = tcKeyRef->transId[0];
     tcIndxRef->transId[1] = tcKeyRef->transId[1];
@@ -14388,20 +14585,6 @@ void Dbtc::execTRANSID_AI(Signal* signal
     // else continue waiting for more TRANSID_AI
     break;
   }
-  case(IOS_INDEX_OPERATION): {
-    // Should never receive TRANSID_AI in this state!!
-    jam();    
-    TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
-
-    tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
-    tcIndxRef->transId[0] = regApiPtr->transid[0];
-    tcIndxRef->transId[1] = regApiPtr->transid[1];
-    tcIndxRef->errorCode = 4349;
-    tcIndxRef->errorData = regApiPtr->errorData;
-    sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal, 
-	       TcKeyRef::SignalLength, JBB);
-    return;
-  }
   }
 }
 
@@ -14597,10 +14780,8 @@ void Dbtc::executeIndexOperation(Signal*
     tcKeyReq->scanInfo = indexOp->fragmentId; // As read from Index table
     TcKeyReq::setDistributionKeyFlag(tcKeyRequestInfo, 1U);
   }
-  indexOp->indexOpState = IOS_INDEX_OPERATION;
-  regApiPtr->m_special_op_flags = TcConnectRecord::SOF_INDEX_BASE_TABLE_ACCESS;
-  regApiPtr->executingIndexOp = indexOp->indexOpId;;
-  regApiPtr->noIndexOp++; // Increase count
+  regApiPtr->m_special_op_flags = 0;
+  regApiPtr->executingIndexOp = 0;
 
   /* KeyInfo section
    * Get the KeyInfo we received from the index table lookup
@@ -14631,6 +14812,9 @@ void Dbtc::executeIndexOperation(Signal*
     signal->header.m_noOfSections = 2;
     indexOp->attrInfoSectionIVal = RNIL;
   }
+
+  releaseIndexOperation(regApiPtr, indexOp);
+
   TcKeyReq::setKeyLength(tcKeyRequestInfo, keyInfoFromTransIdAI.sz);
   TcKeyReq::setAIInTcKeyReq(tcKeyRequestInfo, 0);
   TcKeyReq::setCommitFlag(tcKeyRequestInfo, 0);
@@ -14658,12 +14842,27 @@ void Dbtc::executeIndexOperation(Signal*
   const Uint32 currSavePointId = regApiPtr->currSavePointId;
   regApiPtr->currSavePointId = tmp.p->savePointId;
 
+#ifdef ERROR_INSERT
+  bool err8072 = ERROR_INSERTED(8072);
+  if (err8072)
+  {
+    CLEAR_ERROR_INSERT_VALUE;
+  }
+#endif
+
   /* Execute TCKEYREQ now - it is now responsible for freeing
    * the KeyInfo and AttrInfo sections 
    */
   EXECUTE_DIRECT(DBTC, GSN_TCKEYREQ, signal, TcKeyReq::StaticLength);
   jamEntry();
-  
+
+#ifdef ERROR_INSERT
+  if (err8072)
+  {
+    SET_ERROR_INSERT_VALUE(8072);
+  }
+#endif
+
   if (unlikely(regApiPtr->apiConnectstate == CS_ABORTING))
   {
     // TODO : Presumably the abort cleans up the operation
@@ -14743,6 +14942,31 @@ void Dbtc::saveTriggeringOpState(Signal*
                 LqhKeyConf::SignalLength);  
 }
 
+void
+Dbtc::trigger_op_finished(Signal* signal, ApiConnectRecordPtr regApiPtr,
+                          TcConnectRecord* triggeringOp)
+{
+  if (!regApiPtr.p->isExecutingDeferredTriggers())
+  {
+    ndbassert(triggeringOp->triggerExecutionCount > 0);
+    triggeringOp->triggerExecutionCount--;
+    if (triggeringOp->triggerExecutionCount == 0)
+    {
+      /**
+       * We have completed current trigger execution
+       * Continue triggering operation
+       */
+      jam();
+      continueTriggeringOp(signal, triggeringOp);
+    }
+  }
+  else
+  {
+    jam();
+    lqhKeyConf_checkTransactionState(signal, regApiPtr);
+  }
+}
+
 void Dbtc::continueTriggeringOp(Signal* signal, TcConnectRecord* trigOp)
 {
   LqhKeyConf * lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
@@ -14750,6 +14974,9 @@ void Dbtc::continueTriggeringOp(Signal* 
                 (UintR*)lqhKeyConf,
 		LqhKeyConf::SignalLength);
 
+  ndbassert(trigOp->savedState[LqhKeyConf::SignalLength-1] != ~Uint32(0));
+  trigOp->savedState[LqhKeyConf::SignalLength-1] = ~Uint32(0);
+
   lqhKeyConf->noFiredTriggers = 0;
   trigOp->noReceivedTriggers = 0;
 
@@ -14757,18 +14984,6 @@ void Dbtc::continueTriggeringOp(Signal* 
   execLQHKEYCONF(signal);
 }
 
-void Dbtc::scheduleFiredTrigger(ApiConnectRecordPtr* transPtr,
-                                TcConnectRecordPtr* opPtr)
-{
-  // Set initial values for trigger fireing operation
-  opPtr->p->triggerExecutionCount++;
-
-  // Insert fired trigger in execution queue
-  transPtr->p->theFiredTriggers.add(opPtr->p->accumulatingTriggerData);
-  opPtr->p->accumulatingTriggerData.i = RNIL;
-  opPtr->p->accumulatingTriggerData.p = NULL;
-}
-
 void Dbtc::executeTriggers(Signal* signal, ApiConnectRecordPtr* transPtr)
 {
   ApiConnectRecord* regApiPtr = transPtr->p;
@@ -14779,7 +14994,10 @@ void Dbtc::executeTriggers(Signal* signa
   if (!regApiPtr->theFiredTriggers.isEmpty()) {
     jam();
     if ((regApiPtr->apiConnectstate == CS_STARTED) ||
-        (regApiPtr->apiConnectstate == CS_START_COMMITTING)) {
+        (regApiPtr->apiConnectstate == CS_START_COMMITTING) ||
+        (regApiPtr->apiConnectstate == CS_SEND_FIRE_TRIG_REQ) ||
+        (regApiPtr->apiConnectstate == CS_WAIT_FIRE_TRIG_REQ))
+    {
       jam();
       regApiPtr->theFiredTriggers.first(trigPtr);
       while (trigPtr.i != RNIL) {
@@ -14789,7 +15007,8 @@ void Dbtc::executeTriggers(Signal* signa
         ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
 	FiredTriggerPtr nextTrigPtr = trigPtr;
 	regApiPtr->theFiredTriggers.next(nextTrigPtr);
-        if (opPtr.p->noReceivedTriggers == opPtr.p->noFiredTriggers) {
+        if (opPtr.p->noReceivedTriggers == opPtr.p->noFiredTriggers ||
+            regApiPtr->isExecutingDeferredTriggers()) {
           jam();
           // Fireing operation is ready to have a trigger executing
           executeTrigger(signal, trigPtr.p, transPtr, &opPtr);
@@ -14822,9 +15041,11 @@ void Dbtc::executeTriggers(Signal* signa
       {
         // Wait until transaction is ready to execute a trigger
         jam();
-        if (!regApiPtr->triggerPending) {
+        if (!tc_testbit(regApiPtr->m_flags,
+                        ApiConnectRecord::TF_TRIGGER_PENDING))
+        {
           jam();
-          regApiPtr->triggerPending = true;
+          regApiPtr->m_flags |= ApiConnectRecord::TF_TRIGGER_PENDING;
           signal->theData[0] = TcContinueB::TRIGGER_PENDING;
           signal->theData[1] = transPtr->i;
           signal->theData[2] = regApiPtr->transid[0];
@@ -14856,6 +15077,7 @@ void Dbtc::executeTrigger(Signal* signal
        c_theDefinedTriggers.getPtr(firedTriggerData->triggerId)) 
       != NULL)
   {
+    transPtr->p->pendingTriggers--;
     switch(firedTriggerData->triggerType) {
     case(TriggerType::SECONDARY_INDEX):
       jam();
@@ -14895,8 +15117,8 @@ void Dbtc::executeIndexTrigger(Signal* s
   }
   case(TriggerEvent::TE_UPDATE): {
     jam();
-    deleteFromIndexTable(signal, firedTriggerData, transPtr, opPtr, 
-			 indexData, true); // Hold the triggering operation
+    opPtr->p->triggerExecutionCount++; // One is already added...and this is 2
+    deleteFromIndexTable(signal, firedTriggerData, transPtr, opPtr, indexData);
     insertIntoIndexTable(signal, firedTriggerData, transPtr, opPtr, indexData);
     break;
   }
@@ -15019,8 +15241,7 @@ void Dbtc::insertIntoIndexTable(Signal* 
                                 TcFiredTriggerData* firedTriggerData, 
                                 ApiConnectRecordPtr* transPtr,
                                 TcConnectRecordPtr* opPtr,
-                                TcIndexData* indexData,
-                                bool holdOperation)
+                                TcIndexData* indexData)
 {
   ApiConnectRecord* regApiPtr = transPtr->p;
   TcConnectRecord* opRecord = opPtr->p;
@@ -15034,10 +15255,6 @@ void Dbtc::insertIntoIndexTable(Signal* 
   ptrCheckGuard(indexTabPtr, ctabrecFilesize, tableRecord);
   tcKeyReq->apiConnectPtr = transPtr->i;
   tcKeyReq->senderData = opPtr->i;
-  if (holdOperation) {
-    jam();
-    opRecord->triggerExecutionCount++;
-  }//if
 
   /* Key for insert to unique index table is the afterValues from the
    * base table operation (from update or insert on base).
@@ -15050,6 +15267,15 @@ void Dbtc::insertIntoIndexTable(Signal* 
   LocalDataBuffer<11> afterValues(pool, firedTriggerData->afterValues);
   LocalDataBuffer<11> keyValues(pool, firedTriggerData->keyValues);
 
+  if (afterValues.getSize() == 0)
+  {
+    jam();
+    ndbrequire(tc_testbit(regApiPtr->m_flags,
+                          ApiConnectRecord::TF_DEFERRED_CONSTRAINTS));
+    trigger_op_finished(signal, *transPtr, opRecord);
+    return;
+  }
+
   Uint32 keyIVal= RNIL;
   Uint32 attrIVal= RNIL;
   bool appendOk= false;
@@ -15082,15 +15308,7 @@ void Dbtc::insertIntoIndexTable(Signal* 
     {
       jam();
       releaseSection(keyIVal);
-      opRecord->triggerExecutionCount--;
-      if (opRecord->triggerExecutionCount == 0) {
-        /*
-          We have completed current trigger execution
-          Continue triggering operation
-        */
-        jam();
-        continueTriggeringOp(signal, opRecord);	
-      }//if
+      trigger_op_finished(signal, *transPtr, opRecord);
       return;
     }
     
@@ -15184,8 +15402,7 @@ void Dbtc::deleteFromIndexTable(Signal* 
                                 TcFiredTriggerData* firedTriggerData, 
                                 ApiConnectRecordPtr* transPtr,
                                 TcConnectRecordPtr* opPtr,
-                                TcIndexData* indexData,
-                                bool holdOperation)
+                                TcIndexData* indexData)
 {
   ApiConnectRecord* regApiPtr = transPtr->p;
   TcConnectRecord* opRecord = opPtr->p;
@@ -15197,10 +15414,7 @@ void Dbtc::deleteFromIndexTable(Signal* 
   ptrCheckGuard(indexTabPtr, ctabrecFilesize, tableRecord);
   tcKeyReq->apiConnectPtr = transPtr->i;
   tcKeyReq->senderData = opPtr->i;
-  if (holdOperation) {
-    jam();
-    opRecord->triggerExecutionCount++;
-  }//if
+
   // Calculate key length and renumber attribute id:s
   AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
   LocalDataBuffer<11> beforeValues(pool, firedTriggerData->beforeValues);
@@ -15208,7 +15422,16 @@ void Dbtc::deleteFromIndexTable(Signal* 
   Uint32 keyIVal= RNIL;
   Uint32 attrId= 0;
   bool hasNull= false;
-  
+
+  if (beforeValues.getSize() == 0)
+  {
+    jam();
+    ndbrequire(tc_testbit(regApiPtr->m_flags,
+                          ApiConnectRecord::TF_DEFERRED_CONSTRAINTS));
+    trigger_op_finished(signal, *transPtr, opRecord);
+    return;
+  }
+
   /* Build Delete KeyInfo section from beforevalues */
   if (unlikely((! appendAttrDataToSection(keyIVal,
                                           beforeValues,
@@ -15230,15 +15453,7 @@ void Dbtc::deleteFromIndexTable(Signal* 
   {
     jam();
     releaseSection(keyIVal);
-    opRecord->triggerExecutionCount--;
-    if (opRecord->triggerExecutionCount == 0) {
-      /*
-        We have completed current trigger execution
-        Continue triggering operation
-      */
-      jam();
-      continueTriggeringOp(signal, opRecord);	
-    }//if
+    trigger_op_finished(signal, *transPtr, opRecord);
     return;
   }
 

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp	2010-09-24 18:19:07 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp	2011-04-05 19:12:29 +0000
@@ -954,7 +954,10 @@ ArrayPool<TupTriggerData> c_triggerPool;
       subscriptionDeleteTriggers(triggerPool),
       subscriptionUpdateTriggers(triggerPool),
       constraintUpdateTriggers(triggerPool),
-      tuxCustomTriggers(triggerPool)
+      tuxCustomTriggers(triggerPool),
+      deferredInsertTriggers(triggerPool),
+      deferredDeleteTriggers(triggerPool),
+      deferredUpdateTriggers(triggerPool)
       {}
     
     Bitmask<MAXNROFATTRIBUTESINWORDS> notNullAttributeMask;
@@ -1084,7 +1087,10 @@ ArrayPool<TupTriggerData> c_triggerPool;
     DLList<TupTriggerData> subscriptionDeleteTriggers;
     DLList<TupTriggerData> subscriptionUpdateTriggers;
     DLList<TupTriggerData> constraintUpdateTriggers;
-    
+    DLList<TupTriggerData> deferredInsertTriggers;
+    DLList<TupTriggerData> deferredUpdateTriggers;
+    DLList<TupTriggerData> deferredDeleteTriggers;
+
     // List of ordered indexes
     DLList<TupTriggerData> tuxCustomTriggers;
     
@@ -1496,19 +1502,31 @@ typedef Ptr<HostBuffer> HostBufferPtr;
     STATIC_CONST( SZ32 = 1 );
   };
 
+  enum When
+  {
+    KRS_PREPARE = 0,
+    KRS_COMMIT = 1,
+    KRS_PRE_COMMIT0 = 2, // There can be multiple pre commit phases...
+    KRS_PRE_COMMIT1 = 3
+  };
+
 struct KeyReqStruct {
 
-  KeyReqStruct(EmulatedJamBuffer * _jamBuffer) {
+  KeyReqStruct(EmulatedJamBuffer * _jamBuffer, When when = KRS_PREPARE) {
 #if defined VM_TRACE || defined ERROR_INSERT
     memset(this, 0xf3, sizeof(* this));
 #endif
     jamBuffer = _jamBuffer;
+    m_when = when;
+    m_deferred_constraints = true;
   }
-  KeyReqStruct(Dbtup* tup) {
+  KeyReqStruct(Dbtup* tup, When when = KRS_PREPARE) {
 #if defined VM_TRACE || defined ERROR_INSERT
     memset(this, 0xf3, sizeof(* this));
 #endif
     jamBuffer = tup->jamBuffer();
+    m_when = when;
+    m_deferred_constraints = true;
   }
   
 /**
@@ -1555,6 +1573,7 @@ struct KeyReqStruct {
   /* Flag: is tuple in expanded or in shrunken/stored format? */
   bool is_expanded;
   bool m_is_lcp;
+  enum When m_when;
 
   struct Var_data {
     /*
@@ -1602,6 +1621,7 @@ struct KeyReqStruct {
   bool            last_row;
   bool            m_use_rowid;
   Uint8           m_reorg;
+  bool            m_deferred_constraints;
 
   Signal*         signal;
   Uint32 no_fired_triggers;
@@ -1973,6 +1993,12 @@ private:
 //------------------------------------------------------------------
   void execDROP_TRIG_IMPL_REQ(Signal* signal);
 
+  /**
+   * Deferred triggers execute when execFIRE_TRIG_REQ
+   *   is called
+   */
+  void execFIRE_TRIG_REQ(Signal* signal);
+
 // *****************************************************************
 // Setting up the environment for reads, inserts, updates and deletes.
 // *****************************************************************
@@ -2551,11 +2577,11 @@ private:
                                     Tablerec* tablePtr,
                                     bool disk);
 
-#if 0
-  void checkDeferredTriggers(Signal* signal, 
+  void checkDeferredTriggers(KeyReqStruct *req_struct,
                              Operationrec* regOperPtr,
-                             Tablerec* regTablePtr);
-#endif
+                             Tablerec* regTablePtr,
+                             bool disk);
+
   void checkDetachedTriggers(KeyReqStruct *req_struct,
                              Operationrec* regOperPtr,
                              Tablerec* regTablePtr,
@@ -2566,9 +2592,19 @@ private:
                              Operationrec* regOperPtr,
                              bool disk);
 
+  void checkDeferredTriggersDuringPrepare(KeyReqStruct *req_struct,
+                                          DLList<TupTriggerData>& triggerList,
+                                          Operationrec* const regOperPtr,
+                                          bool disk);
   void fireDeferredTriggers(KeyReqStruct *req_struct,
                             DLList<TupTriggerData>& triggerList,
-                            Operationrec* regOperPtr);
+                            Operationrec* const regOperPtr,
+                            bool disk);
+
+  void fireDeferredConstraints(KeyReqStruct *req_struct,
+                               DLList<TupTriggerData>& triggerList,
+                               Operationrec* const regOperPtr,
+                               bool disk);
 
   void fireDetachedTriggers(KeyReqStruct *req_struct,
                             DLList<TupTriggerData>& triggerList,

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp	2010-09-03 05:35:51 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp	2011-04-05 19:12:29 +0000
@@ -501,7 +501,7 @@ void Dbtup::execTUP_COMMITREQ(Signal* si
   FragrecordPtr regFragPtr;
   OperationrecPtr regOperPtr;
   TablerecPtr regTabPtr;
-  KeyReqStruct req_struct(this);
+  KeyReqStruct req_struct(this, KRS_COMMIT);
   TransState trans_state;
   Uint32 no_of_fragrec, no_of_tablerec;
 

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp	2010-09-07 06:44:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp	2011-04-05 19:12:29 +0000
@@ -251,6 +251,7 @@ Dbtup::setup_read(KeyReqStruct *req_stru
            (req_struct->m_reorg == 2 && moved != 0)))
     {
       terrorCode= ZTUPLE_DELETED_ERROR;
+      jam();
       return false;
     }
   }
@@ -289,6 +290,7 @@ Dbtup::setup_read(KeyReqStruct *req_stru
     if((found && currOp == ZDELETE) || 
        ((dirty || !found) && currOp == ZINSERT))
     {
+      jam();
       terrorCode= ZTUPLE_DELETED_ERROR;
       break;
     }
@@ -597,9 +599,11 @@ void Dbtup::execTUPKEYREQ(Signal* signal
 
    sig1 = tupKeyReq->m_row_id_page_no;
    sig2 = tupKeyReq->m_row_id_page_idx;
+   sig3 = tupKeyReq->deferred_constraints;
 
    req_struct.m_row_id.m_page_no = sig1;
    req_struct.m_row_id.m_page_idx = sig2;
+   req_struct.m_deferred_constraints = sig3;
 
    /* Get AttrInfo section if this is a long TUPKEYREQ */
    Uint32 attrInfoIVal= tupKeyReq->attrInfoIVal;
@@ -1784,6 +1788,7 @@ int Dbtup::handleDeleteReq(Signal* signa
   {
     regOperPtr->tupVersion= req_struct->m_tuple_ptr->get_tuple_version();
   }
+  req_struct->changeMask.set();
 
   if(disk && regOperPtr->m_undo_buffer_space == 0)
   {

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp	2010-09-07 06:44:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp	2011-04-05 19:12:29 +0000
@@ -129,6 +129,8 @@ Dbtup::Dbtup(Block_context& ctx, Uint32 
   addRecSignal(GSN_DROP_FRAG_REQ, &Dbtup::execDROP_FRAG_REQ);
   addRecSignal(GSN_SUB_GCP_COMPLETE_REP, &Dbtup::execSUB_GCP_COMPLETE_REP);
 
+  addRecSignal(GSN_FIRE_TRIG_REQ, &Dbtup::execFIRE_TRIG_REQ);
+
   fragoperrec = 0;
   fragrecord = 0;
   alterTabOperRec = 0;

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp	2010-09-07 09:54:47 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp	2011-04-05 19:12:29 +0000
@@ -476,6 +476,21 @@ Dbtup::execACCKEYCONF(Signal* signal)
   jamEntry();
   ScanOpPtr scanPtr;
   scanPtr.i = signal->theData[0];
+
+  Uint32 localKey1 = signal->theData[3];
+  Uint32 localKey2 = signal->theData[4];
+  Uint32 localKeyFlag = signal->theData[5];
+  Local_key tmp;
+  if (localKeyFlag == 1)
+  {
+    tmp.assref(localKey1);
+  }
+  else
+  {
+    tmp.m_page_no = localKey1;
+    tmp.m_page_idx = localKey2;
+  }
+
   c_scanOpPool.getPtr(scanPtr);
   ScanOp& scan = *scanPtr.p;
   ndbrequire(scan.m_bits & ScanOp::SCAN_LOCK_WAIT && scan.m_accLockOp != RNIL);
@@ -483,10 +498,37 @@ Dbtup::execACCKEYCONF(Signal* signal)
   if (scan.m_state == ScanOp::Blocked) {
     // the lock wait was for current entry
     jam();
-    scan.m_state = ScanOp::Locked;
-    // LQH has the ball
-    return;
+
+    if (likely(scan.m_scanPos.m_key_mm.m_page_no == tmp.m_page_no &&
+               scan.m_scanPos.m_key_mm.m_page_idx == tmp.m_page_idx))
+    {
+      jam();
+      scan.m_state = ScanOp::Locked;
+      // LQH has the ball
+      return;
+    }
+    else
+    {
+      jam();
+      /**
+       * This means that there was DEL/INS on rowid that we tried to lock
+       *   and the primary key that was previously located on this rowid
+       *   (scanPos.m_key_mm) has moved.
+       *   (DBACC keeps of track of primary keys)
+       *
+       * We don't care about the primary keys, but is interested in ROWID
+       *   so rescan this position.
+       *   Which is implemented by using execACCKEYREF...
+       */
+      ndbout << "execACCKEYCONF "
+             << scan.m_scanPos.m_key_mm
+             << " != " << tmp << " ";
+      scan.m_bits |= ScanOp::SCAN_LOCK_WAIT;
+      execACCKEYREF(signal);
+      return;
+    }
   }
+
   if (scan.m_state != ScanOp::Aborting) {
     // we were moved, release lock
     jam();

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp	2010-09-03 05:35:51 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp	2011-04-05 19:12:29 +0000
@@ -32,6 +32,7 @@
 #include <signaldata/DropTrig.hpp>
 #include <signaldata/DropTrigImpl.hpp>
 #include <signaldata/TuxMaint.hpp>
+#include "../dblqh/Dblqh.hpp"
 
 /* **************************************************************** */
 /* ---------------------------------------------------------------- */
@@ -503,6 +504,93 @@ Dbtup::dropTrigger(Tablerec* table, cons
   return 0;
 }//Dbtup::dropTrigger()
 
+void
+Dbtup::execFIRE_TRIG_REQ(Signal* signal)
+{
+  jam();
+  Uint32 opPtrI = signal->theData[0];
+  Uint32 pass = signal->theData[5];
+
+  FragrecordPtr regFragPtr;
+  OperationrecPtr regOperPtr;
+  TablerecPtr regTabPtr;
+  KeyReqStruct req_struct(this, (When)(KRS_PRE_COMMIT0 + pass));
+
+  regOperPtr.i = opPtrI;
+
+  jamEntry();
+
+  c_operation_pool.getPtr(regOperPtr);
+
+  regFragPtr.i = regOperPtr.p->fragmentPtr;
+  Uint32 no_of_fragrec = cnoOfFragrec;
+  ptrCheckGuard(regFragPtr, no_of_fragrec, fragrecord);
+
+  TransState trans_state = get_trans_state(regOperPtr.p);
+  ndbrequire(trans_state == TRANS_STARTED);
+
+  Uint32 no_of_tablerec = cnoOfTablerec;
+  regTabPtr.i = regFragPtr.p->fragTableId;
+  ptrCheckGuard(regTabPtr, no_of_tablerec, tablerec);
+
+  req_struct.signal = signal;
+  req_struct.TC_ref = signal->theData[1];
+  req_struct.TC_index = signal->theData[2];
+  req_struct.trans_id1 = signal->theData[3];
+  req_struct.trans_id2 = signal->theData[4];
+
+  PagePtr page;
+  Tuple_header* tuple_ptr = (Tuple_header*)
+    get_ptr(&page, &regOperPtr.p->m_tuple_location, regTabPtr.p);
+  req_struct.m_tuple_ptr = tuple_ptr;
+
+  OperationrecPtr lastOperPtr;
+  lastOperPtr.i = tuple_ptr->m_operation_ptr_i;
+  c_operation_pool.getPtr(lastOperPtr);
+
+  /**
+   * Deferred triggers should fire only once per primary key (per pass)
+   *   regardless of no of DML on that primary key
+   *
+   * We keep track of this on *last* operation (which btw, implies that
+   *   a trigger can't update "own" tuple...i.e first op would be better...)
+   *
+   */
+  if (!c_lqh->check_fire_trig_pass(lastOperPtr.p->userpointer, pass))
+  {
+    jam();
+    signal->theData[0] = 0;
+    signal->theData[1] = 0;
+    return;
+  }
+
+  /**
+   * This is deferred triggers...
+   *   which is basically the same as detached,
+   *     i.e before value is <before transaction>
+   *     and after values is <after transaction>
+   *   with the difference that they execute (fire) while
+   *   still having a transaction context...
+   *   i.e can abort transactions, modify transaction
+   */
+  req_struct.no_fired_triggers = 0;
+
+  /**
+   * See DbtupCommit re "Setting the op-list has this effect"
+   */
+  Uint32 save[2] = { lastOperPtr.p->nextActiveOp, lastOperPtr.p->prevActiveOp };
+  lastOperPtr.p->nextActiveOp = RNIL;
+  lastOperPtr.p->prevActiveOp = RNIL;
+
+  checkDeferredTriggers(&req_struct, lastOperPtr.p, regTabPtr.p, false);
+
+  lastOperPtr.p->nextActiveOp = save[0];
+  lastOperPtr.p->prevActiveOp = save[1];
+
+  signal->theData[0] = 0;
+  signal->theData[1] = req_struct.no_fired_triggers;
+}
+
 /* ---------------------------------------------------------------- */
 /* -------------- checkImmediateTriggersAfterOp ------------------ */
 /*                                                                  */
@@ -522,13 +610,24 @@ Dbtup::checkImmediateTriggersAfterInsert
     return;
   }
 
-  if ((regOperPtr->op_struct.primary_replica) &&
-      (!(regTablePtr->afterInsertTriggers.isEmpty()))) {
-    jam();
-    fireImmediateTriggers(req_struct,
-                          regTablePtr->afterInsertTriggers,
-                          regOperPtr,
-                          disk);
+  if (regOperPtr->op_struct.primary_replica)
+  {
+    if (! regTablePtr->afterInsertTriggers.isEmpty())
+    {
+      jam();
+      fireImmediateTriggers(req_struct,
+                            regTablePtr->afterInsertTriggers,
+                            regOperPtr,
+                            disk);
+    }
+
+    if (! regTablePtr->deferredInsertTriggers.isEmpty())
+    {
+      checkDeferredTriggersDuringPrepare(req_struct,
+                                         regTablePtr->deferredInsertTriggers,
+                                         regOperPtr,
+                                         disk);
+    }
   }
 }
 
@@ -542,21 +641,34 @@ Dbtup::checkImmediateTriggersAfterUpdate
     return;
   }
 
-  if ((regOperPtr->op_struct.primary_replica) &&
-      (!(regTablePtr->afterUpdateTriggers.isEmpty()))) {
-    jam();
-    fireImmediateTriggers(req_struct,
-                          regTablePtr->afterUpdateTriggers,
-                          regOperPtr,
-                          disk);
-  }
-  if ((regOperPtr->op_struct.primary_replica) &&
-      (!(regTablePtr->constraintUpdateTriggers.isEmpty()))) {
-    jam();
-    fireImmediateTriggers(req_struct,
-                          regTablePtr->constraintUpdateTriggers,
-                          regOperPtr,
-                          disk);
+  if (regOperPtr->op_struct.primary_replica)
+  {
+    if (! regTablePtr->afterUpdateTriggers.isEmpty())
+    {
+      jam();
+      fireImmediateTriggers(req_struct,
+                            regTablePtr->afterUpdateTriggers,
+                            regOperPtr,
+                            disk);
+    }
+
+    if (! regTablePtr->constraintUpdateTriggers.isEmpty())
+    {
+      jam();
+      fireImmediateTriggers(req_struct,
+                            regTablePtr->constraintUpdateTriggers,
+                            regOperPtr,
+                            disk);
+    }
+
+    if (! regTablePtr->deferredUpdateTriggers.isEmpty())
+    {
+      jam();
+      checkDeferredTriggersDuringPrepare(req_struct,
+                                         regTablePtr->deferredUpdateTriggers,
+                                         regOperPtr,
+                                         disk);
+    }
   }
 }
 
@@ -570,17 +682,48 @@ Dbtup::checkImmediateTriggersAfterDelete
     return;
   }
 
-  if ((regOperPtr->op_struct.primary_replica) &&
-      (!(regTablePtr->afterDeleteTriggers.isEmpty()))) {
+  if (regOperPtr->op_struct.primary_replica)
+  {
+    if (! regTablePtr->afterDeleteTriggers.isEmpty())
+    {
+      fireImmediateTriggers(req_struct,
+                            regTablePtr->afterDeleteTriggers,
+                            regOperPtr,
+                            disk);
+    }
+
+    if (! regTablePtr->deferredDeleteTriggers.isEmpty())
+    {
+      checkDeferredTriggersDuringPrepare(req_struct,
+                                         regTablePtr->deferredDeleteTriggers,
+                                         regOperPtr,
+                                         disk);
+    }
+  }
+}
+
+void
+Dbtup::checkDeferredTriggersDuringPrepare(KeyReqStruct *req_struct,
+                                          DLList<TupTriggerData>& triggerList,
+                                          Operationrec* const regOperPtr,
+                                          bool disk)
+{
+  jam();
+  TriggerPtr trigPtr;
+  triggerList.first(trigPtr);
+  while (trigPtr.i != RNIL)
+  {
     jam();
-    executeTriggers(req_struct,
-                    regTablePtr->afterDeleteTriggers,
-                    regOperPtr,
-                    disk);
+    if (trigPtr.p->monitorAllAttributes ||
+        trigPtr.p->attributeMask.overlaps(req_struct->changeMask))
+    {
+      jam();
+      NoOfFiredTriggers::setDeferredBit(req_struct->no_fired_triggers);
+      return;
+    }
   }
 }
 
-#if 0
 /* ---------------------------------------------------------------- */
 /* --------------------- checkDeferredTriggers -------------------- */
 /*                                                                  */
@@ -590,14 +733,95 @@ Dbtup::checkImmediateTriggersAfterDelete
 /* Executes deferred triggers by sending FIRETRIGORD                */
 /*                                                                  */
 /* ---------------------------------------------------------------- */
-void Dbtup::checkDeferredTriggers(Signal* signal, 
-                                  Operationrec* const regOperPtr,
-                                  Tablerec* const regTablePtr)
+void Dbtup::checkDeferredTriggers(KeyReqStruct *req_struct,
+                                  Operationrec* regOperPtr,
+                                  Tablerec* regTablePtr,
+                                  bool disk)
 {
   jam();
-  // NYI
+  Uint32 save_type = regOperPtr->op_struct.op_type;
+  Tuple_header *save_ptr = req_struct->m_tuple_ptr;
+  DLList<TupTriggerData> * deferred_list = 0;
+  DLList<TupTriggerData> * constraint_list = 0;
+
+  switch (save_type) {
+  case ZUPDATE:
+  case ZINSERT:
+    req_struct->m_tuple_ptr =get_copy_tuple(&regOperPtr->m_copy_tuple_location);
+    break;
+  }
+
+  /**
+   * Set correct operation type and fix change mask
+   * Note ALLOC is set in "orig" tuple
+   */
+  if (save_ptr->m_header_bits & Tuple_header::ALLOC) {
+    if (save_type == ZDELETE) {
+      // insert + delete = nothing
+      jam();
+      return;
+      goto end;
+    }
+    regOperPtr->op_struct.op_type = ZINSERT;
+  }
+  else if (save_type == ZINSERT) {
+    /**
+     * Tuple was not created but last op is INSERT.
+     * This is possible only on DELETE + INSERT
+     */
+    regOperPtr->op_struct.op_type = ZUPDATE;
+  }
+
+  switch(regOperPtr->op_struct.op_type) {
+  case(ZINSERT):
+    jam();
+    deferred_list = &regTablePtr->deferredInsertTriggers;
+    constraint_list = &regTablePtr->afterInsertTriggers;
+    break;
+  case(ZDELETE):
+    jam();
+    deferred_list = &regTablePtr->deferredDeleteTriggers;
+    constraint_list = &regTablePtr->afterDeleteTriggers;
+    break;
+  case(ZUPDATE):
+    jam();
+    deferred_list = &regTablePtr->deferredUpdateTriggers;
+    constraint_list = &regTablePtr->afterUpdateTriggers;
+    break;
+  default:
+    ndbrequire(false);
+    break;
+  }
+
+  if (req_struct->m_deferred_constraints == false)
+  {
+    constraint_list = 0;
+  }
+
+  if (deferred_list->isEmpty() &&
+      (constraint_list == 0 || constraint_list->isEmpty()))
+  {
+    goto end;
+  }
+
+  /**
+   * Compute change-mask
+   */
+  set_commit_change_mask_info(regTablePtr, req_struct, regOperPtr);
+  if (!deferred_list->isEmpty())
+  {
+    fireDeferredTriggers(req_struct, * deferred_list, regOperPtr, disk);
+  }
+
+  if (constraint_list && !constraint_list->isEmpty())
+  {
+    fireDeferredConstraints(req_struct, * constraint_list, regOperPtr, disk);
+  }
+
+end:
+  regOperPtr->op_struct.op_type = save_type;
+  req_struct->m_tuple_ptr = save_ptr;
 }//Dbtup::checkDeferredTriggers()
-#endif
 
 /* ---------------------------------------------------------------- */
 /* --------------------- checkDetachedTriggers -------------------- */
@@ -696,6 +920,13 @@ end:
   req_struct->m_tuple_ptr = save_ptr;
 }
 
+static
+bool
+is_constraint(const Dbtup::TupTriggerData * trigPtr)
+{
+  return trigPtr->triggerType == TriggerType::SECONDARY_INDEX;
+}
+
 void 
 Dbtup::fireImmediateTriggers(KeyReqStruct *req_struct,
                              DLList<TupTriggerData>& triggerList, 
@@ -709,6 +940,38 @@ Dbtup::fireImmediateTriggers(KeyReqStruc
     if (trigPtr.p->monitorAllAttributes ||
         trigPtr.p->attributeMask.overlaps(req_struct->changeMask)) {
       jam();
+
+      if (req_struct->m_when == KRS_PREPARE &&
+          req_struct->m_deferred_constraints &&
+          is_constraint(trigPtr.p))
+      {
+        NoOfFiredTriggers::setDeferredBit(req_struct->no_fired_triggers);
+      }
+      else
+      {
+        executeTrigger(req_struct,
+                       trigPtr.p,
+                       regOperPtr,
+                       disk);
+      }
+    }
+    triggerList.next(trigPtr);
+  }//while
+}//Dbtup::fireImmediateTriggers()
+
+void
+Dbtup::fireDeferredConstraints(KeyReqStruct *req_struct,
+                               DLList<TupTriggerData>& triggerList,
+                               Operationrec* const regOperPtr,
+                               bool disk)
+{
+  TriggerPtr trigPtr;
+  triggerList.first(trigPtr);
+  while (trigPtr.i != RNIL) {
+    jam();
+    if (trigPtr.p->monitorAllAttributes ||
+        trigPtr.p->attributeMask.overlaps(req_struct->changeMask)) {
+      jam();
       executeTrigger(req_struct,
                      trigPtr.p,
                      regOperPtr,
@@ -716,14 +979,13 @@ Dbtup::fireImmediateTriggers(KeyReqStruc
     }//if
     triggerList.next(trigPtr);
   }//while
-}//Dbtup::fireImmediateTriggers()
+}//Dbtup::fireDeferredTriggers()
 
-#if 0
-void 
-Dbtup::fireDeferredTriggers(Signal* signal,
-                            KeyReqStruct *req_struct,
-                            DLList<TupTriggerData>& triggerList, 
-                            Operationrec* const regOperPtr)
+void
+Dbtup::fireDeferredTriggers(KeyReqStruct *req_struct,
+                            DLList<TupTriggerData>& triggerList,
+                            Operationrec* const regOperPtr,
+                            bool disk)
 {
   TriggerPtr trigPtr;
   triggerList.first(trigPtr);
@@ -733,13 +995,13 @@ Dbtup::fireDeferredTriggers(Signal* sign
         trigPtr.p->attributeMask.overlaps(req_struct->changeMask)) {
       jam();
       executeTrigger(req_struct,
-                     trigPtr,
-                     regOperPtr);
+                     trigPtr.p,
+                     regOperPtr,
+                     disk);
     }//if
     triggerList.next(trigPtr);
   }//while
 }//Dbtup::fireDeferredTriggers()
-#endif
 
 void 
 Dbtup::fireDetachedTriggers(KeyReqStruct *req_struct,
@@ -1044,6 +1306,44 @@ out:
     return;
   }
 
+  if (triggerType == TriggerType::SECONDARY_INDEX &&
+      req_struct->m_when != KRS_PREPARE)
+  {
+    ndbrequire(req_struct->m_deferred_constraints);
+    if (req_struct->m_when == KRS_PRE_COMMIT0)
+    {
+      switch(regOperPtr->op_struct.op_type){
+      case ZINSERT:
+        NoOfFiredTriggers::setDeferredBit(req_struct->no_fired_triggers);
+        return;
+        break;
+      case ZUPDATE:
+        NoOfFiredTriggers::setDeferredBit(req_struct->no_fired_triggers);
+        noAfterWords = 0;
+        break;
+      case ZDELETE:
+        break;
+      default:
+        ndbrequire(false);
+      }
+    }
+    else
+    {
+      ndbrequire(req_struct->m_when == KRS_PRE_COMMIT1);
+      switch(regOperPtr->op_struct.op_type){
+      case ZINSERT:
+        break;
+      case ZUPDATE:
+        noBeforeWords = 0;
+        break;
+      case ZDELETE:
+        return;
+      default:
+        ndbrequire(false);
+      }
+    }
+  }
+
   req_struct->no_fired_triggers++;
 
   if (longsignal == false)

=== modified file 'storage/ndb/src/ndbapi/NdbOperation.cpp'
--- a/storage/ndb/src/ndbapi/NdbOperation.cpp	2010-09-30 09:32:28 +0000
+++ b/storage/ndb/src/ndbapi/NdbOperation.cpp	2011-04-05 19:12:29 +0000
@@ -71,7 +71,8 @@ NdbOperation::NdbOperation(Ndb* aNdb, Nd
   m_abortOption(-1),
   m_noErrorPropagation(false),
   theLockHandle(NULL),
-  m_blob_lock_upgraded(false)
+  m_blob_lock_upgraded(false),
+  m_deferred_constraints(false)
 {
   theReceiver.init(NdbReceiver::NDB_OPERATION, false, this);
   theError.code = 0;
@@ -175,6 +176,7 @@ NdbOperation::init(const NdbTableImpl* t
   m_extraSetValues = NULL;
   m_numExtraSetValues = 0;
   m_use_any_value = 0;
+  m_deferred_constraints = false;
 
   tSignal = theNdb->getSignal();
   if (tSignal == NULL)

=== modified file 'storage/ndb/src/ndbapi/NdbOperationDefine.cpp'
--- a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp	2010-09-29 13:25:19 +0000
+++ b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp	2011-04-05 19:12:29 +0000
@@ -1386,5 +1386,14 @@ NdbOperation::handleOperationOptions (co
     }    
   }
 
+  if (opts->optionsPresent & OperationOptions::OO_DEFERRED_CONSTAINTS)
+  {
+    op->m_deferred_constraints = 1;
+  }
+  else
+  {
+    op->m_deferred_constraints = 0;
+  }
+
   return 0;
 }

=== modified file 'storage/ndb/src/ndbapi/NdbOperationExec.cpp'
--- a/storage/ndb/src/ndbapi/NdbOperationExec.cpp	2010-09-30 09:32:28 +0000
+++ b/storage/ndb/src/ndbapi/NdbOperationExec.cpp	2011-04-05 19:12:29 +0000
@@ -473,6 +473,7 @@ NdbOperation::prepareSend(Uint32 aTC_Con
   Uint8 tStartIndicator = theStartIndicator;
   Uint8 tInterpretIndicator = theInterpretIndicator;
   Uint8 tNoDisk = m_no_disk_flag;
+  Uint8 tDeferred = m_deferred_constraints;
 
   /**
    * A dirty read, can not abort the transaction
@@ -490,6 +491,7 @@ NdbOperation::prepareSend(Uint32 aTC_Con
   tcKeyReq->setStartFlag(tReqInfo, tStartIndicator);
   tcKeyReq->setInterpretedFlag(tReqInfo, tInterpretIndicator);
   tcKeyReq->setNoDiskFlag(tReqInfo, tNoDisk);
+  tcKeyReq->setDeferredConstraints(tReqInfo, tDeferred);
 
   OperationType tOperationType = theOperationType;
   Uint8 abortOption = (ao == DefaultAbortOption) ? (Uint8) m_abortOption : (Uint8) ao;
@@ -1416,12 +1418,16 @@ NdbOperation::prepareSendNdbRecord(Abort
   m_abortOption= theSimpleIndicator && theOperationType==ReadRequest ?
     (Uint8) AO_IgnoreError : (Uint8) abortOption;
 
+  Uint8 tDeferred = m_deferred_constraints;
+
   TcKeyReq::setAbortOption(tcKeyReq->requestInfo, m_abortOption);
   TcKeyReq::setCommitFlag(tcKeyReq->requestInfo, theCommitIndicator);
   TcKeyReq::setStartFlag(tcKeyReq->requestInfo, theStartIndicator);
   TcKeyReq::setSimpleFlag(tcKeyReq->requestInfo, theSimpleIndicator);
   TcKeyReq::setDirtyFlag(tcKeyReq->requestInfo, theDirtyIndicator);
 
+  TcKeyReq::setDeferredConstraints(tcKeyReq->requestInfo, tDeferred);
+
   theStatus= WaitResponse;
   theReceiver.prepareSend();
 

=== modified file 'storage/ndb/test/include/HugoCalculator.hpp'
--- a/storage/ndb/test/include/HugoCalculator.hpp	2009-10-06 10:39:02 +0000
+++ b/storage/ndb/test/include/HugoCalculator.hpp	2011-04-05 19:12:29 +0000
@@ -44,6 +44,9 @@ public:
   int getUpdatesValue(NDBT_ResultRow* const pRow) const;
   int isIdCol(int colId) { return m_idCol == colId; };
   int isUpdateCol(int colId){ return m_updatesCol == colId; };
+
+  int equalForRow(Uint8 *, const NdbRecord*, int rowid);
+  int setValues(Uint8*, const NdbRecord*, int rowid, int updateval);
 private:
   const NdbDictionary::Table& m_tab;
   int m_idCol;

=== modified file 'storage/ndb/test/ndbapi/testIndex.cpp'
--- a/storage/ndb/test/ndbapi/testIndex.cpp	2010-09-24 18:19:07 +0000
+++ b/storage/ndb/test/ndbapi/testIndex.cpp	2011-04-05 19:12:29 +0000
@@ -2720,6 +2720,361 @@ runBug56829(NDBT_Context* ctx, NDBT_Step
   return result;
 }
 
+#define CHK_RET_FAILED(x) if (!(x)) { ndbout_c("Failed on line: %u", __LINE__); return NDBT_FAILED; }
+
+int
+runBug12315582(NDBT_Context* ctx, NDBT_Step* step)
+{
+  const NdbDictionary::Table * pTab = ctx->getTab();
+  Ndb* pNdb = GETNDB(step);
+  NdbDictionary::Dictionary * dict = pNdb->getDictionary();
+
+  const NdbDictionary::Index* pIdx= dict->getIndex(pkIdxName, pTab->getName());
+  CHK_RET_FAILED(pIdx != 0);
+
+  const NdbRecord * pRowRecord = pTab->getDefaultRecord();
+  CHK_RET_FAILED(pRowRecord != 0);
+  const NdbRecord * pIdxRecord = pIdx->getDefaultRecord();
+  CHK_RET_FAILED(pIdxRecord != 0);
+
+  const Uint32 len = NdbDictionary::getRecordRowLength(pRowRecord);
+  Uint8 * pRow = new Uint8[len];
+  bzero(pRow, len);
+
+  HugoCalculator calc(* pTab);
+  calc.equalForRow(pRow, pRowRecord, 0);
+
+  NdbTransaction* pTrans = pNdb->startTransaction();
+  CHK_RET_FAILED(pTrans != 0);
+
+  const NdbOperation * pOp[2] = { 0, 0 };
+  for (Uint32 i = 0; i<2; i++)
+  {
+    NdbInterpretedCode code;
+    if (i == 0)
+      code.interpret_exit_ok();
+    else
+      code.interpret_exit_nok();
+
+    code.finalise();
+
+    NdbOperation::OperationOptions opts;
+    bzero(&opts, sizeof(opts));
+    opts.optionsPresent = NdbOperation::OperationOptions::OO_INTERPRETED;
+    opts.interpretedCode = &code;
+
+    pOp[i] = pTrans->readTuple(pIdxRecord, (char*)pRow,
+                               pRowRecord, (char*)pRow,
+                               NdbOperation::LM_Read,
+                               0,
+                               &opts,
+                               sizeof(opts));
+    CHK_RET_FAILED(pOp[i]);
+  }
+
+  int res = pTrans->execute(Commit, AO_IgnoreError);
+
+  CHK_RET_FAILED(res == 0);
+  CHK_RET_FAILED(pOp[0]->getNdbError().code == 0);
+  CHK_RET_FAILED(pOp[1]->getNdbError().code != 0);
+
+  delete [] pRow;
+
+  return NDBT_OK;
+}
+
+static
+const int
+deferred_errors[] = {
+  5064, 0,
+  5065, 0,
+  5066, 0,
+  5067, 0,
+  5068, 0,
+  5069, 0,
+  5070, 0,
+  5071, 0,
+  5072, 1,
+  8090, 0,
+  8091, 0,
+  8092, 2, // connected tc
+  0, 0 // trailer
+};
+
+int
+runTestDeferredError(NDBT_Context* ctx, NDBT_Step* step)
+{
+  NdbRestarter res;
+  Ndb* pNdb = GETNDB(step);
+  const NdbDictionary::Table* pTab = ctx->getTab();
+
+  const int rows = ctx->getNumRecords();
+
+  const NdbRecord * pRowRecord = pTab->getDefaultRecord();
+  CHK_RET_FAILED(pRowRecord != 0);
+
+  const Uint32 len = NdbDictionary::getRecordRowLength(pRowRecord);
+  Uint8 * pRow = new Uint8[len];
+
+  for (int i = 0; deferred_errors[i] != 0; i += 2)
+  {
+    const int errorno = deferred_errors[i];
+    const int nodefail = deferred_errors[i+1];
+
+    for (int j = 0; j<3; j++)
+    {
+      NdbTransaction* pTrans = pNdb->startTransaction();
+      CHK_RET_FAILED(pTrans != 0);
+
+      int nodeId =
+        nodefail == 0 ? 0 :
+        nodefail == 1 ? res.getNode(NdbRestarter::NS_RANDOM) :
+        nodefail == 2 ? pTrans->getConnectedNodeId() :
+        0;
+
+      ndbout_c("errorno: %u(nf: %u - %u) j: %u : %s", errorno,
+               nodefail, nodeId, j,
+               j == 0 ? "test before error insert" :
+               j == 1 ? "test with error insert" :
+               j == 2 ? "test after error insert" :
+               "");
+      if (j == 0 || j == 2)
+      {
+        // First time succeed
+        // Last time succeed
+      }
+      else if (nodefail == 0)
+      {
+        CHK_RET_FAILED(res.insertErrorInAllNodes(errorno) == 0);
+      }
+      else
+      {
+        int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+        CHK_RET_FAILED(res.dumpStateOneNode(nodeId, val2, 2) == 0);
+        CHK_RET_FAILED(res.insertErrorInNode(nodeId, errorno) == 0);
+      }
+
+      for (int rowNo = 0; rowNo < 100; rowNo++)
+      {
+        int rowId = rand() % rows;
+        bzero(pRow, len);
+
+        HugoCalculator calc(* pTab);
+        calc.setValues(pRow, pRowRecord, rowId, rand());
+
+        NdbOperation::OperationOptions opts;
+        bzero(&opts, sizeof(opts));
+        opts.optionsPresent =
+          NdbOperation::OperationOptions::OO_DEFERRED_CONSTAINTS;
+
+        const NdbOperation * pOp = pTrans->updateTuple(pRowRecord, (char*)pRow,
+                                                       pRowRecord, (char*)pRow,
+                                                       0,
+                                                       &opts,
+                                                       sizeof(opts));
+        CHK_RET_FAILED(pOp != 0);
+      }
+
+      int result = pTrans->execute(Commit, AO_IgnoreError);
+      if (j == 0 || j == 2)
+      {
+        CHK_RET_FAILED(result == 0);
+      }
+      else
+      {
+        CHK_RET_FAILED(result != 0);
+      }
+      pTrans->close();
+
+
+      if (j == 0 || j == 2)
+      {
+      }
+      else
+      {
+        if (nodefail)
+        {
+          ndbout_c("  waiting for %u to enter not-started", nodeId);
+          // Wait for a node to enter not-started
+          CHK_RET_FAILED(res.waitNodesNoStart(&nodeId, 1) == 0);
+
+          ndbout_c("  starting all");
+          CHK_RET_FAILED(res.startAll() == 0);
+          ndbout_c("  wait cluster started");
+          CHK_RET_FAILED(res.waitClusterStarted() == 0);
+          ndbout_c("  cluster started");
+        }
+        CHK_RET_FAILED(res.insertErrorInAllNodes(0) == 0);
+      }
+    }
+  }
+
+  delete [] pRow;
+
+  return NDBT_OK;
+}
+
+int
+runMixedDML(NDBT_Context* ctx, NDBT_Step* step)
+{
+  Ndb* pNdb = GETNDB(step);
+  const NdbDictionary::Table* pTab = ctx->getTab();
+
+  unsigned seed = (unsigned)NdbTick_CurrentMillisecond();
+
+  const int rows = ctx->getNumRecords();
+  const int loops = 10 * ctx->getNumLoops();
+  const int until_stopped = ctx->getProperty("UntilStopped");
+  const int deferred = ctx->getProperty("Deferred");
+  const int batch = ctx->getProperty("Batch", Uint32(50));
+
+  const NdbRecord * pRowRecord = pTab->getDefaultRecord();
+  CHK_RET_FAILED(pRowRecord != 0);
+
+  const Uint32 len = NdbDictionary::getRecordRowLength(pRowRecord);
+  Uint8 * pRow = new Uint8[len];
+
+  int count_ok = 0;
+  int count_failed = 0;
+  for (int i = 0; i < loops || (until_stopped && !ctx->isTestStopped()); i++)
+  {
+    NdbTransaction* pTrans = pNdb->startTransaction();
+    CHK_RET_FAILED(pTrans != 0);
+
+    int lastrow = 0;
+    int result = 0;
+    for (int rowNo = 0; rowNo < batch; rowNo++)
+    {
+      int left = rows - lastrow;
+      int rowId = lastrow;
+      if (left)
+      {
+        rowId += ndb_rand_r(&seed) % (left / 10 + 1);
+      }
+      else
+      {
+        break;
+      }
+      lastrow = rowId;
+
+      bzero(pRow, len);
+
+      HugoCalculator calc(* pTab);
+      calc.setValues(pRow, pRowRecord, rowId, rand());
+
+      NdbOperation::OperationOptions opts;
+      bzero(&opts, sizeof(opts));
+      if (deferred)
+      {
+        opts.optionsPresent =
+          NdbOperation::OperationOptions::OO_DEFERRED_CONSTAINTS;
+      }
+
+      const NdbOperation* pOp = 0;
+      switch(ndb_rand_r(&seed) % 3){
+      case 0:
+        pOp = pTrans->writeTuple(pRowRecord, (char*)pRow,
+                                 pRowRecord, (char*)pRow,
+                                 0,
+                                 &opts,
+                                 sizeof(opts));
+        break;
+      case 1:
+        pOp = pTrans->deleteTuple(pRowRecord, (char*)pRow,
+                                  pRowRecord, (char*)pRow,
+                                  0,
+                                  &opts,
+                                  sizeof(opts));
+        break;
+      case 2:
+        pOp = pTrans->updateTuple(pRowRecord, (char*)pRow,
+                                  pRowRecord, (char*)pRow,
+                                  0,
+                                  &opts,
+                                  sizeof(opts));
+        break;
+      }
+      CHK_RET_FAILED(pOp != 0);
+      result = pTrans->execute(NoCommit, AO_IgnoreError);
+      if (result != 0)
+      {
+        goto found_error;
+      }
+    }
+
+    result = pTrans->execute(Commit, AO_IgnoreError);
+    if (result != 0)
+    {
+  found_error:
+      count_failed++;
+      NdbError err = pTrans->getNdbError();
+      ndbout << err << endl;
+      CHK_RET_FAILED(err.code == 1235 ||
+                     err.code == 1236 ||
+                     err.code == 5066 ||
+                     err.status == NdbError::TemporaryError ||
+                     err.classification == NdbError::NoDataFound ||
+                     err.classification == NdbError::ConstraintViolation);
+    }
+    else
+    {
+      count_ok++;
+    }
+    pTrans->close();
+  }
+
+  ndbout_c("count_ok: %d count_failed: %d",
+           count_ok, count_failed);
+  delete [] pRow;
+
+  return NDBT_OK;
+}
+
+int
+runDeferredError(NDBT_Context* ctx, NDBT_Step* step)
+{
+  NdbRestarter res;
+
+  for (int l = 0; l<ctx->getNumLoops() && !ctx->isTestStopped(); l++)
+  {
+    for (int i = 0; deferred_errors[i] != 0 && !ctx->isTestStopped(); i += 2)
+    {
+      const int errorno = deferred_errors[i];
+      const int nodefail = deferred_errors[i+1];
+
+      int nodeId = res.getNode(NdbRestarter::NS_RANDOM);
+
+      ndbout_c("errorno: %u (nf: %u - %u)",
+               errorno,
+               nodefail, nodeId);
+
+      if (nodefail == 0)
+      {
+        CHK_RET_FAILED(res.insertErrorInNode(nodeId, errorno) == 0);
+        NdbSleep_MilliSleep(300);
+        CHK_RET_FAILED(res.insertErrorInNode(nodeId, errorno) == 0);
+      }
+      else
+      {
+        int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+        CHK_RET_FAILED(res.dumpStateOneNode(nodeId, val2, 2) == 0);
+        CHK_RET_FAILED(res.insertErrorInNode(nodeId, errorno) == 0);
+        ndbout_c("  waiting for %u to enter not-started", nodeId);
+        // Wait for a node to enter not-started
+        CHK_RET_FAILED(res.waitNodesNoStart(&nodeId, 1) == 0);
+
+        ndbout_c("  starting all");
+        CHK_RET_FAILED(res.startAll() == 0);
+        ndbout_c("  wait cluster started");
+        CHK_RET_FAILED(res.waitClusterStarted() == 0);
+        ndbout_c("  cluster started");
+      }
+    }
+  }
+
+  ctx->stopTest();
+  return NDBT_OK;
+}
 
 NDBT_TESTSUITE(testIndex);
 TESTCASE("CreateAll", 
@@ -3121,12 +3476,80 @@ TESTCASE("FireTrigOverload", ""){
   FINALIZER(runClearError);
   FINALIZER(createRandomIndex_Drop);
 }
+TESTCASE("DeferredError",
+         "Test with deferred unique index handling and error inserts")
+{
+  TC_PROPERTY("LoggedIndexes", Uint32(0));
+  TC_PROPERTY("OrderedIndex", Uint32(0));
+  INITIALIZER(createPkIndex);
+  INITIALIZER(runLoadTable);
+  STEP(runTestDeferredError);
+  FINALIZER(createPkIndex_Drop);
+}
+TESTCASE("DeferredMixedLoad",
+         "Test mixed load of DML with deferred indexes")
+{
+  TC_PROPERTY("LoggedIndexes", Uint32(0));
+  TC_PROPERTY("OrderedIndex", Uint32(0));
+  TC_PROPERTY("UntilStopped", Uint32(0));
+  TC_PROPERTY("Deferred", Uint32(1));
+  INITIALIZER(createPkIndex);
+  INITIALIZER(runLoadTable);
+  STEPS(runMixedDML, 10);
+  FINALIZER(createPkIndex_Drop);
+}
+TESTCASE("DeferredMixedLoadError",
+         "Test mixed load of DML with deferred indexes")
+{
+  TC_PROPERTY("LoggedIndexes", Uint32(0));
+  TC_PROPERTY("OrderedIndex", Uint32(0));
+  TC_PROPERTY("UntilStopped", Uint32(1));
+  TC_PROPERTY("Deferred", Uint32(1));
+  INITIALIZER(createPkIndex);
+  INITIALIZER(runLoadTable);
+  STEPS(runMixedDML, 4);
+  STEP(runDeferredError);
+  FINALIZER(createPkIndex_Drop);
+}
+TESTCASE("NF_DeferredMixed",
+         "Test mixed load of DML with deferred indexes")
+{
+  TC_PROPERTY("LoggedIndexes", Uint32(0));
+  TC_PROPERTY("OrderedIndex", Uint32(0));
+  TC_PROPERTY("UntilStopped", Uint32(1));
+  TC_PROPERTY("Deferred", Uint32(1));
+  INITIALIZER(createPkIndex);
+  INITIALIZER(runLoadTable);
+  STEPS(runMixedDML, 4);
+  STEP(runRestarts);
+  FINALIZER(createPkIndex_Drop);
+}
+TESTCASE("NF_Mixed",
+         "Test mixed load of DML")
+{
+  TC_PROPERTY("LoggedIndexes", Uint32(0));
+  TC_PROPERTY("OrderedIndex", Uint32(0));
+  TC_PROPERTY("UntilStopped", Uint32(1));
+  INITIALIZER(createPkIndex);
+  INITIALIZER(runLoadTable);
+  STEPS(runMixedDML, 4);
+  STEP(runRestarts);
+  FINALIZER(createPkIndex_Drop);
+}
 TESTCASE("Bug56829",
          "Return empty ordered index nodes to index fragment "
          "so that empty fragment pages can be freed"){
   STEP(runBug56829);
 }
-  
+TESTCASE("Bug12315582", "")
+{
+  TC_PROPERTY("LoggedIndexes", Uint32(0));
+  TC_PROPERTY("OrderedIndex", Uint32(0));
+  INITIALIZER(createPkIndex);
+  INITIALIZER(runLoadTable);
+  INITIALIZER(runBug12315582);
+  FINALIZER(createPkIndex_Drop);
+}
 NDBT_TESTSUITE_END(testIndex);
 
 int main(int argc, const char** argv){

=== modified file 'storage/ndb/test/run-test/daily-basic-tests.txt'
--- a/storage/ndb/test/run-test/daily-basic-tests.txt	2010-11-02 14:53:26 +0000
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt	2011-04-05 19:12:29 +0000
@@ -1,3 +1,23 @@
+max-time: 900
+cmd: testIndex
+args: -n DeferredError
+
+max-time: 900
+cmd: testIndex
+args: -n DeferredMixedLoad T1 T6 T13
+
+max-time: 900
+cmd: testIndex
+args: -n DeferredMixedLoadError T1 T6 T13
+
+max-time: 900
+cmd: testIndex
+args: -n NF_DeferredMixed T1 T6 T13
+
+max-time: 900
+cmd: testIndex
+args: -n NF_Mixed T1 T6 T13
+
 max-time: 600
 cmd: atrt-testBackup
 args: -n NFMaster T1
@@ -1617,6 +1637,10 @@ max-time: 300
 cmd: testIndex
 args: -n Bug56829 T1
 
+max-time: 300
+cmd: testIndex
+args: -n Bug12315582 T1
+
 max-time: 500
 cmd testNodeRestart
 args: -n ForceStopAndRestart T1

=== modified file 'storage/ndb/test/src/HugoCalculator.cpp'
--- a/storage/ndb/test/src/HugoCalculator.cpp	2010-06-17 05:20:22 +0000
+++ b/storage/ndb/test/src/HugoCalculator.cpp	2011-04-05 19:12:29 +0000
@@ -446,3 +446,75 @@ HugoCalculator::getUpdatesValue(NDBT_Res
   return pRow->attributeStore(m_updatesCol)->u_32_value();
 }
 
+int
+HugoCalculator::equalForRow(Uint8 * pRow,
+                            const NdbRecord* pRecord,
+                            int rowId)
+{
+  for(int attrId = 0; attrId < m_tab.getNoOfColumns(); attrId++)
+  {
+    const NdbDictionary::Column* attr = m_tab.getColumn(attrId);
+
+    if (attr->getPrimaryKey() == true)
+    {
+      char buf[8000];
+      int len = attr->getSizeInBytes();
+      memset(buf, 0, sizeof(buf));
+      Uint32 real_len;
+      const char * value = calcValue(rowId, attrId, 0, buf,
+                                     len, &real_len);
+      assert(value != 0); // NULLable PK not supported...
+      Uint32 off = 0;
+      bool ret = NdbDictionary::getOffset(pRecord, attrId, off);
+      if (!ret)
+        abort();
+      memcpy(pRow + off, buf, real_len);
+    }
+  }
+  return NDBT_OK;
+}
+
+int
+HugoCalculator::setValues(Uint8 * pRow,
+                          const NdbRecord* pRecord,
+                          int rowId,
+                          int updateVal)
+{
+  int res = equalForRow(pRow, pRecord, rowId);
+  if (res != 0)
+  {
+    return res;
+  }
+
+  for(int attrId = 0; attrId < m_tab.getNoOfColumns(); attrId++)
+  {
+    const NdbDictionary::Column* attr = m_tab.getColumn(attrId);
+
+    if (attr->getPrimaryKey() == false)
+    {
+      char buf[8000];
+      int len = attr->getSizeInBytes();
+      memset(buf, 0, sizeof(buf));
+      Uint32 real_len;
+      const char * value = calcValue(rowId, attrId, updateVal, buf,
+                                     len, &real_len);
+      if (value != 0)
+      {
+        Uint32 off = 0;
+        bool ret = NdbDictionary::getOffset(pRecord, attrId, off);
+        if (!ret)
+          abort();
+        memcpy(pRow + off, buf, real_len);
+        if (attr->getNullable())
+          NdbDictionary::setNull(pRecord, (char*)pRow, attrId, false);
+      }
+      else
+      {
+        assert(attr->getNullable());
+        NdbDictionary::setNull(pRecord, (char*)pRow, attrId, true);
+      }
+    }
+  }
+
+  return NDBT_OK;
+}


Attachment: [text/bzr-bundle] bzr/jonas@mysql.com-20110405191229-s8efbduqkdfhoa5w.bundle
Thread
bzr commit into mysql-5.1-telco-7.1 branch (jonas:3946) jonas oreland5 Apr