summaryrefslogtreecommitdiff
path: root/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp')
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp1150
1 files changed, 1150 insertions, 0 deletions
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp
new file mode 100644
index 00000000000..476a4b5724b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp
@@ -0,0 +1,1150 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <AttributeHeader.hpp>
+#include <signaldata/FireTrigOrd.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/TuxMaint.hpp>
+
+#define ljam() { jamLine(7000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(7000 + __LINE__); }
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ----------------------- TRIGGER HANDLING ----------------------- */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+ArrayList<Dbtup::TupTriggerData>*
+Dbtup::findTriggerList(Tablerec* table,
+ TriggerType::Value ttype,
+ TriggerActionTime::Value ttime,
+ TriggerEvent::Value tevent)
+{
+ ArrayList<TupTriggerData>* tlist = NULL;
+ switch (ttype) {
+ case TriggerType::SUBSCRIPTION:
+ case TriggerType::SUBSCRIPTION_BEFORE:
+ switch (tevent) {
+ case TriggerEvent::TE_INSERT:
+ ljam();
+ if (ttime == TriggerActionTime::TA_DETACHED)
+ tlist = &table->subscriptionInsertTriggers;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_DETACHED)
+ tlist = &table->subscriptionUpdateTriggers;
+ break;
+ case TriggerEvent::TE_DELETE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_DETACHED)
+ tlist = &table->subscriptionDeleteTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TriggerType::SECONDARY_INDEX:
+ switch (tevent) {
+ case TriggerEvent::TE_INSERT:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->afterInsertTriggers;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->afterUpdateTriggers;
+ break;
+ case TriggerEvent::TE_DELETE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->afterDeleteTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TriggerType::ORDERED_INDEX:
+ switch (tevent) {
+ case TriggerEvent::TE_CUSTOM:
+ ljam();
+ if (ttime == TriggerActionTime::TA_CUSTOM)
+ tlist = &table->tuxCustomTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TriggerType::READ_ONLY_CONSTRAINT:
+ switch (tevent) {
+ case TriggerEvent::TE_UPDATE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->constraintUpdateTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return tlist;
+}
+
+// Trigger signals
+void
+Dbtup::execCREATE_TRIG_REQ(Signal* signal)
+{
+ ljamEntry();
+ BlockReference senderRef = signal->getSendersBlockRef();
+ const CreateTrigReq reqCopy = *(const CreateTrigReq*)signal->getDataPtr();
+ const CreateTrigReq* const req = &reqCopy;
+
+ // Find table
+ TablerecPtr tabPtr;
+ tabPtr.i = req->getTableId();
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+ // Create trigger and associate it with the table
+ if (createTrigger(tabPtr.p, req)) {
+ ljam();
+ // Send conf
+ CreateTrigConf* const conf = (CreateTrigConf*)signal->getDataPtrSend();
+ conf->setUserRef(reference());
+ conf->setConnectionPtr(req->getConnectionPtr());
+ conf->setRequestType(req->getRequestType());
+ conf->setTableId(req->getTableId());
+ conf->setIndexId(req->getIndexId());
+ conf->setTriggerId(req->getTriggerId());
+ conf->setTriggerInfo(req->getTriggerInfo());
+ sendSignal(senderRef, GSN_CREATE_TRIG_CONF,
+ signal, CreateTrigConf::SignalLength, JBB);
+ } else {
+ ljam();
+ // Send ref
+ CreateTrigRef* const ref = (CreateTrigRef*)signal->getDataPtrSend();
+ ref->setUserRef(reference());
+ ref->setConnectionPtr(req->getConnectionPtr());
+ ref->setRequestType(req->getRequestType());
+ ref->setTableId(req->getTableId());
+ ref->setIndexId(req->getIndexId());
+ ref->setTriggerId(req->getTriggerId());
+ ref->setTriggerInfo(req->getTriggerInfo());
+ ref->setErrorCode(CreateTrigRef::TooManyTriggers);
+ sendSignal(senderRef, GSN_CREATE_TRIG_REF,
+ signal, CreateTrigRef::SignalLength, JBB);
+ }
+}//Dbtup::execCREATE_TRIG_REQ()
+
+void
+Dbtup::execDROP_TRIG_REQ(Signal* signal)
+{
+ ljamEntry();
+ BlockReference senderRef = signal->getSendersBlockRef();
+ const DropTrigReq reqCopy = *(const DropTrigReq*)signal->getDataPtr();
+ const DropTrigReq* const req = &reqCopy;
+
+ // Find table
+ TablerecPtr tabPtr;
+ tabPtr.i = req->getTableId();
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+ // Drop trigger
+ Uint32 r = dropTrigger(tabPtr.p, req);
+ if (r == 0){
+ // Send conf
+ DropTrigConf* const conf = (DropTrigConf*)signal->getDataPtrSend();
+ conf->setUserRef(senderRef);
+ conf->setConnectionPtr(req->getConnectionPtr());
+ conf->setRequestType(req->getRequestType());
+ conf->setTableId(req->getTableId());
+ conf->setIndexId(req->getIndexId());
+ conf->setTriggerId(req->getTriggerId());
+ sendSignal(senderRef, GSN_DROP_TRIG_CONF,
+ signal, DropTrigConf::SignalLength, JBB);
+ } else {
+ // Send ref
+ DropTrigRef* const ref = (DropTrigRef*)signal->getDataPtrSend();
+ ref->setUserRef(senderRef);
+ ref->setConnectionPtr(req->getConnectionPtr());
+ ref->setRequestType(req->getRequestType());
+ ref->setTableId(req->getTableId());
+ ref->setIndexId(req->getIndexId());
+ ref->setTriggerId(req->getTriggerId());
+ ref->setErrorCode((DropTrigRef::ErrorCode)r);
+ ref->setErrorLine(__LINE__);
+ ref->setErrorNode(refToNode(reference()));
+ sendSignal(senderRef, GSN_DROP_TRIG_REF,
+ signal, DropTrigRef::SignalLength, JBB);
+ }
+}//Dbtup::DROP_TRIG_REQ()
+
+/* ---------------------------------------------------------------- */
+/* ------------------------- createTrigger ------------------------ */
+/* */
+/* Creates a new trigger record by fetching one from the trigger */
+/* pool and associates it with the given table. */
+/* Trigger type can be one of secondary_index, subscription, */
+/* constraint(NYI), foreign_key(NYI), schema_upgrade(NYI), */
+/* api_trigger(NYI) or sql_trigger(NYI). */
+/* Note that this method only checks for total number of allowed */
+/* triggers. Checking the number of allowed triggers per table is */
+/* done by TRIX. */
+/* */
+/* ---------------------------------------------------------------- */
+bool
+Dbtup::createTrigger(Tablerec* table, const CreateTrigReq* req)
+{
+ if (ERROR_INSERTED(4003)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return false;
+ }
+ TriggerType::Value ttype = req->getTriggerType();
+ TriggerActionTime::Value ttime = req->getTriggerActionTime();
+ TriggerEvent::Value tevent = req->getTriggerEvent();
+
+ ArrayList<TupTriggerData>* tlist = findTriggerList(table, ttype, ttime, tevent);
+ ndbrequire(tlist != NULL);
+
+ TriggerPtr tptr;
+ if (!tlist->seize(tptr))
+ return false;
+
+ // Set trigger id
+ tptr.p->triggerId = req->getTriggerId();
+
+ // ndbout_c("Create TupTrigger %u = %u %u %u %u", tptr.p->triggerId, table, ttype, ttime, tevent);
+
+ // Set index id
+ tptr.p->indexId = req->getIndexId();
+
+ // Set trigger type etc
+ tptr.p->triggerType = ttype;
+ tptr.p->triggerActionTime = ttime;
+ tptr.p->triggerEvent = tevent;
+
+ tptr.p->sendBeforeValues = true;
+ if ((tptr.p->triggerType == TriggerType::SUBSCRIPTION) &&
+ ((tptr.p->triggerEvent == TriggerEvent::TE_UPDATE) ||
+ (tptr.p->triggerEvent == TriggerEvent::TE_DELETE))) {
+ ljam();
+ tptr.p->sendBeforeValues = false;
+ }
+ tptr.p->sendOnlyChangedAttributes = false;
+ if (((tptr.p->triggerType == TriggerType::SUBSCRIPTION) ||
+ (tptr.p->triggerType == TriggerType::SUBSCRIPTION_BEFORE)) &&
+ (tptr.p->triggerEvent == TriggerEvent::TE_UPDATE)) {
+ ljam();
+ tptr.p->sendOnlyChangedAttributes = true;
+ }
+
+ // Set monitor all
+ tptr.p->monitorAllAttributes = req->getMonitorAllAttributes();
+ tptr.p->monitorReplicas = req->getMonitorReplicas();
+ tptr.p->m_receiverBlock = refToBlock(req->getReceiverRef());
+
+ tptr.p->attributeMask.clear();
+ if (tptr.p->monitorAllAttributes) {
+ ljam();
+ for(Uint32 i = 0; i < table->noOfAttr; i++) {
+ if (!primaryKey(table, i)) {
+ ljam();
+ tptr.p->attributeMask.set(i);
+ }
+ }
+ } else {
+ // Set attribute mask
+ ljam();
+ tptr.p->attributeMask = req->getAttributeMask();
+ }
+ return true;
+}//Dbtup::createTrigger()
+
+bool
+Dbtup::primaryKey(Tablerec* const regTabPtr, Uint32 attrId)
+{
+ Uint32 attrDescriptorStart = regTabPtr->tabDescriptor;
+ Uint32 attrDescriptor = getTabDescrWord(attrDescriptorStart + (attrId * ZAD_SIZE));
+ return (bool)AttributeDescriptor::getPrimaryKey(attrDescriptor);
+}//Dbtup::primaryKey()
+
+/* ---------------------------------------------------------------- */
+/* -------------------------- dropTrigger ------------------------- */
+/* */
+/* Deletes a trigger record by disassociating it with the given */
+/* table and returning it to the trigger pool. */
+/* Trigger type can be one of secondary_index, subscription, */
+/* constraint(NYI), foreign_key(NYI), schema_upgrade(NYI), */
+/* api_trigger(NYI) or sql_trigger(NYI). */
+/* */
+/* ---------------------------------------------------------------- */
+Uint32
+Dbtup::dropTrigger(Tablerec* table, const DropTrigReq* req)
+{
+ Uint32 triggerId = req->getTriggerId();
+
+ TriggerType::Value ttype = req->getTriggerType();
+ TriggerActionTime::Value ttime = req->getTriggerActionTime();
+ TriggerEvent::Value tevent = req->getTriggerEvent();
+
+ // ndbout_c("Drop TupTrigger %u = %u %u %u %u", triggerId, table, ttype, ttime, tevent);
+
+ ArrayList<TupTriggerData>* tlist = findTriggerList(table, ttype, ttime, tevent);
+ ndbrequire(tlist != NULL);
+
+ Ptr<TupTriggerData> ptr;
+ for (tlist->first(ptr); !ptr.isNull(); tlist->next(ptr)) {
+ ljam();
+ if (ptr.p->triggerId == triggerId) {
+ ljam();
+ tlist->release(ptr.i);
+ return 0;
+ }
+ }
+ return DropTrigRef::TriggerNotFound;
+}//Dbtup::dropTrigger()
+
+/* ---------------------------------------------------------------- */
+/* -------------- checkImmediateTriggersAfterOp ------------------ */
+/* */
+/* Called after an insert, delete, or update operation takes */
+/* place. Fetches before tuple for deletes and updates and */
+/* after tuple for inserts and updates. */
+/* Executes immediate triggers by sending FIRETRIGORD */
+/* */
+/* ---------------------------------------------------------------- */
+void Dbtup::checkImmediateTriggersAfterInsert(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ if(refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ return;
+ }
+
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->afterInsertTriggers.isEmpty()))) {
+ ljam();
+ fireImmediateTriggers(signal,
+ regTablePtr->afterInsertTriggers,
+ regOperPtr);
+ }//if
+}//Dbtup::checkImmediateTriggersAfterInsert()
+
+void Dbtup::checkImmediateTriggersAfterUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ if(refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ return;
+ }
+
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->afterUpdateTriggers.isEmpty()))) {
+ ljam();
+ fireImmediateTriggers(signal,
+ regTablePtr->afterUpdateTriggers,
+ regOperPtr);
+ }//if
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->constraintUpdateTriggers.isEmpty()))) {
+ ljam();
+ fireImmediateTriggers(signal,
+ regTablePtr->constraintUpdateTriggers,
+ regOperPtr);
+ }//if
+}//Dbtup::checkImmediateTriggersAfterUpdate()
+
+void Dbtup::checkImmediateTriggersAfterDelete(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ if(refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ return;
+ }
+
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->afterDeleteTriggers.isEmpty()))) {
+ ljam();
+ executeTriggers(signal,
+ regTablePtr->afterDeleteTriggers,
+ regOperPtr);
+ }//if
+}//Dbtup::checkImmediateTriggersAfterDelete()
+
+#if 0
+/* ---------------------------------------------------------------- */
+/* --------------------- checkDeferredTriggers -------------------- */
+/* */
+/* Called before commit after an insert, delete, or update */
+/* operation. Fetches before tuple for deletes and updates and */
+/* after tuple for inserts and updates. */
+/* Executes deferred triggers by sending FIRETRIGORD */
+/* */
+/* ---------------------------------------------------------------- */
+void Dbtup::checkDeferredTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ ljam();
+ // NYI
+}//Dbtup::checkDeferredTriggers()
+#endif
+
+/* ---------------------------------------------------------------- */
+/* --------------------- checkDetachedTriggers -------------------- */
+/* */
+/* Called at commit after an insert, delete, or update operation. */
+/* Fetches before tuple for deletes and updates and */
+/* after tuple for inserts and updates. */
+/* Executes detached triggers by sending FIRETRIGORD */
+/* */
+/* ---------------------------------------------------------------- */
+void Dbtup::checkDetachedTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ switch(regOperPtr->optype) {
+ case(ZINSERT):
+ ljam();
+ if (regTablePtr->subscriptionInsertTriggers.isEmpty()) {
+ // Table has no active triggers monitoring inserts at commit
+ ljam();
+ return;
+ }//if
+
+ // If any fired immediate insert trigger then fetch after tuple
+ fireDetachedTriggers(signal,
+ regTablePtr->subscriptionInsertTriggers,
+ regOperPtr);
+ break;
+ case(ZDELETE):
+ ljam();
+ if (regTablePtr->subscriptionDeleteTriggers.isEmpty()) {
+ // Table has no active triggers monitoring deletes at commit
+ ljam();
+ return;
+ }//if
+
+ // Execute any after delete triggers by sending
+ // FIRETRIGORD with the before tuple
+ executeTriggers(signal,
+ regTablePtr->subscriptionDeleteTriggers,
+ regOperPtr);
+ break;
+ case(ZUPDATE):
+ ljam();
+ if (regTablePtr->subscriptionUpdateTriggers.isEmpty()) {
+ // Table has no active triggers monitoring updates at commit
+ ljam();
+ return;
+ }//if
+
+ // If any fired immediate update trigger then fetch after tuple
+ // and send two FIRETRIGORD one with before tuple and one with after tuple
+ fireDetachedTriggers(signal,
+ regTablePtr->subscriptionUpdateTriggers,
+ regOperPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbtup::CheckDetachedTriggers()
+
+void
+Dbtup::fireImmediateTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ if (trigPtr.p->monitorAllAttributes ||
+ trigPtr.p->attributeMask.overlaps(regOperPtr->changeMask)) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr.p,
+ regOperPtr);
+ }//if
+ triggerList.next(trigPtr);
+ }//while
+}//Dbtup::fireImmediateTriggers()
+
+#if 0
+void
+Dbtup::fireDeferredTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ if (trigPtr.p->monitorAllAttributes ||
+ trigPtr.p->attributeMask.overlaps(regOperPtr->changeMask)) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr,
+ regOperPtr);
+ }//if
+ triggerList.next(trigPtr);
+ }//while
+}//Dbtup::fireDeferredTriggers()
+#endif
+
+void
+Dbtup::fireDetachedTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ if ((trigPtr.p->monitorReplicas || regOperPtr->primaryReplica) &&
+ (trigPtr.p->monitorAllAttributes ||
+ trigPtr.p->attributeMask.overlaps(regOperPtr->changeMask))) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr.p,
+ regOperPtr);
+ }//if
+ triggerList.next(trigPtr);
+ }//while
+}//Dbtup::fireDetachedTriggers()
+
+void Dbtup::executeTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr.p,
+ regOperPtr);
+ triggerList.next(trigPtr);
+
+ }//while
+}//Dbtup::executeTriggers()
+
+void Dbtup::executeTrigger(Signal* signal,
+ TupTriggerData* const trigPtr,
+ Operationrec* const regOperPtr)
+{
+
+ /**
+ * The block below does not work together with GREP.
+ * I have 2 db nodes (2 replicas) -> one node group.
+ * I want to have FIRETRIG_ORD sent to all SumaParticipants,
+ * from all nodes in the node group described above. However,
+ * only one of the nodes in the node group actually sends the
+ * FIRE_TRIG_ORD, and the other node enters this "hack" below.
+ * I don't really know what the code snippet below does, but it
+ * does not work with GREP the way Lars and I want it.
+ * We need to have triggers fired from both the primary and the
+ * backup replica, not only the primary as it is now.
+ *
+ * Note: In Suma, I have changed triggers to be created with
+ * setMonitorReplicas(true).
+ * /Johan
+ *
+ * See RT 709
+ */
+ // XXX quick fix to NR, should fix in LQHKEYREQ instead
+ /*
+ if (refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ jam();
+ return;
+ }
+ */
+ BlockReference ref = trigPtr->m_receiverBlock;
+ Uint32* const keyBuffer = &cinBuffer[0];
+ Uint32* const mainBuffer = &coutBuffer[0];
+ Uint32* const copyBuffer = &clogMemBuffer[0];
+
+ Uint32 noPrimKey, noMainWords, noCopyWords;
+
+ if (ref == BACKUP) {
+ ljam();
+ /*
+ In order for the implementation of BACKUP to work even when changing
+ primaries in the middle of the backup we need to set the trigger on
+ all replicas. This check checks whether this is the node where this
+ trigger should be fired. The check should preferably have been put
+ completely in the BACKUP block but it was about five times simpler
+ to put it here and also much faster for the backup (small overhead
+ for everybody else.
+ */
+ signal->theData[0] = trigPtr->triggerId;
+ signal->theData[1] = regOperPtr->fragId;
+ EXECUTE_DIRECT(BACKUP, GSN_BACKUP_TRIG_REQ, signal, 2);
+ ljamEntry();
+ if (signal->theData[0] == 0) {
+ ljam();
+ return;
+ }//if
+ }//if
+ if (!readTriggerInfo(trigPtr,
+ regOperPtr,
+ keyBuffer,
+ noPrimKey,
+ mainBuffer,
+ noMainWords,
+ copyBuffer,
+ noCopyWords)) {
+ ljam();
+ return;
+ }//if
+//--------------------------------------------------------------------
+// Now all data for this trigger has been read. It is now time to send
+// the trigger information consisting of two or three sets of TRIG_
+// ATTRINFO signals and one FIRE_TRIG_ORD signal.
+// We start by setting common header info for all TRIG_ATTRINFO signals.
+//--------------------------------------------------------------------
+ bool executeDirect;
+ TrigAttrInfo* const trigAttrInfo = (TrigAttrInfo *)signal->getDataPtrSend();
+ trigAttrInfo->setConnectionPtr(regOperPtr->tcOpIndex);
+ trigAttrInfo->setTriggerId(trigPtr->triggerId);
+
+ switch(trigPtr->triggerType) {
+ case (TriggerType::SECONDARY_INDEX):
+ ljam();
+ ref = regOperPtr->coordinatorTC;
+ executeDirect = false;
+ break;
+ case (TriggerType::SUBSCRIPTION):
+ case (TriggerType::SUBSCRIPTION_BEFORE):
+ ljam();
+ // Since only backup uses subscription triggers we send to backup directly for now
+ ref = trigPtr->m_receiverBlock;
+ executeDirect = true;
+ break;
+ case (TriggerType::READ_ONLY_CONSTRAINT):
+ terrorCode = ZREAD_ONLY_CONSTRAINT_VIOLATION;
+ // XXX should return status and abort the rest
+ return;
+ default:
+ ndbrequire(false);
+ executeDirect= false; // remove warning
+ }//switch
+
+ regOperPtr->noFiredTriggers++;
+
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::PRIMARY_KEY);
+ sendTrigAttrInfo(signal, keyBuffer, noPrimKey, executeDirect, ref);
+
+ Uint32 noAfter = 0;
+ Uint32 noBefore = 0;
+ switch(regOperPtr->optype) {
+ case(ZINSERT):
+ ljam();
+ // Send AttrInfo signals with new attribute values
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::AFTER_VALUES);
+ sendTrigAttrInfo(signal, mainBuffer, noMainWords, executeDirect, ref);
+ noAfter = noMainWords;
+ break;
+ case(ZDELETE):
+ if (trigPtr->sendBeforeValues) {
+ ljam();
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::BEFORE_VALUES);
+ sendTrigAttrInfo(signal, mainBuffer, noMainWords, executeDirect, ref);
+ noBefore = noMainWords;
+ }//if
+ break;
+ case(ZUPDATE):
+ ljam();
+ if (trigPtr->sendBeforeValues) {
+ ljam();
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::BEFORE_VALUES);
+ sendTrigAttrInfo(signal, copyBuffer, noCopyWords, executeDirect, ref);
+ noBefore = noCopyWords;
+ }//if
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::AFTER_VALUES);
+ sendTrigAttrInfo(signal, mainBuffer, noMainWords, executeDirect, ref);
+ noAfter = noMainWords;
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+ sendFireTrigOrd(signal,
+ regOperPtr,
+ trigPtr,
+ noPrimKey,
+ noBefore,
+ noAfter);
+}//Dbtup::executeTrigger()
+
+Uint32 Dbtup::setAttrIds(Bitmask<MAXNROFATTRIBUTESINWORDS>& attributeMask,
+ Uint32 noOfAttributes,
+ Uint32* inBuffer)
+{
+ Uint32 bufIndx = 0;
+ for (Uint32 i = 0; i < noOfAttributes; i++) {
+ ljam();
+ if (attributeMask.get(i)) {
+ ljam();
+ AttributeHeader::init(&inBuffer[bufIndx++], i, 0);
+ }//if
+ }//for
+ return bufIndx;
+}//Dbtup::setAttrIds()
+
+bool Dbtup::readTriggerInfo(TupTriggerData* const trigPtr,
+ Operationrec* const regOperPtr,
+ Uint32* const keyBuffer,
+ Uint32& noPrimKey,
+ Uint32* const mainBuffer,
+ Uint32& noMainWords,
+ Uint32* const copyBuffer,
+ Uint32& noCopyWords)
+{
+ noCopyWords = 0;
+ noMainWords = 0;
+ Uint32 readBuffer[MAX_ATTRIBUTES_IN_TABLE];
+ PagePtr pagep;
+
+//---------------------------------------------------------------------------
+// Set-up variables needed by readAttributes operPtr.p, tabptr.p
+//---------------------------------------------------------------------------
+ operPtr.p = regOperPtr;
+ tabptr.i = regOperPtr->tableRef;
+ ptrCheckGuard(tabptr, cnoOfTablerec, tablerec);
+ Tablerec* const regTabPtr = tabptr.p;
+//--------------------------------------------------------------------
+// Initialise pagep and tuple offset for read of main tuple
+//--------------------------------------------------------------------
+ Uint32 tupheadoffset = regOperPtr->pageOffset;
+ pagep.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagep, cnoOfPage, page);
+
+//--------------------------------------------------------------------
+// Read Primary Key Values
+//--------------------------------------------------------------------
+ int ret= readAttributes(pagep.p,
+ tupheadoffset,
+ &tableDescriptor[regTabPtr->readKeyArray].tabDescr,
+ regTabPtr->noOfKeyAttr,
+ keyBuffer,
+ ZATTR_BUFFER_SIZE,
+ false);
+ ndbrequire(ret != -1);
+ noPrimKey= ret;
+
+ Uint32 numAttrsToRead;
+ if ((regOperPtr->optype == ZUPDATE) &&
+ (trigPtr->sendOnlyChangedAttributes)) {
+ ljam();
+//--------------------------------------------------------------------
+// Update that sends only changed information
+//--------------------------------------------------------------------
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attributeMask;
+ attributeMask = trigPtr->attributeMask;
+ attributeMask.bitAND(regOperPtr->changeMask);
+ numAttrsToRead = setAttrIds(attributeMask, regTabPtr->noOfAttr, &readBuffer[0]);
+
+ } else if ((regOperPtr->optype == ZDELETE) &&
+ (!trigPtr->sendBeforeValues)) {
+ ljam();
+//--------------------------------------------------------------------
+// Delete without sending before values only read Primary Key
+//--------------------------------------------------------------------
+ return true;
+ } else {
+ ljam();
+//--------------------------------------------------------------------
+// All others send all attributes that are monitored
+//--------------------------------------------------------------------
+ numAttrsToRead = setAttrIds(trigPtr->attributeMask, regTabPtr->noOfAttr, &readBuffer[0]);
+ }//if
+ ndbrequire(numAttrsToRead < MAX_ATTRIBUTES_IN_TABLE);
+//--------------------------------------------------------------------
+// Read Main tuple values
+//--------------------------------------------------------------------
+ if ((regOperPtr->optype != ZDELETE) ||
+ (trigPtr->sendBeforeValues)) {
+ ljam();
+ int ret= readAttributes(pagep.p,
+ tupheadoffset,
+ &readBuffer[0],
+ numAttrsToRead,
+ mainBuffer,
+ ZATTR_BUFFER_SIZE,
+ false);
+ ndbrequire(ret != -1);
+ noMainWords= ret;
+ } else {
+ ljam();
+ noMainWords = 0;
+ }//if
+//--------------------------------------------------------------------
+// Read Copy tuple values for UPDATE's
+//--------------------------------------------------------------------
+// Initialise pagep and tuple offset for read of copy tuple
+//--------------------------------------------------------------------
+ if ((regOperPtr->optype == ZUPDATE) &&
+ (trigPtr->sendBeforeValues)) {
+ ljam();
+
+ tupheadoffset = regOperPtr->pageOffsetC;
+ pagep.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(pagep, cnoOfPage, page);
+
+ int ret= readAttributes(pagep.p,
+ tupheadoffset,
+ &readBuffer[0],
+ numAttrsToRead,
+ copyBuffer,
+ ZATTR_BUFFER_SIZE,
+ false);
+
+ ndbrequire(ret != -1);
+ noCopyWords = ret;
+ if ((noMainWords == noCopyWords) &&
+ (memcmp(mainBuffer, copyBuffer, noMainWords << 2) == 0)) {
+//--------------------------------------------------------------------
+// Although a trigger was fired it was not necessary since the old
+// value and the new value was exactly the same
+//--------------------------------------------------------------------
+ ljam();
+ return false;
+ }//if
+ }//if
+ return true;
+}//Dbtup::readTriggerInfo()
+
+void Dbtup::sendTrigAttrInfo(Signal* signal,
+ Uint32* data,
+ Uint32 dataLen,
+ bool executeDirect,
+ BlockReference receiverReference)
+{
+ TrigAttrInfo* const trigAttrInfo = (TrigAttrInfo *)signal->getDataPtrSend();
+ Uint32 sigLen;
+ Uint32 dataIndex = 0;
+ do {
+ sigLen = dataLen - dataIndex;
+ if (sigLen > TrigAttrInfo::DataLength) {
+ ljam();
+ sigLen = TrigAttrInfo::DataLength;
+ }//if
+ MEMCOPY_NO_WORDS(trigAttrInfo->getData(),
+ data + dataIndex,
+ sigLen);
+ if (executeDirect) {
+ ljam();
+ EXECUTE_DIRECT(receiverReference,
+ GSN_TRIG_ATTRINFO,
+ signal,
+ TrigAttrInfo::StaticLength + sigLen);
+ ljamEntry();
+ } else {
+ ljam();
+ sendSignal(receiverReference,
+ GSN_TRIG_ATTRINFO,
+ signal,
+ TrigAttrInfo::StaticLength + sigLen,
+ JBB);
+ }//if
+ dataIndex += sigLen;
+ } while (dataLen != dataIndex);
+}//Dbtup::sendTrigAttrInfo()
+
+void Dbtup::sendFireTrigOrd(Signal* signal,
+ Operationrec * const regOperPtr,
+ TupTriggerData* const trigPtr,
+ Uint32 noPrimKeyWords,
+ Uint32 noBeforeValueWords,
+ Uint32 noAfterValueWords)
+{
+ FireTrigOrd* const fireTrigOrd = (FireTrigOrd *)signal->getDataPtrSend();
+
+ fireTrigOrd->setConnectionPtr(regOperPtr->tcOpIndex);
+ fireTrigOrd->setTriggerId(trigPtr->triggerId);
+
+ switch(regOperPtr->optype) {
+ case(ZINSERT):
+ ljam();
+ fireTrigOrd->setTriggerEvent(TriggerEvent::TE_INSERT);
+ break;
+ case(ZDELETE):
+ ljam();
+ fireTrigOrd->setTriggerEvent(TriggerEvent::TE_DELETE);
+ break;
+ case(ZUPDATE):
+ ljam();
+ fireTrigOrd->setTriggerEvent(TriggerEvent::TE_UPDATE);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+
+ fireTrigOrd->setNoOfPrimaryKeyWords(noPrimKeyWords);
+ fireTrigOrd->setNoOfBeforeValueWords(noBeforeValueWords);
+ fireTrigOrd->setNoOfAfterValueWords(noAfterValueWords);
+
+ switch(trigPtr->triggerType) {
+ case (TriggerType::SECONDARY_INDEX):
+ ljam();
+ sendSignal(regOperPtr->coordinatorTC, GSN_FIRE_TRIG_ORD,
+ signal, FireTrigOrd::SignalLength, JBB);
+ break;
+ case (TriggerType::SUBSCRIPTION_BEFORE): // Only Suma
+ ljam();
+ // Since only backup uses subscription triggers we
+ // send to backup directly for now
+ fireTrigOrd->setGCI(regOperPtr->gci);
+ fireTrigOrd->setHashValue(regOperPtr->hashValue);
+ EXECUTE_DIRECT(trigPtr->m_receiverBlock,
+ GSN_FIRE_TRIG_ORD,
+ signal,
+ FireTrigOrd::SignalWithHashValueLength);
+ break;
+ case (TriggerType::SUBSCRIPTION):
+ ljam();
+ // Since only backup uses subscription triggers we
+ // send to backup directly for now
+ fireTrigOrd->setGCI(regOperPtr->gci);
+ EXECUTE_DIRECT(trigPtr->m_receiverBlock,
+ GSN_FIRE_TRIG_ORD,
+ signal,
+ FireTrigOrd::SignalWithGCILength);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbtup::sendFireTrigOrd()
+
+/*
+ * Ordered index triggers.
+ *
+ * Insert: add entry to index
+ * Update: add entry to index, de|ay remove until commit
+ * Delete: do nothing, delay remove until commit
+ * Commit: remove entry delayed from update and delete
+ * Abort : remove entry added by insert and update
+ *
+ * See Notes.txt for the details.
+ */
+
+int
+Dbtup::executeTuxInsertTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffset + 1];
+ ndbrequire(tupVersion == regOperPtr->tupVersion);
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpAdd;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ if (req->errorCode != 0) {
+ ljam();
+ terrorCode = req->errorCode;
+ return -1;
+ }
+ triggerList.next(triggerPtr);
+ }
+ return 0;
+}
+
+int
+Dbtup::executeTuxUpdateTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffset + 1];
+ ndbrequire(tupVersion == regOperPtr->tupVersion);
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpAdd;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ if (req->errorCode != 0) {
+ ljam();
+ terrorCode = req->errorCode;
+ return -1;
+ }
+ triggerList.next(triggerPtr);
+ }
+ return 0;
+}
+
+int
+Dbtup::executeTuxDeleteTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ // do nothing
+ return 0;
+}
+
+void
+Dbtup::executeTuxCommitTriggers(Signal* signal,
+ Operationrec* regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ // get version
+ // XXX could add prevTupVersion to Operationrec
+ Uint32 tupVersion;
+ if (regOperPtr->optype == ZINSERT) {
+ if (! regOperPtr->deleteInsertFlag)
+ return;
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffsetC + 1];
+ ndbrequire(tupVersion != regOperPtr->tupVersion);
+ } else if (regOperPtr->optype == ZUPDATE) {
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffsetC + 1];
+ ndbrequire(tupVersion != regOperPtr->tupVersion);
+ } else if (regOperPtr->optype == ZDELETE) {
+ if (regOperPtr->deleteInsertFlag)
+ return;
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffset + 1];
+ ndbrequire(tupVersion == regOperPtr->tupVersion);
+ } else {
+ ndbrequire(false);
+ tupVersion= 0; // remove warning
+ }
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpRemove;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ // commit must succeed
+ ndbrequire(req->errorCode == 0);
+ triggerList.next(triggerPtr);
+ }
+}
+
+void
+Dbtup::executeTuxAbortTriggers(Signal* signal,
+ Operationrec* regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ // get version
+ Uint32 tupVersion;
+ if (regOperPtr->optype == ZINSERT) {
+ ljam();
+ tupVersion = regOperPtr->tupVersion;
+ } else if (regOperPtr->optype == ZUPDATE) {
+ ljam();
+ tupVersion = regOperPtr->tupVersion;
+ } else if (regOperPtr->optype == ZDELETE) {
+ ljam();
+ return;
+ } else {
+ ndbrequire(false);
+ tupVersion= 0; // remove warning
+ }
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpRemove;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL,
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ // abort must succeed
+ ndbrequire(req->errorCode == 0);
+ triggerList.next(triggerPtr);
+ }
+}