diff options
author | unknown <pekka@mysql.com> | 2006-01-25 22:39:00 +0100 |
---|---|---|
committer | unknown <pekka@mysql.com> | 2006-01-25 22:39:00 +0100 |
commit | 63a80b964f925000958d32e8f5a014b9e112e85d (patch) | |
tree | 6dc3b802fea8d098ea8c04f558e69776638a95b6 /storage | |
parent | 0e155240e97a69d2d8ddf60be771338618eeab2a (diff) | |
parent | 465960c2e3a5c9bf69e4ac25aec56081ec4edc4a (diff) | |
download | mariadb-git-63a80b964f925000958d32e8f5a014b9e112e85d.tar.gz |
Merge pnousiainen@bk-internal.mysql.com:/home/bk/mysql-5.1-new
into mysql.com:/space/pekka/ndb/version/my51
mysql-test/t/disabled.def:
Auto merged
Diffstat (limited to 'storage')
-rw-r--r-- | storage/ndb/include/ndbapi/NdbBlob.hpp | 38 | ||||
-rw-r--r-- | storage/ndb/include/ndbapi/NdbDictionary.hpp | 22 | ||||
-rw-r--r-- | storage/ndb/include/ndbapi/NdbEventOperation.hpp | 8 | ||||
-rw-r--r-- | storage/ndb/ndbapi-examples/ndbapi_event/Makefile | 6 | ||||
-rw-r--r-- | storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp | 169 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbBlob.cpp | 271 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbDictionary.cpp | 11 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp | 132 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp | 8 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbEventOperation.cpp | 12 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp | 501 | ||||
-rw-r--r-- | storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp | 36 | ||||
-rw-r--r-- | storage/ndb/test/ndbapi/test_event_merge.cpp | 174 |
13 files changed, 1153 insertions, 235 deletions
diff --git a/storage/ndb/include/ndbapi/NdbBlob.hpp b/storage/ndb/include/ndbapi/NdbBlob.hpp index cb0caafe34f..5d6022862b8 100644 --- a/storage/ndb/include/ndbapi/NdbBlob.hpp +++ b/storage/ndb/include/ndbapi/NdbBlob.hpp @@ -28,6 +28,7 @@ class NdbOperation; class NdbRecAttr; class NdbTableImpl; class NdbColumnImpl; +class NdbEventOperationImpl; /** * @class NdbBlob @@ -71,6 +72,10 @@ class NdbColumnImpl; * writes. It avoids execute penalty if nothing is pending. It is not * needed after execute (obviously) or after next scan result. * + * NdbBlob also supports reading post or pre blob data from events. The + * handle can be read after next event on main table has been retrieved. + * The data is available immediately. See NdbEventOperation. + * * NdbBlob methods return -1 on error and 0 on success, and use output * parameters when necessary. * @@ -146,6 +151,12 @@ public: */ int setActiveHook(ActiveHook* activeHook, void* arg); /** + * Check if blob value is defined (NULL or not). Used as first call + * on event based blob. The argument is set to -1 for not defined. + * Unlike getNull() this does not cause error on the handle. + */ + int getDefined(int& isNull); + /** * Check if blob is null. */ int getNull(bool& isNull); @@ -192,6 +203,11 @@ public: */ static int getBlobTableName(char* btname, Ndb* anNdb, const char* tableName, const char* columnName); /** + * Get blob event name. The blob event is created if the main event + * monitors the blob column. The name includes main event name. + */ + static int getBlobEventName(char* bename, Ndb* anNdb, const char* eventName, const char* columnName); + /** * Return error object. The error may be blob specific (below) or may * be copied from a failed implicit operation. */ @@ -217,17 +233,29 @@ private: friend class NdbScanOperation; friend class NdbDictionaryImpl; friend class NdbResultSet; // atNextResult + friend class NdbEventBuffer; + friend class NdbEventOperationImpl; #endif // state State theState; void setState(State newState); + // quick and dirty support for events (consider subclassing) + int theEventBlobVersion; // -1=normal blob 0=post event 1=pre event // define blob table static void getBlobTableName(char* btname, const NdbTableImpl* t, const NdbColumnImpl* c); static void getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnImpl* c); + static void getBlobEventName(char* bename, const NdbEventImpl* e, const NdbColumnImpl* c); + static void getBlobEvent(NdbEventImpl& be, const NdbEventImpl* e, const NdbColumnImpl* c); // ndb api stuff Ndb* theNdb; NdbTransaction* theNdbCon; NdbOperation* theNdbOp; + NdbEventOperationImpl* theEventOp; + NdbEventOperationImpl* theBlobEventOp; + NdbRecAttr* theBlobEventPkRecAttr; + NdbRecAttr* theBlobEventDistRecAttr; + NdbRecAttr* theBlobEventPartRecAttr; + NdbRecAttr* theBlobEventDataRecAttr; const NdbTableImpl* theTable; const NdbTableImpl* theAccessTable; const NdbTableImpl* theBlobTable; @@ -263,6 +291,8 @@ private: Buf theHeadInlineBuf; Buf theHeadInlineCopyBuf; // for writeTuple Buf thePartBuf; + Buf theBlobEventDataBuf; + Uint32 thePartNumber; // for event Head* theHead; char* theInlineData; NdbRecAttr* theHeadInlineRecAttr; @@ -306,6 +336,8 @@ private: int readDataPrivate(char* buf, Uint32& bytes); int writeDataPrivate(const char* buf, Uint32 bytes); int readParts(char* buf, Uint32 part, Uint32 count); + int readTableParts(char* buf, Uint32 part, Uint32 count); + int readEventParts(char* buf, Uint32 part, Uint32 count); int insertParts(const char* buf, Uint32 part, Uint32 count); int updateParts(const char* buf, Uint32 part, Uint32 count); int deleteParts(Uint32 part, Uint32 count); @@ -317,19 +349,23 @@ private: int invokeActiveHook(); // blob handle maintenance int atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl* aColumn); + int atPrepare(NdbEventOperationImpl* anOp, NdbEventOperationImpl* aBlobOp, const NdbColumnImpl* aColumn, int version); + int prepareColumn(); int preExecute(NdbTransaction::ExecType anExecType, bool& batch); int postExecute(NdbTransaction::ExecType anExecType); int preCommit(); int atNextResult(); + int atNextEvent(); // errors void setErrorCode(int anErrorCode, bool invalidFlag = true); void setErrorCode(NdbOperation* anOp, bool invalidFlag = true); void setErrorCode(NdbTransaction* aCon, bool invalidFlag = true); + void setErrorCode(NdbEventOperationImpl* anOp, bool invalidFlag = true); #ifdef VM_TRACE int getOperationType() const; friend class NdbOut& operator<<(NdbOut&, const NdbBlob&); #endif - + // list stuff void next(NdbBlob* obj) { theNext= obj;} NdbBlob* next() { return theNext;} friend struct Ndb_free_list_t<NdbBlob>; diff --git a/storage/ndb/include/ndbapi/NdbDictionary.hpp b/storage/ndb/include/ndbapi/NdbDictionary.hpp index 6f8ead63a81..4196328976a 100644 --- a/storage/ndb/include/ndbapi/NdbDictionary.hpp +++ b/storage/ndb/include/ndbapi/NdbDictionary.hpp @@ -883,6 +883,7 @@ public: private: #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + friend class NdbDictionaryImpl; friend class NdbTableImpl; #endif class NdbTableImpl & m_impl; @@ -1124,7 +1125,7 @@ public: _TE_NODE_FAILURE=10, _TE_SUBSCRIBE=11, _TE_UNSUBSCRIBE=12, - _TE_NUL=13 // internal (INS o DEL within same GCI) + _TE_NUL=13 // internal (e.g. INS o DEL within same GCI) }; #endif /** @@ -1262,6 +1263,24 @@ public: int getNoOfEventColumns() const; /** + * The merge events flag is false by default. Setting it true + * implies that events are merged in following ways: + * + * - for given NdbEventOperation associated with this event, + * events on same PK within same GCI are merged into single event + * + * - a blob table event is created for each blob attribute + * and blob events are handled as part of main table events + * + * - blob post/pre data from the blob part events can be read + * via NdbBlob methods as a single value + * + * NOTE: Currently this flag is not inherited by NdbEventOperation + * and must be set on NdbEventOperation explicitly. + */ + void mergeEvents(bool flag); + + /** * Get object status */ virtual Object::Status getObjectStatus() const; @@ -1746,6 +1765,7 @@ public: #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL const Table * getTable(const char * name, void **data) const; void set_local_table_data_size(unsigned sz); + void fix_blob_events(const Table* table, const char* ev_name); #endif }; }; diff --git a/storage/ndb/include/ndbapi/NdbEventOperation.hpp b/storage/ndb/include/ndbapi/NdbEventOperation.hpp index 5992fc2b036..4a9dd6cd295 100644 --- a/storage/ndb/include/ndbapi/NdbEventOperation.hpp +++ b/storage/ndb/include/ndbapi/NdbEventOperation.hpp @@ -150,6 +150,14 @@ public: */ NdbRecAttr *getPreValue(const char *anAttrName, char *aValue = 0); + /** + * These methods replace getValue/getPreValue for blobs. Each + * method creates a blob handle NdbBlob. The handle supports only + * read operations. See NdbBlob. + */ + NdbBlob* getBlobHandle(const char *anAttrName); + NdbBlob* getPreBlobHandle(const char *anAttrName); + int isOverrun() const; /** diff --git a/storage/ndb/ndbapi-examples/ndbapi_event/Makefile b/storage/ndb/ndbapi-examples/ndbapi_event/Makefile index 54c4c903a56..a3ca6fd61e3 100644 --- a/storage/ndb/ndbapi-examples/ndbapi_event/Makefile +++ b/storage/ndb/ndbapi-examples/ndbapi_event/Makefile @@ -4,7 +4,7 @@ OBJS = ndbapi_event.o CXX = g++ -g CFLAGS = -c -Wall -fno-rtti -fno-exceptions CXXFLAGS = -DEBUG = +DEBUG =# -DVM_TRACE LFLAGS = -Wall TOP_SRCDIR = ../../../.. INCLUDE_DIR = $(TOP_SRCDIR)/storage/ndb/include @@ -16,8 +16,8 @@ SYS_LIB = $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) $(LFLAGS) $(LIB_DIR) $(OBJS) -lndbclient -lmysqlclient_r -lmysys -lmystrings -lz $(SYS_LIB) -o $(TARGET) -$(TARGET).o: $(SRCS) - $(CXX) $(CFLAGS) -I$(INCLUDE_DIR) -I$(INCLUDE_DIR)/ndbapi -I$(TOP_SRCDIR)/include $(SRCS) +$(TARGET).o: $(SRCS) Makefile + $(CXX) $(CFLAGS) $(DEBUG) -I$(INCLUDE_DIR) -I$(INCLUDE_DIR)/ndbapi -I$(TOP_SRCDIR)/include $(SRCS) clean: rm -f *.o $(TARGET) diff --git a/storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp b/storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp index 98b34e51e27..328d43caf28 100644 --- a/storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp +++ b/storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event.cpp @@ -54,26 +54,32 @@ #include <stdio.h> #include <iostream> #include <unistd.h> +#ifdef VM_TRACE +#include <my_global.h> +#endif +#ifndef assert +#include <assert.h> +#endif /** - * - * Assume that there is a table t0 which is being updated by + * Assume that there is a table which is being updated by * another process (e.g. flexBench -l 0 -stdtables). - * We want to monitor what happens with columns c0,c1,c2,c3. + * We want to monitor what happens with column values. * - * or together with the mysql client; + * Or using the mysql client: * * shell> mysql -u root * mysql> create database TEST_DB; * mysql> use TEST_DB; - * mysql> create table t0 (c0 int, c1 int, c2 char(4), c3 char(4), + * mysql> create table t0 + * (c0 int, c1 int, c2 char(4), c3 char(4), c4 text, * primary key(c0, c2)) engine ndb charset latin1; * * In another window start ndbapi_event, wait until properly started - * - insert into t0 values (1, 2, 'a', 'b'); - insert into t0 values (3, 4, 'c', 'd'); + + insert into t0 values (1, 2, 'a', 'b', null); + insert into t0 values (3, 4, 'c', 'd', null); update t0 set c3 = 'e' where c0 = 1 and c2 = 'a'; -- use pk update t0 set c3 = 'f'; -- use scan update t0 set c3 = 'F'; -- use scan update to 'same' @@ -81,7 +87,18 @@ update t0 set c2 = 'G' where c0 = 1; -- update pk part to 'same' update t0 set c0 = 5, c2 = 'H' where c0 = 3; -- update full PK delete from t0; - * + + insert ...; update ...; -- see events w/ same pk merged (if -m option) + delete ...; insert ...; -- there are 5 combinations ID IU DI UD UU + update ...; update ...; + + -- text requires -m flag + set @a = repeat('a',256); -- inline size + set @b = repeat('b',2000); -- part size + set @c = repeat('c',2000*30); -- 30 parts + + -- update the text field using combinations of @a, @b, @c ... + * you should see the data popping up in the example window * */ @@ -95,12 +112,18 @@ int myCreateEvent(Ndb* myNdb, const char *eventName, const char *eventTableName, const char **eventColumnName, - const int noEventColumnName); + const int noEventColumnName, + bool merge_events); int main(int argc, char** argv) { ndb_init(); - bool merge_events = argc > 1 && strcmp(argv[1], "-m") == 0; + bool merge_events = argc > 1 && strchr(argv[1], 'm') != 0; +#ifdef VM_TRACE + bool dbug = argc > 1 && strchr(argv[1], 'd') != 0; + if (dbug) DBUG_PUSH("d:t:"); + if (dbug) putenv("API_SIGNAL_LOG=-"); +#endif Ndb_cluster_connection *cluster_connection= new Ndb_cluster_connection(); // Object representing the cluster @@ -134,12 +157,13 @@ int main(int argc, char** argv) const char *eventName= "CHNG_IN_t0"; const char *eventTableName= "t0"; - const int noEventColumnName= 4; + const int noEventColumnName= 5; const char *eventColumnName[noEventColumnName]= {"c0", "c1", "c2", - "c3" + "c3", + "c4" }; // Create events @@ -147,9 +171,14 @@ int main(int argc, char** argv) eventName, eventTableName, eventColumnName, - noEventColumnName); + noEventColumnName, + merge_events); + + // Normal values and blobs are unfortunately handled differently.. + typedef union { NdbRecAttr* ra; NdbBlob* bh; } RA_BH; - int j= 0; + int i, j, k, l; + j = 0; while (j < 99) { // Start "transaction" for handling events @@ -160,12 +189,17 @@ int main(int argc, char** argv) op->mergeEvents(merge_events); printf("get values\n"); - NdbRecAttr* recAttr[noEventColumnName]; - NdbRecAttr* recAttrPre[noEventColumnName]; + RA_BH recAttr[noEventColumnName]; + RA_BH recAttrPre[noEventColumnName]; // primary keys should always be a part of the result - for (int i = 0; i < noEventColumnName; i++) { - recAttr[i] = op->getValue(eventColumnName[i]); - recAttrPre[i] = op->getPreValue(eventColumnName[i]); + for (i = 0; i < noEventColumnName; i++) { + if (i < 4) { + recAttr[i].ra = op->getValue(eventColumnName[i]); + recAttrPre[i].ra = op->getPreValue(eventColumnName[i]); + } else if (merge_events) { + recAttr[i].bh = op->getBlobHandle(eventColumnName[i]); + recAttrPre[i].bh = op->getPreBlobHandle(eventColumnName[i]); + } } // set up the callbacks @@ -174,13 +208,16 @@ int main(int argc, char** argv) if (op->execute()) APIERROR(op->getNdbError()); - int i= 0; - while(i < 40) { + NdbEventOperation* the_op = op; + + i= 0; + while (i < 40) { // printf("now waiting for event...\n"); - int r= myNdb->pollEvents(1000); // wait for event or 1000 ms + int r = myNdb->pollEvents(1000); // wait for event or 1000 ms if (r > 0) { // printf("got data! %d\n", r); while ((op= myNdb->nextEvent())) { + assert(the_op == op); i++; switch (op->getEventType()) { case NdbDictionary::Event::TE_INSERT: @@ -195,40 +232,66 @@ int main(int argc, char** argv) default: abort(); // should not happen } - printf(" gci=%d\n", op->getGCI()); - printf("post: "); - for (int i = 0; i < noEventColumnName; i++) { - if (recAttr[i]->isNULL() >= 0) { // we have a value - if (recAttr[i]->isNULL() == 0) { // we have a non-null value - if (i < 2) - printf("%-5u", recAttr[i]->u_32_value()); - else - printf("%-5.4s", recAttr[i]->aRef()); - } else // we have a null value - printf("%-5s", "NULL"); - } else - printf("%-5s", "-"); + printf(" gci=%d\n", (int)op->getGCI()); + for (k = 0; k <= 1; k++) { + printf(k == 0 ? "post: " : "pre : "); + for (l = 0; l < noEventColumnName; l++) { + if (l < 4) { + NdbRecAttr* ra = k == 0 ? recAttr[l].ra : recAttrPre[l].ra; + if (ra->isNULL() >= 0) { // we have a value + if (ra->isNULL() == 0) { // we have a non-null value + if (l < 2) + printf("%-5u", ra->u_32_value()); + else + printf("%-5.4s", ra->aRef()); + } else + printf("%-5s", "NULL"); + } else + printf("%-5s", "-"); // no value + } else if (merge_events) { + int isNull; + NdbBlob* bh = k == 0 ? recAttr[l].bh : recAttrPre[l].bh; + bh->getDefined(isNull); + if (isNull >= 0) { // we have a value + if (! isNull) { // we have a non-null value + Uint64 length = 0; + bh->getLength(length); + // read into buffer + unsigned char* buf = new unsigned char [length]; + memset(buf, 'X', length); + Uint32 n = length; + bh->readData(buf, n); // n is in/out + assert(n == length); + // pretty-print + bool first = true; + Uint32 i = 0; + while (i < n) { + unsigned char c = buf[i++]; + Uint32 m = 1; + while (i < n && buf[i] == c) + i++, m++; + if (! first) + printf("+"); + printf("%u%c", m, c); + first = false; + } + printf("[%u]", n); + delete [] buf; + } else + printf("%-5s", "NULL"); + } else + printf("%-5s", "-"); // no value + } + } + printf("\n"); } - printf("\npre : "); - for (int i = 0; i < noEventColumnName; i++) { - if (recAttrPre[i]->isNULL() >= 0) { // we have a value - if (recAttrPre[i]->isNULL() == 0) { // we have a non-null value - if (i < 2) - printf("%-5u", recAttrPre[i]->u_32_value()); - else - printf("%-5.4s", recAttrPre[i]->aRef()); - } else // we have a null value - printf("%-5s", "NULL"); - } else - printf("%-5s", "-"); - } - printf("\n"); } } else ;//printf("timed out\n"); } // don't want to listen to events anymore - if (myNdb->dropEventOperation(op)) APIERROR(myNdb->getNdbError()); + if (myNdb->dropEventOperation(the_op)) APIERROR(myNdb->getNdbError()); + the_op = 0; j++; } @@ -250,7 +313,8 @@ int myCreateEvent(Ndb* myNdb, const char *eventName, const char *eventTableName, const char **eventColumnNames, - const int noEventColumnNames) + const int noEventColumnNames, + bool merge_events) { NdbDictionary::Dictionary *myDict= myNdb->getDictionary(); if (!myDict) APIERROR(myNdb->getNdbError()); @@ -265,6 +329,7 @@ int myCreateEvent(Ndb* myNdb, // myEvent.addTableEvent(NdbDictionary::Event::TE_DELETE); myEvent.addEventColumns(noEventColumnNames, eventColumnNames); + myEvent.mergeEvents(merge_events); // Add event to database if (myDict->createEvent(myEvent) == 0) diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp index 2f5325bd844..19a86cbf557 100644 --- a/storage/ndb/src/ndbapi/NdbBlob.cpp +++ b/storage/ndb/src/ndbapi/NdbBlob.cpp @@ -23,6 +23,7 @@ #include <NdbBlob.hpp> #include "NdbBlobImpl.hpp" #include <NdbScanOperation.hpp> +#include <NdbEventOperationImpl.hpp> /* * Reading index table directly (as a table) is faster but there are @@ -147,6 +148,61 @@ NdbBlob::getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnIm DBUG_VOID_RETURN; } +int +NdbBlob::getBlobEventName(char* bename, Ndb* anNdb, const char* eventName, const char* columnName) +{ + NdbEventImpl* e = anNdb->theDictionary->m_impl.getEvent(eventName); + if (e == NULL) + return -1; + NdbColumnImpl* c = e->m_tableImpl->getColumn(columnName); + if (c == NULL) + return -1; + getBlobEventName(bename, e, c); + return 0; +} + +void +NdbBlob::getBlobEventName(char* bename, const NdbEventImpl* e, const NdbColumnImpl* c) +{ + // XXX events should have object id + snprintf(bename, MAX_TAB_NAME_SIZE, "NDB$BLOBEVENT_%s_%d", e->m_name.c_str(), (int)c->m_column_no); +} + +void +NdbBlob::getBlobEvent(NdbEventImpl& be, const NdbEventImpl* e, const NdbColumnImpl* c) +{ + DBUG_ENTER("NdbBlob::getBlobEvent"); + // blob table + assert(c->m_blobTable != NULL); + const NdbTableImpl& bt = *c->m_blobTable; + // blob event name + char bename[NdbBlobImpl::BlobTableNameSize]; + getBlobEventName(bename, e, c); + be.setName(bename); + be.setTable(bt); + // simple assigments + be.mi_type = e->mi_type; + be.m_dur = e->m_dur; + be.m_mergeEvents = e->m_mergeEvents; + // report unchanged data + // not really needed now since UPD is DEL o INS and we subscribe to all + be.setReport(NdbDictionary::Event::ER_ALL); + // columns PK - DIST - PART - DATA + { const NdbColumnImpl* bc = bt.getColumn((Uint32)0); + be.addColumn(*bc); + } + { const NdbColumnImpl* bc = bt.getColumn((Uint32)1); + be.addColumn(*bc); + } + { const NdbColumnImpl* bc = bt.getColumn((Uint32)2); + be.addColumn(*bc); + } + { const NdbColumnImpl* bc = bt.getColumn((Uint32)3); + be.addColumn(*bc); + } + DBUG_VOID_RETURN; +} + // initialization NdbBlob::NdbBlob(Ndb*) @@ -158,9 +214,16 @@ void NdbBlob::init() { theState = Idle; + theEventBlobVersion = -1; theNdb = NULL; theNdbCon = NULL; theNdbOp = NULL; + theEventOp = NULL; + theBlobEventOp = NULL; + theBlobEventPkRecAttr = NULL; + theBlobEventDistRecAttr = NULL; + theBlobEventPartRecAttr = NULL; + theBlobEventDataRecAttr = NULL; theTable = NULL; theAccessTable = NULL; theBlobTable = NULL; @@ -439,7 +502,7 @@ NdbBlob::getHeadFromRecAttr() DBUG_ENTER("NdbBlob::getHeadFromRecAttr"); assert(theHeadInlineRecAttr != NULL); theNullFlag = theHeadInlineRecAttr->isNULL(); - assert(theNullFlag != -1); + assert(theEventBlobVersion >= 0 || theNullFlag != -1); theLength = ! theNullFlag ? theHead->length : 0; DBUG_VOID_RETURN; } @@ -544,6 +607,18 @@ NdbBlob::setActiveHook(ActiveHook activeHook, void* arg) // misc operations int +NdbBlob::getDefined(int& isNull) +{ + DBUG_ENTER("NdbBlob::getDefined"); + if (theState == Prepared && theSetFlag) { + isNull = (theSetBuf == NULL); + DBUG_RETURN(0); + } + isNull = theNullFlag; + DBUG_RETURN(0); +} + +int NdbBlob::getNull(bool& isNull) { DBUG_ENTER("NdbBlob::getNull"); @@ -887,6 +962,18 @@ NdbBlob::readParts(char* buf, Uint32 part, Uint32 count) { DBUG_ENTER("NdbBlob::readParts"); DBUG_PRINT("info", ("part=%u count=%u", part, count)); + int ret; + if (theEventBlobVersion == -1) + ret = readTableParts(buf, part, count); + else + ret = readEventParts(buf, part, count); + DBUG_RETURN(ret); +} + +int +NdbBlob::readTableParts(char* buf, Uint32 part, Uint32 count) +{ + DBUG_ENTER("NdbBlob::readTableParts"); Uint32 n = 0; while (n < count) { NdbOperation* tOp = theNdbCon->getNdbOperation(theBlobTable); @@ -907,6 +994,18 @@ NdbBlob::readParts(char* buf, Uint32 part, Uint32 count) } int +NdbBlob::readEventParts(char* buf, Uint32 part, Uint32 count) +{ + DBUG_ENTER("NdbBlob::readEventParts"); + int ret = theEventOp->readBlobParts(buf, this, part, count); + if (ret != 0) { + setErrorCode(theEventOp); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} + +int NdbBlob::insertParts(const char* buf, Uint32 part, Uint32 count) { DBUG_ENTER("NdbBlob::insertParts"); @@ -1094,48 +1193,12 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl theTable = anOp->m_currentTable; theAccessTable = anOp->m_accessTable; theColumn = aColumn; - NdbDictionary::Column::Type partType = NdbDictionary::Column::Undefined; - switch (theColumn->getType()) { - case NdbDictionary::Column::Blob: - partType = NdbDictionary::Column::Binary; - theFillChar = 0x0; - break; - case NdbDictionary::Column::Text: - partType = NdbDictionary::Column::Char; - theFillChar = 0x20; - break; - default: - setErrorCode(NdbBlobImpl::ErrUsage); + // prepare blob column and table + if (prepareColumn() == -1) DBUG_RETURN(-1); - } - // sizes - theInlineSize = theColumn->getInlineSize(); - thePartSize = theColumn->getPartSize(); - theStripeSize = theColumn->getStripeSize(); - // sanity check - assert((NDB_BLOB_HEAD_SIZE << 2) == sizeof(Head)); - assert(theColumn->m_attrSize * theColumn->m_arraySize == sizeof(Head) + theInlineSize); - if (thePartSize > 0) { - const NdbDictionary::Table* bt = NULL; - const NdbDictionary::Column* bc = NULL; - if (theStripeSize == 0 || - (bt = theColumn->getBlobTable()) == NULL || - (bc = bt->getColumn("DATA")) == NULL || - bc->getType() != partType || - bc->getLength() != (int)thePartSize) { - setErrorCode(NdbBlobImpl::ErrTable); - DBUG_RETURN(-1); - } - theBlobTable = &NdbTableImpl::getImpl(*bt); - } - // buffers - theKeyBuf.alloc(theTable->m_keyLenInWords << 2); + // extra buffers theAccessKeyBuf.alloc(theAccessTable->m_keyLenInWords << 2); - theHeadInlineBuf.alloc(sizeof(Head) + theInlineSize); theHeadInlineCopyBuf.alloc(sizeof(Head) + theInlineSize); - thePartBuf.alloc(thePartSize); - theHead = (Head*)theHeadInlineBuf.data; - theInlineData = theHeadInlineBuf.data + sizeof(Head); // handle different operation types bool supportedOp = false; if (isKeyOp()) { @@ -1189,6 +1252,103 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl DBUG_RETURN(0); } +int +NdbBlob::atPrepare(NdbEventOperationImpl* anOp, NdbEventOperationImpl* aBlobOp, const NdbColumnImpl* aColumn, int version) +{ + DBUG_ENTER("NdbBlob::atPrepare [event]"); + DBUG_PRINT("info", ("this=%p op=%p", this, anOp)); + assert(theState == Idle); + assert(version == 0 || version == 1); + theEventBlobVersion = version; + // ndb api stuff + theNdb = anOp->m_ndb; + theEventOp = anOp; + theBlobEventOp = aBlobOp; + theTable = anOp->m_eventImpl->m_tableImpl; + theColumn = aColumn; + // prepare blob column and table + if (prepareColumn() == -1) + DBUG_RETURN(-1); + // tinyblob sanity + assert((theBlobEventOp == NULL) == (theBlobTable == NULL)); + // extra buffers + theBlobEventDataBuf.alloc(thePartSize); + // prepare receive of head+inline + theHeadInlineRecAttr = theEventOp->getValue(aColumn, theHeadInlineBuf.data, version); + if (theHeadInlineRecAttr == NULL) { + setErrorCode(theEventOp); + DBUG_RETURN(-1); + } + // prepare receive of blob part + if (theBlobEventOp != NULL) { + if ((theBlobEventPkRecAttr = + theBlobEventOp->getValue(theBlobTable->getColumn((Uint32)0), + theKeyBuf.data, version)) == NULL || + (theBlobEventDistRecAttr = + theBlobEventOp->getValue(theBlobTable->getColumn((Uint32)1), + (char*)0, version)) == NULL || + (theBlobEventPartRecAttr = + theBlobEventOp->getValue(theBlobTable->getColumn((Uint32)2), + (char*)&thePartNumber, version)) == NULL || + (theBlobEventDataRecAttr = + theBlobEventOp->getValue(theBlobTable->getColumn((Uint32)3), + theBlobEventDataBuf.data, version)) == NULL) { + setErrorCode(theBlobEventOp); + DBUG_RETURN(-1); + } + } + setState(Prepared); + DBUG_RETURN(0); +} + +int +NdbBlob::prepareColumn() +{ + DBUG_ENTER("prepareColumn"); + NdbDictionary::Column::Type partType = NdbDictionary::Column::Undefined; + switch (theColumn->getType()) { + case NdbDictionary::Column::Blob: + partType = NdbDictionary::Column::Binary; + theFillChar = 0x0; + break; + case NdbDictionary::Column::Text: + partType = NdbDictionary::Column::Char; + theFillChar = 0x20; + break; + default: + setErrorCode(NdbBlobImpl::ErrUsage); + DBUG_RETURN(-1); + } + // sizes + theInlineSize = theColumn->getInlineSize(); + thePartSize = theColumn->getPartSize(); + theStripeSize = theColumn->getStripeSize(); + // sanity check + assert((NDB_BLOB_HEAD_SIZE << 2) == sizeof(Head)); + assert(theColumn->m_attrSize * theColumn->m_arraySize == sizeof(Head) + theInlineSize); + if (thePartSize > 0) { + const NdbTableImpl* bt = NULL; + const NdbColumnImpl* bc = NULL; + if (theStripeSize == 0 || + (bt = theColumn->m_blobTable) == NULL || + (bc = bt->getColumn("DATA")) == NULL || + bc->getType() != partType || + bc->getLength() != (int)thePartSize) { + setErrorCode(NdbBlobImpl::ErrTable); + DBUG_RETURN(-1); + } + // blob table + theBlobTable = &NdbTableImpl::getImpl(*bt); + } + // these buffers are always used + theKeyBuf.alloc(theTable->m_keyLenInWords << 2); + theHeadInlineBuf.alloc(sizeof(Head) + theInlineSize); + theHead = (Head*)theHeadInlineBuf.data; + theInlineData = theHeadInlineBuf.data + sizeof(Head); + thePartBuf.alloc(thePartSize); + DBUG_RETURN(0); +} + /* * Before execute of prepared operation. May add new operations before * this one. May ask that this operation and all before it (a "batch") @@ -1537,6 +1697,26 @@ NdbBlob::atNextResult() DBUG_RETURN(0); } +/* + * After next event on main table. + */ +int +NdbBlob::atNextEvent() +{ + DBUG_ENTER("NdbBlob::atNextEvent"); + DBUG_PRINT("info", ("this=%p op=%p blob op=%p version=%d", this, theEventOp, theBlobEventOp, theEventBlobVersion)); + if (theState == Invalid) + DBUG_RETURN(-1); + assert(theEventBlobVersion >= 0); + getHeadFromRecAttr(); + if (theNullFlag == -1) // value not defined + DBUG_RETURN(0); + if (setPos(0) == -1) + DBUG_RETURN(-1); + setState(Active); + DBUG_RETURN(0); +} + // misc const NdbDictionary::Column* @@ -1589,6 +1769,17 @@ NdbBlob::setErrorCode(NdbTransaction* aCon, bool invalidFlag) setErrorCode(code, invalidFlag); } +void +NdbBlob::setErrorCode(NdbEventOperationImpl* anOp, bool invalidFlag) +{ + int code = 0; + if ((code = anOp->m_error.code) != 0) + ; + else + code = NdbBlobImpl::ErrUnknown; + setErrorCode(code, invalidFlag); +} + // info about all blobs in this operation NdbBlob* diff --git a/storage/ndb/src/ndbapi/NdbDictionary.cpp b/storage/ndb/src/ndbapi/NdbDictionary.cpp index fd11aed14e3..3dd73a57346 100644 --- a/storage/ndb/src/ndbapi/NdbDictionary.cpp +++ b/storage/ndb/src/ndbapi/NdbDictionary.cpp @@ -901,6 +901,11 @@ int NdbDictionary::Event::getNoOfEventColumns() const return m_impl.getNoOfEventColumns(); } +void NdbDictionary::Event::mergeEvents(bool flag) +{ + m_impl.m_mergeEvents = flag; +} + NdbDictionary::Object::Status NdbDictionary::Event::getObjectStatus() const { @@ -1473,6 +1478,12 @@ NdbDictionary::Dictionary::getNdbError() const { return m_impl.getNdbError(); } +void +NdbDictionary::Dictionary::fix_blob_events(const Table* table, const char* ev_name) +{ + m_impl.fix_blob_events(table, ev_name); +} + // printers NdbOut& diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp index 5716288263d..4f8ab972bd6 100644 --- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -1072,6 +1072,7 @@ void NdbEventImpl::init() m_tableId= RNIL; mi_type= 0; m_dur= NdbDictionary::Event::ED_UNDEFINED; + m_mergeEvents = false; m_tableImpl= NULL; m_rep= NdbDictionary::Event::ER_UPDATED; } @@ -2036,7 +2037,7 @@ int NdbDictionaryImpl::addBlobTables(NdbTableImpl &t) { unsigned n= t.m_noOfBlobs; - DBUG_ENTER("NdbDictioanryImpl::addBlobTables"); + DBUG_ENTER("NdbDictionaryImpl::addBlobTables"); // optimized for blob column being the last one // and not looking for more than one if not neccessary for (unsigned i = t.m_columns.size(); i > 0 && n > 0;) { @@ -3151,7 +3152,37 @@ NdbDictionaryImpl::createEvent(NdbEventImpl & evnt) #endif // NdbDictInterface m_receiver; - DBUG_RETURN(m_receiver.createEvent(m_ndb, evnt, 0 /* getFlag unset */)); + if (m_receiver.createEvent(m_ndb, evnt, 0 /* getFlag unset */) != 0) + DBUG_RETURN(-1); + + // Create blob events + if (evnt.m_mergeEvents && createBlobEvents(evnt) != 0) { + int save_code = m_error.code; + (void)dropEvent(evnt.m_name.c_str()); + m_error.code = save_code; + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} + +int +NdbDictionaryImpl::createBlobEvents(NdbEventImpl& evnt) +{ + DBUG_ENTER("NdbDictionaryImpl::createBlobEvents"); + NdbTableImpl& t = *evnt.m_tableImpl; + Uint32 n = t.m_noOfBlobs; + Uint32 i; + for (i = 0; i < evnt.m_columns.size() && n > 0; i++) { + NdbColumnImpl & c = *evnt.m_columns[i]; + if (! c.getBlobType() || c.getPartSize() == 0) + continue; + n--; + NdbEventImpl blob_evnt; + NdbBlob::getBlobEvent(blob_evnt, &evnt, &c); + if (createEvent(blob_evnt) != 0) + DBUG_RETURN(-1); + } + DBUG_RETURN(0); } int @@ -3367,12 +3398,14 @@ NdbDictionaryImpl::getEvent(const char * eventName) if (ev->m_tableId == info->m_table_impl->m_id && ev->m_tableVersion == info->m_table_impl->m_version) break; + DBUG_PRINT("error",("%s: retry=%d: " + "table version mismatch, event: [%u,%u] table: [%u,%u]", + ev->getTableName(), retry, + ev->m_tableId, ev->m_tableVersion, + info->m_table_impl->m_id, info->m_table_impl->m_version)); if (retry) { m_error.code= 241; - DBUG_PRINT("error",("%s: table version mismatch, event: [%u,%u] table: [%u,%u]", - ev->getTableName(), ev->m_tableId, ev->m_tableVersion, - info->m_table_impl->m_id, info->m_table_impl->m_version)); delete ev; DBUG_RETURN(NULL); } @@ -3400,6 +3433,7 @@ NdbDictionaryImpl::getEvent(const char * eventName) if ( attributeList_sz > table.getNoOfColumns() ) { + m_error.code = 241; DBUG_PRINT("error",("Invalid version, too many columns")); delete ev; DBUG_RETURN(NULL); @@ -3409,6 +3443,7 @@ NdbDictionaryImpl::getEvent(const char * eventName) for(unsigned id= 0; ev->m_columns.size() < attributeList_sz; id++) { if ( id >= table.getNoOfColumns()) { + m_error.code = 241; DBUG_PRINT("error",("Invalid version, column %d out of range", id)); delete ev; DBUG_RETURN(NULL); @@ -3566,13 +3601,64 @@ NdbDictInterface::execSUB_START_REF(NdbApiSignal * signal, int NdbDictionaryImpl::dropEvent(const char * eventName) { - NdbEventImpl *ev= new NdbEventImpl(); - ev->setName(eventName); - int ret= m_receiver.dropEvent(*ev); - delete ev; + DBUG_ENTER("NdbDictionaryImpl::dropEvent"); + DBUG_PRINT("info", ("name=%s", eventName)); - // printf("__________________RET %u\n", ret); - return ret; + NdbEventImpl *evnt = getEvent(eventName); // allocated + if (evnt == NULL) { + if (m_error.code != 723 && // no such table + m_error.code != 241) // invalid table + DBUG_RETURN(-1); + DBUG_PRINT("info", ("no table err=%d, drop by name alone", m_error.code)); + evnt = new NdbEventImpl(); + evnt->setName(eventName); + } + int ret = dropEvent(*evnt); + delete evnt; + DBUG_RETURN(ret); +} + +int +NdbDictionaryImpl::dropEvent(const NdbEventImpl& evnt) +{ + if (dropBlobEvents(evnt) != 0) + return -1; + if (m_receiver.dropEvent(evnt) != 0) + return -1; + return 0; +} + +int +NdbDictionaryImpl::dropBlobEvents(const NdbEventImpl& evnt) +{ + DBUG_ENTER("NdbDictionaryImpl::dropBlobEvents"); + if (evnt.m_tableImpl != 0) { + const NdbTableImpl& t = *evnt.m_tableImpl; + Uint32 n = t.m_noOfBlobs; + Uint32 i; + for (i = 0; i < evnt.m_columns.size() && n > 0; i++) { + const NdbColumnImpl& c = *evnt.m_columns[i]; + if (! c.getBlobType() || c.getPartSize() == 0) + continue; + n--; + char bename[MAX_TAB_NAME_SIZE]; + NdbBlob::getBlobEventName(bename, &evnt, &c); + (void)dropEvent(bename); + } + } else { + // loop over MAX_ATTRIBUTES_IN_TABLE ... + Uint32 i; + for (i = 0; i < MAX_ATTRIBUTES_IN_TABLE; i++) { + char bename[MAX_TAB_NAME_SIZE]; + // XXX should get name from NdbBlob + sprintf(bename, "NDB$BLOBEVENT_%s_%u", evnt.getName(), i); + NdbEventImpl* bevnt = new NdbEventImpl(); + bevnt->setName(bename); + (void)m_receiver.dropEvent(*bevnt); + delete bevnt; + } + } + DBUG_RETURN(0); } int @@ -4557,6 +4643,30 @@ NdbDictInterface::parseFileInfo(NdbFileImpl &dst, return 0; } +// XXX temp +void +NdbDictionaryImpl::fix_blob_events(const NdbDictionary::Table* table, const char* ev_name) +{ + const NdbTableImpl& t = table->m_impl; + const NdbEventImpl* ev = getEvent(ev_name); + assert(ev != NULL && ev->m_tableImpl == &t); + Uint32 i; + for (i = 0; i < t.m_columns.size(); i++) { + assert(t.m_columns[i] != NULL); + const NdbColumnImpl& c = *t.m_columns[i]; + if (! c.getBlobType() || c.getPartSize() == 0) + continue; + char bename[200]; + NdbBlob::getBlobEventName(bename, ev, &c); + // following fixes dict cache blob table + NdbEventImpl* bev = getEvent(bename); + if (c.m_blobTable != bev->m_tableImpl) { + // XXX const violation + ((NdbColumnImpl*)&c)->m_blobTable = bev->m_tableImpl; + } + } +} + template class Vector<int>; template class Vector<Uint16>; template class Vector<Uint32>; diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp index f812860c164..bd59fc8cc5b 100644 --- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp @@ -277,7 +277,6 @@ public: NdbDictionary::Event::EventDurability getDurability() const; void setReport(NdbDictionary::Event::EventReport r); NdbDictionary::Event::EventReport getReport() const; - void addEventColumn(const NdbColumnImpl &c); int getNoOfEventColumns() const; void print() { @@ -295,6 +294,7 @@ public: Uint32 mi_type; NdbDictionary::Event::EventDurability m_dur; NdbDictionary::Event::EventReport m_rep; + bool m_mergeEvents; NdbTableImpl *m_tableImpl; BaseString m_tableName; @@ -547,7 +547,10 @@ public: NdbTableImpl * table); int createEvent(NdbEventImpl &); + int createBlobEvents(NdbEventImpl &); int dropEvent(const char * eventName); + int dropEvent(const NdbEventImpl &); + int dropBlobEvents(const NdbEventImpl &); int executeSubscribeEvent(NdbEventOperationImpl &); int stopSubscribeEvent(NdbEventOperationImpl &); @@ -589,6 +592,9 @@ public: NdbDictInterface m_receiver; Ndb & m_ndb; + + // XXX temp + void fix_blob_events(const NdbDictionary::Table* table, const char* ev_name); private: NdbIndexImpl * getIndexImpl(const char * name, const BaseString& internalName); diff --git a/storage/ndb/src/ndbapi/NdbEventOperation.cpp b/storage/ndb/src/ndbapi/NdbEventOperation.cpp index e7cc4abbb2a..22e4794775f 100644 --- a/storage/ndb/src/ndbapi/NdbEventOperation.cpp +++ b/storage/ndb/src/ndbapi/NdbEventOperation.cpp @@ -55,6 +55,18 @@ NdbEventOperation::getPreValue(const char *colName, char *aValue) return m_impl.getValue(colName, aValue, 1); } +NdbBlob * +NdbEventOperation::getBlobHandle(const char *colName) +{ + return m_impl.getBlobHandle(colName, 0); +} + +NdbBlob * +NdbEventOperation::getPreBlobHandle(const char *colName) +{ + return m_impl.getBlobHandle(colName, 1); +} + int NdbEventOperation::execute() { diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp index 94022d9a737..822a223cf5a 100644 --- a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp +++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp @@ -38,6 +38,7 @@ #include "DictCache.hpp" #include <portlib/NdbMem.h> #include <NdbRecAttr.hpp> +#include <NdbBlob.hpp> #include <NdbEventOperation.hpp> #include "NdbEventOperationImpl.hpp" @@ -48,6 +49,20 @@ static Gci_container g_empty_gci_container; static const Uint32 ACTIVE_GCI_DIRECTORY_SIZE = 4; static const Uint32 ACTIVE_GCI_MASK = ACTIVE_GCI_DIRECTORY_SIZE - 1; +#ifdef VM_TRACE +static void +print_std(const SubTableData * sdata, LinearSectionPtr ptr[3]) +{ + printf("addr=%p gci=%d op=%d\n", (void*)sdata, sdata->gci, sdata->operation); + for (int i = 0; i <= 2; i++) { + printf("sec=%d addr=%p sz=%d\n", i, (void*)ptr[i].p, ptr[i].sz); + for (int j = 0; j < ptr[i].sz; j++) + printf("%08x ", ptr[i].p[j]); + printf("\n"); + } +} +#endif + /* * Class NdbEventOperationImpl * @@ -60,7 +75,7 @@ static const Uint32 ACTIVE_GCI_MASK = ACTIVE_GCI_DIRECTORY_SIZE - 1; #define DBUG_RETURN_EVENT(A) DBUG_RETURN(A) #define DBUG_VOID_RETURN_EVENT DBUG_VOID_RETURN #define DBUG_PRINT_EVENT(A,B) DBUG_PRINT(A,B) -#define DBUG_DUMP_EVENT(A,B,C) DBUG_SUMP(A,B,C) +#define DBUG_DUMP_EVENT(A,B,C) DBUG_DUMP(A,B,C) #else #define DBUG_ENTER_EVENT(A) #define DBUG_RETURN_EVENT(A) return(A) @@ -92,6 +107,11 @@ NdbEventOperationImpl::NdbEventOperationImpl(NdbEventOperation &N, theCurrentDataAttrs[0] = NULL; theFirstDataAttrs[1] = NULL; theCurrentDataAttrs[1] = NULL; + + theBlobList = NULL; + theBlobOpList = NULL; + theMainOp = NULL; + m_data_item= NULL; m_eventImpl = NULL; @@ -117,7 +137,11 @@ NdbEventOperationImpl::NdbEventOperationImpl(NdbEventOperation &N, m_state= EO_CREATED; - m_mergeEvents = false; +#ifdef ndb_event_stores_merge_events_flag + m_mergeEvents = m_eventImpl->m_mergeEvents; +#else + m_mergeEvents = false; +#endif m_has_error= 0; @@ -254,10 +278,191 @@ NdbEventOperationImpl::getValue(const NdbColumnImpl *tAttrInfo, char *aValue, in DBUG_RETURN(tAttr); } +NdbBlob* +NdbEventOperationImpl::getBlobHandle(const char *colName, int n) +{ + DBUG_ENTER("NdbEventOperationImpl::getBlobHandle (colName)"); + + assert(m_mergeEvents); + + if (m_state != EO_CREATED) { + ndbout_c("NdbEventOperationImpl::getBlobHandle may only be called between " + "instantiation and execute()"); + DBUG_RETURN(NULL); + } + + NdbColumnImpl *tAttrInfo = m_eventImpl->m_tableImpl->getColumn(colName); + + if (tAttrInfo == NULL) { + ndbout_c("NdbEventOperationImpl::getBlobHandle attribute %s not found",colName); + DBUG_RETURN(NULL); + } + + NdbBlob* bh = getBlobHandle(tAttrInfo, n); + DBUG_RETURN(bh); +} + +NdbBlob* +NdbEventOperationImpl::getBlobHandle(const NdbColumnImpl *tAttrInfo, int n) +{ + DBUG_ENTER("NdbEventOperationImpl::getBlobHandle"); + DBUG_PRINT("info", ("attr=%s post/pre=%d", tAttrInfo->m_name.c_str(), n)); + + // as in NdbOperation, create only one instance + NdbBlob* tBlob = theBlobList; + NdbBlob* tLastBlob = NULL; + while (tBlob != NULL) { + if (tBlob->theColumn == tAttrInfo && tBlob->theEventBlobVersion == n) + DBUG_RETURN(tBlob); + tLastBlob = tBlob; + tBlob = tBlob->theNext; + } + + NdbEventOperationImpl* tBlobOp = NULL; + + const bool is_tinyblob = (tAttrInfo->getPartSize() == 0); + assert(is_tinyblob == (tAttrInfo->m_blobTable == NULL)); + + if (! is_tinyblob) { + // blob event name + char bename[MAX_TAB_NAME_SIZE]; + NdbBlob::getBlobEventName(bename, m_eventImpl, tAttrInfo); + + // find blob event op if any (it serves both post and pre handles) + tBlobOp = theBlobOpList; + NdbEventOperationImpl* tLastBlopOp = NULL; + while (tBlobOp != NULL) { + if (strcmp(tBlobOp->m_eventImpl->m_name.c_str(), bename) == 0) { + assert(tBlobOp->m_eventImpl->m_tableImpl == tAttrInfo->m_blobTable); + break; + } + tLastBlopOp = tBlobOp; + tBlobOp = tBlobOp->m_next; + } + + DBUG_PRINT("info", ("%s op %s", tBlobOp ? " reuse" : " create", bename)); + + // create blob event op if not found + if (tBlobOp == NULL) { + // to hide blob op it is linked under main op, not under m_ndb + NdbEventOperation* tmp = + m_ndb->theEventBuffer->createEventOperation(bename, m_error); + if (tmp == NULL) + DBUG_RETURN(NULL); + tBlobOp = &tmp->m_impl; + + // pointer to main table op + tBlobOp->theMainOp = this; + tBlobOp->m_mergeEvents = m_mergeEvents; + + // add to list end + if (tLastBlopOp == NULL) + theBlobOpList = tBlobOp; + else + tLastBlopOp->m_next = tBlobOp; + tBlobOp->m_next = NULL; + } + } + + tBlob = m_ndb->getNdbBlob(); + if (tBlob == NULL) + DBUG_RETURN(NULL); + + // calls getValue on inline and blob part + if (tBlob->atPrepare(this, tBlobOp, tAttrInfo, n) == -1) { + m_ndb->releaseNdbBlob(tBlob); + DBUG_RETURN(NULL); + } + + // add to list end + if (tLastBlob == NULL) + theBlobList = tBlob; + else + tLastBlob->theNext = tBlob; + tBlob->theNext = NULL; + DBUG_RETURN(tBlob); +} + +int +NdbEventOperationImpl::readBlobParts(char* buf, NdbBlob* blob, + Uint32 part, Uint32 count) +{ + DBUG_ENTER_EVENT("NdbEventOperationImpl::readBlobParts"); + DBUG_PRINT_EVENT("info", ("part=%u count=%u post/pre=%d", + part, count, blob->theEventBlobVersion)); + + NdbEventOperationImpl* blob_op = blob->theBlobEventOp; + + EventBufData* main_data = m_data_item; + DBUG_PRINT_EVENT("info", ("main_data=%p", main_data)); + assert(main_data != NULL); + + // search for blob parts list head + EventBufData* head; + assert(m_data_item != NULL); + head = m_data_item->m_next_blob; + while (head != NULL) + { + if (head->m_event_op == blob_op) + { + DBUG_PRINT_EVENT("info", ("found blob parts head %p", head)); + break; + } + head = head->m_next_blob; + } + + Uint32 nparts = 0; + EventBufData* data = head; + // XXX optimize using part no ordering + while (data != NULL) + { + /* + * Hack part no directly out of buffer since it is not returned + * in pre data (PK buglet). For part data use receive_event(). + * This means extra copy. + */ + blob_op->m_data_item = data; + int r = blob_op->receive_event(); + assert(r > 0); + Uint32 no = data->get_blob_part_no(); + Uint32 sz = blob->thePartSize; + const char* src = blob->theBlobEventDataBuf.data; + + DBUG_PRINT_EVENT("info", ("part_data=%p part no=%u part sz=%u", data, no, sz)); + + if (part <= no && no < part + count) + { + DBUG_PRINT_EVENT("info", ("part within read range")); + memcpy(buf + (no - part) * sz, src, sz); + nparts++; + } + else + { + DBUG_PRINT_EVENT("info", ("part outside read range")); + } + data = data->m_next; + } + assert(nparts == count); + + DBUG_RETURN_EVENT(0); +} + int NdbEventOperationImpl::execute() { DBUG_ENTER("NdbEventOperationImpl::execute"); + m_ndb->theEventBuffer->add_drop_lock(); + int r = execute_nolock(); + m_ndb->theEventBuffer->add_drop_unlock(); + DBUG_RETURN(r); +} + +int +NdbEventOperationImpl::execute_nolock() +{ + DBUG_ENTER("NdbEventOperationImpl::execute_nolock"); + DBUG_PRINT("info", ("this=%p type=%s", this, !theMainOp ? "main" : "blob")); + NdbDictionary::Dictionary *myDict = m_ndb->getDictionary(); if (!myDict) { m_error.code= m_ndb->getNdbError().code; @@ -266,18 +471,26 @@ NdbEventOperationImpl::execute() if (theFirstPkAttrs[0] == NULL && theFirstDataAttrs[0] == NULL) { // defaults to get all - } - m_ndb->theEventBuffer->add_drop_lock(); m_magic_number= NDB_EVENT_OP_MAGIC_NUMBER; m_state= EO_EXECUTING; mi_type= m_eventImpl->mi_type; m_ndb->theEventBuffer->add_op(); int r= NdbDictionaryImpl::getImpl(*myDict).executeSubscribeEvent(*this); if (r == 0) { - m_ndb->theEventBuffer->add_drop_unlock(); - DBUG_RETURN(0); + if (theMainOp == NULL) { + DBUG_PRINT("info", ("execute blob ops")); + NdbEventOperationImpl* blob_op = theBlobOpList; + while (blob_op != NULL) { + r = blob_op->execute_nolock(); + if (r != 0) + break; + blob_op = blob_op->m_next; + } + } + if (r == 0) + DBUG_RETURN(0); } //Error m_state= EO_ERROR; @@ -285,7 +498,6 @@ NdbEventOperationImpl::execute() m_magic_number= 0; m_error.code= myDict->getNdbError().code; m_ndb->theEventBuffer->remove_op(); - m_ndb->theEventBuffer->add_drop_unlock(); DBUG_RETURN(r); } @@ -709,21 +921,6 @@ NdbEventBuffer::pollEvents(int aMillisecondNumber, Uint64 *latestGCI) return ret; } -#ifdef VM_TRACE -static void -print_std(const char* tag, const SubTableData * sdata, LinearSectionPtr ptr[3]) -{ - printf("%s\n", tag); - printf("addr=%p gci=%d op=%d\n", (void*)sdata, sdata->gci, sdata->operation); - for (int i = 0; i <= 2; i++) { - printf("sec=%d addr=%p sz=%d\n", i, (void*)ptr[i].p, ptr[i].sz); - for (int j = 0; j < ptr[i].sz; j++) - printf("%08x ", ptr[i].p[j]); - printf("\n"); - } -} -#endif - NdbEventOperation * NdbEventBuffer::nextEvent() { @@ -751,6 +948,10 @@ NdbEventBuffer::nextEvent() while ((data= m_available_data.m_head)) { NdbEventOperationImpl *op= data->m_event_op; + DBUG_PRINT_EVENT("info", ("available data=%p op=%p", data, op)); + + // blob table ops must not be seen at this level + assert(op->theMainOp == NULL); // set NdbEventOperation data op->m_data_item= data; @@ -767,7 +968,10 @@ NdbEventBuffer::nextEvent() // NUL event is not returned if (data->sdata->operation == NdbDictionary::Event::_TE_NUL) + { + DBUG_PRINT_EVENT("info", ("skip _TE_NUL")); continue; + } int r= op->receive_event(); if (r > 0) @@ -777,6 +981,12 @@ NdbEventBuffer::nextEvent() #ifdef VM_TRACE m_latest_command= m_latest_command_save; #endif + NdbBlob* tBlob = op->theBlobList; + while (tBlob != NULL) + { + (void)tBlob->atNextEvent(); + tBlob = tBlob->theNext; + } DBUG_RETURN_EVENT(op->m_facade); } // the next event belonged to an event op that is no @@ -1161,7 +1371,7 @@ NdbEventBuffer::insertDataL(NdbEventOperationImpl *op, DBUG_ENTER_EVENT("NdbEventBuffer::insertDataL"); Uint64 gci= sdata->gci; - if ( likely((Uint32)op->mi_type & 1 << (Uint32)sdata->operation) ) + if ( likely((Uint32)op->mi_type & (1 << (Uint32)sdata->operation)) ) { Gci_container* bucket= find_bucket(&m_active_gci, gci); @@ -1179,9 +1389,17 @@ NdbEventBuffer::insertDataL(NdbEventOperationImpl *op, DBUG_RETURN_EVENT(0); } - bool use_hash = - op->m_mergeEvents && + const bool is_blob_event = (op->theMainOp != NULL); + const bool is_data_event = sdata->operation < NdbDictionary::Event::_TE_FIRST_NON_DATA_EVENT; + const bool use_hash = op->m_mergeEvents && is_data_event; + + if (! is_data_event && is_blob_event) + { + // currently subscribed to but not used + DBUG_PRINT_EVENT("info", ("ignore non-data event on blob table")); + DBUG_RETURN_EVENT(0); + } // find position in bucket hash table EventBufData* data = 0; @@ -1201,16 +1419,43 @@ NdbEventBuffer::insertDataL(NdbEventOperationImpl *op, op->m_has_error = 2; DBUG_RETURN_EVENT(-1); } - if (unlikely(copy_data(sdata, ptr, data))) { op->m_has_error = 3; DBUG_RETURN_EVENT(-1); } - // add it to list and hash table - bucket->m_data.append(data); + data->m_event_op = op; + if (! is_blob_event || ! is_data_event) + { + bucket->m_data.append(data); + } + else + { + // find or create main event for this blob event + EventBufData_hash::Pos main_hpos; + int ret = get_main_data(bucket, main_hpos, data); + if (ret == -1) + { + op->m_has_error = 4; + DBUG_RETURN_EVENT(-1); + } + EventBufData* main_data = main_hpos.data; + if (ret != 0) // main event was created + { + main_data->m_event_op = op->theMainOp; + bucket->m_data.append(main_data); + if (use_hash) + { + main_data->m_pkhash = main_hpos.pkhash; + bucket->m_data_hash.append(main_hpos, main_data); + } + } + // link blob event under main event + add_blob_data(main_data, data); + } if (use_hash) { + data->m_pkhash = hpos.pkhash; bucket->m_data_hash.append(hpos, data); } #ifdef VM_TRACE @@ -1226,18 +1471,12 @@ NdbEventBuffer::insertDataL(NdbEventOperationImpl *op, DBUG_RETURN_EVENT(-1); } } - data->m_event_op = op; - if (use_hash) - { - data->m_pkhash = hpos.pkhash; - } DBUG_RETURN_EVENT(0); } #ifdef VM_TRACE - if ((Uint32)op->m_eventImpl->mi_type & 1 << (Uint32)sdata->operation) + if ((Uint32)op->m_eventImpl->mi_type & (1 << (Uint32)sdata->operation)) { - // XXX never reached DBUG_PRINT_EVENT("info",("Data arrived before ready eventId", op->m_eventId)); DBUG_RETURN_EVENT(0); } @@ -1300,6 +1539,8 @@ NdbEventBuffer::alloc_data() int NdbEventBuffer::alloc_mem(EventBufData* data, LinearSectionPtr ptr[3]) { + DBUG_ENTER("NdbEventBuffer::alloc_mem"); + DBUG_PRINT("info", ("ptr sz %u + %u + %u", ptr[0].sz, ptr[1].sz, ptr[2].sz)); const Uint32 min_alloc_size = 128; Uint32 sz4 = (sizeof(SubTableData) + 3) >> 2; @@ -1317,7 +1558,7 @@ NdbEventBuffer::alloc_mem(EventBufData* data, LinearSectionPtr ptr[3]) data->memory = (Uint32*)NdbMem_Allocate(alloc_size); if (data->memory == 0) - return -1; + DBUG_RETURN(-1); data->sz = alloc_size; m_total_alloc += data->sz; } @@ -1332,7 +1573,7 @@ NdbEventBuffer::alloc_mem(EventBufData* data, LinearSectionPtr ptr[3]) memptr += ptr[i].sz; } - return 0; + DBUG_RETURN(0); } int @@ -1404,13 +1645,10 @@ copy_attr(AttributeHeader ah, { Uint32 k; for (k = 0; k < n; k++) - p1[j1++] = p2[j2++]; - } - else - { - j1 += n; - j2 += n; + p1[j1 + k] = p2[j2 + k]; } + j1 += n; + j2 += n; } int @@ -1443,8 +1681,8 @@ NdbEventBuffer::merge_data(const SubTableData * const sdata, data->sz = 0; // compose ptr1 o ptr2 = ptr - LinearSectionPtr (&ptr1) [3] = olddata.ptr; - LinearSectionPtr (&ptr) [3] = data->ptr; + LinearSectionPtr (&ptr1)[3] = olddata.ptr; + LinearSectionPtr (&ptr)[3] = data->ptr; // loop twice where first loop only sets sizes int loop; @@ -1458,7 +1696,7 @@ NdbEventBuffer::merge_data(const SubTableData * const sdata, data->sdata->operation = tp->t3; } - ptr[0].sz = ptr[1].sz = ptr[3].sz = 0; + ptr[0].sz = ptr[1].sz = ptr[2].sz = 0; // copy pk from new version { @@ -1572,6 +1810,113 @@ NdbEventBuffer::merge_data(const SubTableData * const sdata, DBUG_RETURN_EVENT(0); } + +/* + * Given blob part event, find main table event on inline part. It + * should exist (force in TUP) but may arrive later. If so, create + * NUL event on main table. The real event replaces it later. + */ + +// write attribute headers for concatened PK +static void +split_concatenated_pk(const NdbTableImpl* t, Uint32* ah_buffer, + const Uint32* pk_buffer, Uint32 pk_sz) +{ + Uint32 sz = 0; // words parsed so far + Uint32 n; // pk attr count + Uint32 i; + for (i = n = 0; i < t->m_columns.size() && n < t->m_noOfKeys; i++) + { + const NdbColumnImpl* c = t->getColumn(i); + assert(c != NULL); + if (! c->m_pk) + continue; + + assert(sz < pk_sz); + Uint32 bytesize = c->m_attrSize * c->m_arraySize; + Uint32 lb, len; + bool ok = NdbSqlUtil::get_var_length(c->m_type, &pk_buffer[sz], bytesize, + lb, len); + assert(ok); + + AttributeHeader ah(i, lb + len); + ah_buffer[n++] = ah.m_value; + sz += ah.getDataSize(); + } + assert(n == t->m_noOfKeys && sz == pk_sz); +} + +int +NdbEventBuffer::get_main_data(Gci_container* bucket, + EventBufData_hash::Pos& hpos, + EventBufData* blob_data) +{ + DBUG_ENTER_EVENT("NdbEventBuffer::get_main_data"); + + NdbEventOperationImpl* main_op = blob_data->m_event_op->theMainOp; + assert(main_op != NULL); + const NdbTableImpl* mainTable = main_op->m_eventImpl->m_tableImpl; + + // create LinearSectionPtr for main table key + LinearSectionPtr ptr[3]; + Uint32 ah_buffer[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY]; + ptr[0].sz = mainTable->m_noOfKeys; + ptr[0].p = ah_buffer; + ptr[1].sz = AttributeHeader(blob_data->ptr[0].p[0]).getDataSize(); + ptr[1].p = blob_data->ptr[1].p; + ptr[2].sz = 0; + ptr[2].p = 0; + split_concatenated_pk(mainTable, ptr[0].p, ptr[1].p, ptr[1].sz); + + DBUG_DUMP_EVENT("ah", (char*)ptr[0].p, ptr[0].sz << 2); + DBUG_DUMP_EVENT("pk", (char*)ptr[1].p, ptr[1].sz << 2); + + // search for main event buffer + bucket->m_data_hash.search(hpos, main_op, ptr); + if (hpos.data != NULL) + DBUG_RETURN_EVENT(0); + + // not found, create a place-holder + EventBufData* main_data = alloc_data(); + if (main_data == NULL) + DBUG_RETURN_EVENT(-1); + SubTableData sdata = *blob_data->sdata; + sdata.tableId = main_op->m_eventImpl->m_tableImpl->m_id; + sdata.operation = NdbDictionary::Event::_TE_NUL; + if (copy_data(&sdata, ptr, main_data) != 0) + DBUG_RETURN_EVENT(-1); + hpos.data = main_data; + + DBUG_RETURN_EVENT(1); +} + +void +NdbEventBuffer::add_blob_data(EventBufData* main_data, + EventBufData* blob_data) +{ + DBUG_ENTER_EVENT("NdbEventBuffer::add_blob_data"); + DBUG_PRINT_EVENT("info", ("main_data=%p blob_data=%p", main_data, blob_data)); + EventBufData* head; + head = main_data->m_next_blob; + while (head != NULL) + { + if (head->m_event_op == blob_data->m_event_op) + break; + head = head->m_next_blob; + } + if (head == NULL) + { + head = blob_data; + head->m_next_blob = main_data->m_next_blob; + main_data->m_next_blob = head; + } + else + { + blob_data->m_next = head->m_next; + head->m_next = blob_data; + } + DBUG_VOID_RETURN_EVENT; +} NdbEventOperationImpl * NdbEventBuffer::move_data() @@ -1613,6 +1958,31 @@ NdbEventBuffer::free_list(EventBufData_list &list) #endif m_free_data_sz+= list.m_sz; + // free blobs XXX unacceptable performance, fix later + { + EventBufData* data = list.m_head; + while (1) { + while (data->m_next_blob != NULL) { + EventBufData* blob_head = data->m_next_blob; + data->m_next_blob = blob_head->m_next_blob; + blob_head->m_next_blob = NULL; + while (blob_head != NULL) { + EventBufData* blob_part = blob_head; + blob_head = blob_head->m_next; + blob_part->m_next = m_free_data; + m_free_data = blob_part; +#ifdef VM_TRACE + m_free_data_count++; +#endif + m_free_data_sz += blob_part->sz; + } + } + if (data == list.m_tail) + break; + data = data->m_next; + } + } + // list returned to m_free_data new (&list) EventBufData_list; } @@ -1648,6 +2018,17 @@ NdbEventBuffer::dropEventOperation(NdbEventOperation* tOp) if (m_dropped_ev_op) m_dropped_ev_op->m_prev= op; m_dropped_ev_op= op; + + // stop blob event ops + if (op->theMainOp == NULL) + { + NdbEventOperationImpl* tBlobOp = op->theBlobOpList; + while (tBlobOp != NULL) + { + tBlobOp->stop(); + tBlobOp = tBlobOp->m_next; + } + } // ToDo, take care of these to be deleted at the // appropriate time, after we are sure that there @@ -1717,6 +2098,10 @@ send_report: Uint32 EventBufData_hash::getpkhash(NdbEventOperationImpl* op, LinearSectionPtr ptr[3]) { + DBUG_ENTER_EVENT("EventBufData_hash::getpkhash"); + DBUG_DUMP_EVENT("ah", (char*)ptr[0].p, ptr[0].sz << 2); + DBUG_DUMP_EVENT("pk", (char*)ptr[1].p, ptr[1].sz << 2); + const NdbTableImpl* tab = op->m_eventImpl->m_tableImpl; // in all cases ptr[0] = pk ah.. ptr[1] = pk ad.. @@ -1747,13 +2132,19 @@ EventBufData_hash::getpkhash(NdbEventOperationImpl* op, LinearSectionPtr ptr[3]) (*cs->coll->hash_sort)(cs, dptr + lb, len, &nr1, &nr2); dptr += ((bytesize + 3) / 4) * 4; } - return nr1; + DBUG_PRINT_EVENT("info", ("hash result=%08x", nr1)); + DBUG_RETURN_EVENT(nr1); } -// this is seldom invoked bool EventBufData_hash::getpkequal(NdbEventOperationImpl* op, LinearSectionPtr ptr1[3], LinearSectionPtr ptr2[3]) { + DBUG_ENTER_EVENT("EventBufData_hash::getpkequal"); + DBUG_DUMP_EVENT("ah1", (char*)ptr1[0].p, ptr1[0].sz << 2); + DBUG_DUMP_EVENT("pk1", (char*)ptr1[1].p, ptr1[1].sz << 2); + DBUG_DUMP_EVENT("ah2", (char*)ptr2[0].p, ptr2[0].sz << 2); + DBUG_DUMP_EVENT("pk2", (char*)ptr2[1].p, ptr2[1].sz << 2); + const NdbTableImpl* tab = op->m_eventImpl->m_tableImpl; Uint32 nkey = tab->m_noOfKeys; @@ -1763,6 +2154,8 @@ EventBufData_hash::getpkequal(NdbEventOperationImpl* op, LinearSectionPtr ptr1[3 const uchar* dptr1 = (uchar*)ptr1[1].p; const uchar* dptr2 = (uchar*)ptr2[1].p; + bool equal = true; + while (nkey-- != 0) { AttributeHeader ah1(*hptr1++); @@ -1787,16 +2180,22 @@ EventBufData_hash::getpkequal(NdbEventOperationImpl* op, LinearSectionPtr ptr1[3 CHARSET_INFO* cs = col->m_cs ? col->m_cs : &my_charset_bin; int res = (cs->coll->strnncollsp)(cs, dptr1 + lb1, len1, dptr2 + lb2, len2, false); if (res != 0) - return false; + { + equal = false; + break; + } dptr1 += ((bytesize1 + 3) / 4) * 4; dptr2 += ((bytesize2 + 3) / 4) * 4; } - return true; + + DBUG_PRINT_EVENT("info", ("equal=%s", equal ? "true" : "false")); + DBUG_RETURN_EVENT(equal); } void EventBufData_hash::search(Pos& hpos, NdbEventOperationImpl* op, LinearSectionPtr ptr[3]) { + DBUG_ENTER_EVENT("EventBufData_hash::search"); Uint32 pkhash = getpkhash(op, ptr); Uint32 index = (op->m_oid ^ pkhash) % GCI_EVENT_HASH_SIZE; EventBufData* data = m_hash[index]; @@ -1811,6 +2210,8 @@ EventBufData_hash::search(Pos& hpos, NdbEventOperationImpl* op, LinearSectionPtr hpos.index = index; hpos.data = data; hpos.pkhash = pkhash; + DBUG_PRINT_EVENT("info", ("search result=%p", data)); + DBUG_VOID_RETURN_EVENT; } template class Vector<Gci_container>; diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp index 2a1d1643a12..5e52befbf3f 100644 --- a/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp @@ -21,6 +21,7 @@ #include <signaldata/SumaImpl.hpp> #include <transporter/TransporterDefinitions.hpp> #include <NdbRecAttr.hpp> +#include <AttributeHeader.hpp> #define NDB_EVENT_OP_MAGIC_NUMBER 0xA9F301B4 @@ -35,9 +36,28 @@ struct EventBufData LinearSectionPtr ptr[3]; unsigned sz; NdbEventOperationImpl *m_event_op; - EventBufData *m_next; // Next wrt to global order + + /* + * Blobs are stored in blob list (m_next_blob) where each entry + * is list of parts (m_next) in part number order. + * + * TODO order by part no and link for fast read and free_list + */ + + EventBufData *m_next; // Next wrt to global order or Next blob part + EventBufData *m_next_blob; // First part in next blob + EventBufData *m_next_hash; // Next in per-GCI hash Uint32 m_pkhash; // PK hash (without op) for fast compare + + // Get blob part number from blob data + Uint32 get_blob_part_no() { + assert(ptr[0].sz > 2); + Uint32 pos = AttributeHeader(ptr[0].p[0]).getDataSize() + + AttributeHeader(ptr[0].p[1]).getDataSize(); + Uint32 no = ptr[1].p[pos]; + return no; + } }; class EventBufData_list @@ -70,7 +90,6 @@ EventBufData_list::~EventBufData_list() { } - inline int EventBufData_list::is_empty() { @@ -173,9 +192,13 @@ public: NdbEventOperation::State getState(); int execute(); + int execute_nolock(); int stop(); NdbRecAttr *getValue(const char *colName, char *aValue, int n); NdbRecAttr *getValue(const NdbColumnImpl *, char *aValue, int n); + NdbBlob *getBlobHandle(const char *colName, int n); + NdbBlob *getBlobHandle(const NdbColumnImpl *, int n); + int readBlobParts(char* buf, NdbBlob* blob, Uint32 part, Uint32 count); int receive_event(); Uint64 getGCI(); Uint64 getLatestGCI(); @@ -199,6 +222,10 @@ public: NdbRecAttr *theFirstDataAttrs[2]; NdbRecAttr *theCurrentDataAttrs[2]; + NdbBlob* theBlobList; + NdbEventOperationImpl* theBlobOpList; // in main op, list of blob ops + NdbEventOperationImpl* theMainOp; // in blob op, the main op + NdbEventOperation::State m_state; /* note connection to mi_type */ Uint32 mi_type; /* should be == 0 if m_state != EO_EXECUTING * else same as in EventImpl @@ -275,6 +302,11 @@ public: int merge_data(const SubTableData * const sdata, LinearSectionPtr ptr[3], EventBufData* data); + int get_main_data(Gci_container* bucket, + EventBufData_hash::Pos& hpos, + EventBufData* blob_data); + void add_blob_data(EventBufData* main_data, + EventBufData* blob_data); void free_list(EventBufData_list &list); diff --git a/storage/ndb/test/ndbapi/test_event_merge.cpp b/storage/ndb/test/ndbapi/test_event_merge.cpp index 882679ffcb7..d3c1ee70ce7 100644 --- a/storage/ndb/test/ndbapi/test_event_merge.cpp +++ b/storage/ndb/test/ndbapi/test_event_merge.cpp @@ -21,14 +21,7 @@ #include <my_sys.h> #include <ndb_version.h> -#if NDB_VERSION_D < MAKE_VERSION(5, 1, 0) -#define version50 -#else -#undef version50 -#endif - -// until rbr in 5.1 -#undef version51rbr +// version >= 5.1 required #if !defined(min) || !defined(max) #define min(x, y) ((x) < (y) ? (x) : (y)) @@ -57,11 +50,11 @@ * There are other -no-* options, each added to isolate a specific bug. * * There are 5 ways (ignoring NUL operand) to compose 2 ops: - * 5.0 bugs 5.1 bugs + * * INS o DEL = NUL - * INS o UPD = INS type=INS - * DEL o INS = UPD type=INS type=INS - * UPD o DEL = DEL no event + * INS o UPD = INS + * DEL o INS = UPD + * UPD o DEL = DEL * UPD o UPD = UPD */ @@ -73,17 +66,19 @@ struct Opts { uint maxpk; my_bool no_blobs; my_bool no_implicit_nulls; + my_bool no_missing_update; my_bool no_multiops; my_bool no_nulls; my_bool one_blob; const char* opstring; uint seed; my_bool separate_events; + uint tweak; // whatever's useful my_bool use_table; }; static Opts g_opts; -static const uint g_maxpk = 100; +static const uint g_maxpk = 1000; static const uint g_maxopstringpart = 100; static const char* g_opstringpart[g_maxopstringpart]; static uint g_opstringparts = 0; @@ -208,7 +203,9 @@ struct Col { uint length; uint size; bool isblob() const { - return type == NdbDictionary::Column::Text; + return + type == NdbDictionary::Column::Text || + type == NdbDictionary::Column::Blob; } }; @@ -218,19 +215,21 @@ static Col g_col[] = { { 2, "seq", NdbDictionary::Column::Unsigned, false, false, 1, 4 }, { 3, "cc1", NdbDictionary::Column::Char, false, true, g_charlen, g_charlen }, { 4, "tx1", NdbDictionary::Column::Text, false, true, 0, 0 }, - { 5, "tx2", NdbDictionary::Column::Text, false, true, 0, 0 } + { 5, "tx2", NdbDictionary::Column::Text, false, true, 0, 0 }, + { 6, "bl1", NdbDictionary::Column::Blob, false, true, 0, 0 } // tinyblob }; static const uint g_maxcol = sizeof(g_col)/sizeof(g_col[0]); +static const uint g_blobcols = 3; static uint ncol() { uint n = g_maxcol; if (g_opts.no_blobs) - n -= 2; + n -= g_blobcols; else if (g_opts.one_blob) - n -= 1; + n -= (g_blobcols - 1); return n; } @@ -283,6 +282,11 @@ createtable() col.setStripeSize(g_blobstripesize); col.setCharset(cs); break; + case NdbDictionary::Column::Blob: + col.setInlineSize(g_blobinlinesize); + col.setPartSize(0); + col.setStripeSize(0); + break; default: assert(false); break; @@ -337,6 +341,7 @@ struct Data { char cc1[g_charlen + 1]; Txt tx1; Txt tx2; + Txt bl1; Ptr ptr[g_maxcol]; int ind[g_maxcol]; // -1 = no data, 1 = NULL, 0 = not NULL uint noop; // bit: omit in NdbOperation (implicit NULL INS or no UPD) @@ -347,14 +352,15 @@ struct Data { memset(pk2, 0, sizeof(pk2)); seq = 0; memset(cc1, 0, sizeof(cc1)); - tx1.val = tx2.val = 0; - tx1.len = tx2.len = 0; + tx1.val = tx2.val = bl1.val = 0; + tx1.len = tx2.len = bl1.len = 0; ptr[0].u32 = &pk1; ptr[1].ch = pk2; ptr[2].u32 = &seq; ptr[3].ch = cc1; ptr[4].txt = &tx1; ptr[5].txt = &tx2; + ptr[6].txt = &bl1; for (i = 0; i < g_maxcol; i++) ind[i] = -1; noop = 0; @@ -363,6 +369,7 @@ struct Data { void free() { delete [] tx1.val; delete [] tx2.val; + delete [] bl1.val; init(); } }; @@ -384,6 +391,7 @@ cmpcol(const Col& c, const Data& d1, const Data& d2) return 1; break; case NdbDictionary::Column::Text: + case NdbDictionary::Column::Blob: { const Data::Txt& t1 = *d1.ptr[i].txt; const Data::Txt& t2 = *d2.ptr[i].txt; @@ -434,6 +442,7 @@ operator<<(NdbOut& out, const Data& d) } break; case NdbDictionary::Column::Text: + case NdbDictionary::Column::Blob: { Data::Txt& t = *d.ptr[i].txt; bool first = true; @@ -712,6 +721,20 @@ checkop(const Op* op, Uint32& pk1) if (! c.nullable) { chkrc(ind0 <= 0 && ind1 <= 0); } + if (c.isblob()) { + // blob values must be from allowed chars + int j; + for (j = 0; j < 2; j++) { + const Data& d = op->data[j]; + if (d.ind[i] == 0) { + const Data::Txt& t = *d.ptr[i].txt; + int k; + for (k = 0; k < t.len; k++) { + chkrc(strchr(g_charval, t.val[k]) != 0); + } + } + } + } } return 0; } @@ -849,9 +872,8 @@ createevent() const Col& c = g_col[i]; evt.addEventColumn(c.name); } -#ifdef version51rbr + evt.setReport(NdbDictionary::Event::ER_UPDATED); evt.mergeEvents(! g_opts.separate_events); -#endif if (g_dic->getEvent(evt.getName()) != 0) chkdb(g_dic->dropEvent(evt.getName()) == 0); chkdb(g_dic->createEvent(evt) == 0); @@ -875,14 +897,8 @@ static int createeventop() { ll1("createeventop"); -#ifdef version50 - uint bsz = 10 * g_opts.maxops; - chkdb((g_evt_op = g_ndb->createEventOperation(g_evt->getName(), bsz)) != 0); -#else chkdb((g_evt_op = g_ndb->createEventOperation(g_evt->getName())) != 0); - // available in gci merge changeset g_evt_op->mergeEvents(! g_opts.separate_events); // not yet inherited -#endif uint i; for (i = 0; i < ncol(); i++) { const Col& c = g_col[i]; @@ -891,10 +907,8 @@ createeventop() chkdb((g_ev_ra[0][i] = g_evt_op->getValue(c.name, (char*)d[0].ptr[i].v)) != 0); chkdb((g_ev_ra[1][i] = g_evt_op->getPreValue(c.name, (char*)d[1].ptr[i].v)) != 0); } else { -#ifdef version51rbr chkdb((g_ev_bh[0][i] = g_evt_op->getBlobHandle(c.name)) != 0); chkdb((g_ev_bh[1][i] = g_evt_op->getPreBlobHandle(c.name)) != 0); -#endif } } return 0; @@ -909,10 +923,10 @@ dropeventop() return 0; } +// wait for event to be installed and for GCIs to pass static int -waitgci() // wait for event to be installed and for at least 1 GCI to pass +waitgci(uint ngci) { - const uint ngci = 3; ll1("waitgci " << ngci); Uint32 gci[2]; uint i = 0; @@ -976,7 +990,6 @@ scantab() if (! c.isblob()) { ind = ra[i]->isNULL(); } else { -#ifdef version51rbr int ret; ret = bh[i]->getDefined(ind); assert(ret == 0); @@ -992,8 +1005,10 @@ scantab() Uint32 len = t.len; ret = bh[i]->readData(t.val, len); assert(ret == 0 && len == t.len); + // to see the data, have to execute... + chkdb(g_con->execute(NoCommit) == 0); + assert(memchr(t.val, 'X', t.len) == 0); } -#endif } assert(ind >= 0); d0.ind[i] = ind; @@ -1042,7 +1057,7 @@ makedata(const Col& c, Data& d, Uint32 pk1, Op::Type t) } else if (t == Op::INS && ! g_opts.no_implicit_nulls && c.nullable && urandom(10, 100)) { d.noop |= (1 << i); d.ind[i] = 1; // implicit NULL value is known - } else if (t == Op::UPD && urandom(10, 100)) { + } else if (t == Op::UPD && ! g_opts.no_missing_update && urandom(10, 100)) { d.noop |= (1 << i); d.ind[i] = -1; // fixed up in caller } else if (! g_opts.no_nulls && c.nullable && urandom(10, 100)) { @@ -1060,6 +1075,8 @@ makedata(const Col& c, Data& d, Uint32 pk1, Op::Type t) { char* p = d.ptr[i].ch; uint u = urandom(g_charlen); + if (u == 0) + u = urandom(g_charlen); // 2x bias for non-empty uint j; for (j = 0; j < g_charlen; j++) { uint v = urandom(strlen(g_charval)); @@ -1068,12 +1085,23 @@ makedata(const Col& c, Data& d, Uint32 pk1, Op::Type t) } break; case NdbDictionary::Column::Text: + case NdbDictionary::Column::Blob: { + const bool tinyblob = (c.type == NdbDictionary::Column::Blob); Data::Txt& t = *d.ptr[i].txt; - uint u = urandom(g_maxblobsize); + delete [] t.val; + t.val = 0; + if (g_opts.tweak & 1) { + uint u = g_blobinlinesize + (tinyblob ? 0 : g_blobpartsize); + uint v = (g_opts.tweak & 2) ? 0 : urandom(strlen(g_charval)); + t.val = new char [u]; + t.len = u; + memset(t.val, g_charval[v], u); + break; + } + uint u = urandom(tinyblob ? g_blobinlinesize : g_maxblobsize); u = urandom(u); // 4x bias for smaller blobs u = urandom(u); - delete [] t.val; t.val = new char [u]; t.len = u; uint j = 0; @@ -1134,9 +1162,15 @@ makeops() { ll1("makeops"); Uint32 pk1 = 0; - while (g_usedops < g_opts.maxops && pk1 < g_opts.maxpk) { - if (g_opts.opstring == 0) + while (1) { + if (g_opts.opstring == 0) { + if (g_usedops >= g_opts.maxops) // use up ops + break; pk1 = urandom(g_opts.maxpk); + } else { + if (pk1 >= g_opts.maxpk) // use up pks + break; + } ll2("makeops: pk1=" << pk1); // total op on the pk so far // optype either NUL=initial/deleted or INS=created @@ -1465,7 +1499,7 @@ matchevent(Op* ev) } if (tmpok) { ok = gci_op->match = true; - ll2("===: match"); + ll2("match"); } } pos++; @@ -1555,7 +1589,6 @@ geteventdata() NdbRecAttr* ra = g_ev_ra[j][i]; ind = ra->isNULL(); } else { -#ifdef version51rbr NdbBlob* bh = g_ev_bh[j][i]; ret = bh->getDefined(ind); assert(ret == 0); @@ -1572,7 +1605,6 @@ geteventdata() ret = bh->readData(t.val, len); assert(ret == 0 && len == t.len); } -#endif } d[j].ind[i] = ind; } @@ -1585,38 +1617,22 @@ runevents() ll1("runevents"); uint mspoll = 1000; uint npoll = 6; // strangely long delay + ll1("poll " << npoll); while (npoll != 0) { npoll--; int ret; - ll1("poll"); ret = g_ndb->pollEvents(mspoll); if (ret <= 0) continue; while (1) { g_rec_ev->init(Op::EV); -#ifdef version50 - int overrun = g_opts.maxops; - chkdb((ret = g_evt_op->next(&overrun)) >= 0); - chkrc(overrun == 0); - if (ret == 0) - break; -#else NdbEventOperation* tmp_op = g_ndb->nextEvent(); if (tmp_op == 0) break; reqrc(g_evt_op == tmp_op); -#endif chkrc(seteventtype(g_rec_ev, g_evt_op->getEventType()) == 0); geteventdata(); g_rec_ev->gci = g_evt_op->getGCI(); -#ifdef version50 - // fix to match 5.1 - if (g_rec_ev->type == Op::UPD) { - Uint32 pk1 = g_rec_ev->data[0].pk1; - makedata(getcol("pk1"), g_rec_ev->data[1], pk1, Op::UPD); - makedata(getcol("pk2"), g_rec_ev->data[1], pk1, Op::UPD); - } -#endif // get indicators and blob value ll2("runevents: EVT: " << *g_rec_ev); // check basic sanity @@ -1667,7 +1683,7 @@ runtest() chkrc(createtable() == 0); chkrc(createevent() == 0); for (g_loop = 0; g_opts.loop == 0 || g_loop < g_opts.loop; g_loop++) { - ll0("loop " << g_loop); + ll0("=== loop " << g_loop << " ==="); setseed(g_loop); resetmem(); chkrc(scantab() == 0); // alternative: save tot_op for loop > 0 @@ -1675,7 +1691,7 @@ runtest() g_rec_ev = getop(Op::EV); chkrc(createeventop() == 0); chkdb(g_evt_op->execute() == 0); - chkrc(waitgci() == 0); + chkrc(waitgci(3) == 0); chkrc(runops() == 0); if (! g_opts.separate_events) chkrc(mergeops() == 0); @@ -1685,6 +1701,8 @@ runtest() chkrc(matchevents() == 0); chkrc(matchops() == 0); chkrc(dropeventop() == 0); + // time erases everything.. + chkrc(waitgci(1) == 0); } chkrc(dropevent() == 0); chkrc(droptable() == 0); @@ -1703,41 +1721,48 @@ my_long_options[] = { "loglevel", 1002, "Logging level in this program (default 0)", (gptr*)&g_opts.loglevel, (gptr*)&g_opts.loglevel, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "loop", 1003, "Number of test loops (default 2, 0=forever)", + { "loop", 1003, "Number of test loops (default 3, 0=forever)", (gptr*)&g_opts.loop, (gptr*)&g_opts.loop, 0, - GET_INT, REQUIRED_ARG, 2, 0, 0, 0, 0, 0 }, + GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0 }, { "maxops", 1004, "Approx number of PK operations (default 1000)", (gptr*)&g_opts.maxops, (gptr*)&g_opts.maxops, 0, GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 }, { "maxpk", 1005, "Number of different PK values (default 10)", (gptr*)&g_opts.maxpk, (gptr*)&g_opts.maxpk, 0, - GET_UINT, REQUIRED_ARG, 10, 1, g_maxpk, 0, 0, 0 }, + GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 }, { "no-blobs", 1006, "Omit blob attributes (5.0: true)", (gptr*)&g_opts.no_blobs, (gptr*)&g_opts.no_blobs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "no-implicit-nulls", 1007, "Insert must include NULL values explicitly", + { "no-implicit-nulls", 1007, "Insert must include all attrs" + " i.e. no implicit NULLs", (gptr*)&g_opts.no_implicit_nulls, (gptr*)&g_opts.no_implicit_nulls, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "no-multiops", 1008, "Allow only 1 operation per commit", + { "no-missing-update", 1008, "Update must include all non-PK attrs", + (gptr*)&g_opts.no_missing_update, (gptr*)&g_opts.no_missing_update, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "no-multiops", 1009, "Allow only 1 operation per commit", (gptr*)&g_opts.no_multiops, (gptr*)&g_opts.no_multiops, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "no-nulls", 1009, "Create no NULL values", + { "no-nulls", 1010, "Create no NULL values", (gptr*)&g_opts.no_nulls, (gptr*)&g_opts.no_nulls, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "one-blob", 1010, "Only one blob attribute (defautt 2)", + { "one-blob", 1011, "Only one blob attribute (default 2)", (gptr*)&g_opts.one_blob, (gptr*)&g_opts.one_blob, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "opstring", 1011, "Operations to run e.g. idiucdc (c is commit) or" + { "opstring", 1012, "Operations to run e.g. idiucdc (c is commit) or" " iuuc:uudc (the : separates loops)", (gptr*)&g_opts.opstring, (gptr*)&g_opts.opstring, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "seed", 1012, "Random seed (0=loop number, default -1=random)", + { "seed", 1013, "Random seed (0=loop number, default -1=random)", (gptr*)&g_opts.seed, (gptr*)&g_opts.seed, 0, GET_INT, REQUIRED_ARG, -1, 0, 0, 0, 0, 0 }, - { "separate-events", 1013, "Do not combine events per GCI (5.0: true)", + { "separate-events", 1014, "Do not combine events per GCI (5.0: true)", (gptr*)&g_opts.separate_events, (gptr*)&g_opts.separate_events, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, - { "use-table", 1014, "Use existing table 'tem1'", + { "tweak", 1015, "Whatever the source says", + (gptr*)&g_opts.tweak, (gptr*)&g_opts.tweak, 0, + GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "use-table", 1016, "Use existing table 'tem1'", (gptr*)&g_opts.use_table, (gptr*)&g_opts.use_table, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, @@ -1754,9 +1779,10 @@ usage() static int checkopts() { -#ifdef version50 - g_opts.separate_events = true; -#endif + if (g_opts.maxpk > g_maxpk) { + ll0("setting maxpk to " << g_maxpk); + g_opts.maxpk = g_maxpk; + } if (g_opts.separate_events) { g_opts.no_blobs = true; } |