summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/ndb_blob.result0
-rw-r--r--mysql-test/t/ndb_blob.test0
-rw-r--r--ndb/include/kernel/signaldata/DictTabInfo.hpp4
-rw-r--r--ndb/include/ndbapi/NdbBlob.hpp82
-rw-r--r--ndb/include/ndbapi/NdbConnection.hpp27
-rw-r--r--ndb/include/ndbapi/NdbDictionary.hpp7
-rw-r--r--ndb/include/util/NdbSqlUtil.hpp12
-rw-r--r--ndb/src/common/util/NdbSqlUtil.cpp8
-rw-r--r--ndb/src/ndbapi/NdbBlob.cpp252
-rw-r--r--ndb/src/ndbapi/NdbConnection.cpp67
-rw-r--r--ndb/src/ndbapi/NdbDictionary.cpp91
-rw-r--r--ndb/src/ndbapi/NdbDictionaryImpl.cpp8
-rw-r--r--ndb/src/ndbapi/NdbDictionaryImpl.hpp2
-rw-r--r--ndb/src/ndbapi/NdbRecAttr.cpp65
-rw-r--r--ndb/src/ndbapi/NdbResultSet.cpp7
-rw-r--r--ndb/test/include/NDBT_Table.hpp1
-rw-r--r--ndb/test/ndbapi/testBlobs.cpp603
-rw-r--r--ndb/test/src/NDBT_Table.cpp29
-rw-r--r--ndb/test/src/NDBT_Test.cpp3
19 files changed, 850 insertions, 418 deletions
diff --git a/mysql-test/r/ndb_blob.result b/mysql-test/r/ndb_blob.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/mysql-test/r/ndb_blob.result
diff --git a/mysql-test/t/ndb_blob.test b/mysql-test/t/ndb_blob.test
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/mysql-test/t/ndb_blob.test
diff --git a/ndb/include/kernel/signaldata/DictTabInfo.hpp b/ndb/include/kernel/signaldata/DictTabInfo.hpp
index d5f27257eb8..dec7145c897 100644
--- a/ndb/include/kernel/signaldata/DictTabInfo.hpp
+++ b/ndb/include/kernel/signaldata/DictTabInfo.hpp
@@ -311,7 +311,7 @@ public:
ExtDatetime = NdbSqlUtil::Type::Datetime,
ExtTimespec = NdbSqlUtil::Type::Timespec,
ExtBlob = NdbSqlUtil::Type::Blob,
- ExtClob = NdbSqlUtil::Type::Clob
+ ExtText = NdbSqlUtil::Type::Text
};
// Attribute data interpretation
@@ -435,7 +435,7 @@ public:
AttributeArraySize = 12 * AttributeExtLength;
return true;
case DictTabInfo::ExtBlob:
- case DictTabInfo::ExtClob:
+ case DictTabInfo::ExtText:
AttributeType = DictTabInfo::StringType;
AttributeSize = DictTabInfo::an8Bit;
// head + inline part [ attr precision ]
diff --git a/ndb/include/ndbapi/NdbBlob.hpp b/ndb/include/ndbapi/NdbBlob.hpp
index 16df56e230b..af4c049d4a7 100644
--- a/ndb/include/ndbapi/NdbBlob.hpp
+++ b/ndb/include/ndbapi/NdbBlob.hpp
@@ -50,24 +50,33 @@ class NdbColumnImpl;
* - closed: after transaction commit
* - invalid: after rollback or transaction close
*
- * NdbBlob supports 2 styles of data access:
+ * NdbBlob supports 3 styles of data access:
*
* - in prepare phase, NdbBlob methods getValue and setValue are used to
- * prepare a read or write of a single blob value of known size
+ * prepare a read or write of a blob value of known size
*
- * - in active phase, NdbBlob methods readData and writeData are used to
- * read or write blob data of undetermined size
+ * - in prepare phase, setActiveHook is used to define a routine which
+ * is invoked as soon as the handle becomes active
+ *
+ * - in active phase, readData and writeData are used to read or write
+ * blob data of arbitrary size
+ *
+ * The styles can be applied in combination (in above order).
+ *
+ * Blob operations take effect at next transaction execute. In some
+ * cases NdbBlob is forced to do implicit executes. To avoid this,
+ * operate on complete blob parts.
+ *
+ * Use NdbConnection::executePendingBlobOps to flush your reads and
+ * writes. It avoids execute penalty if nothing is pending. It is not
+ * needed after execute (obviously) or after next scan result.
*
* NdbBlob methods return -1 on error and 0 on success, and use output
* parameters when necessary.
*
* Notes:
* - table and its blob part tables are not created atomically
- * - blob data operations take effect at next transaction execute
- * - NdbBlob may need to do implicit executes on the transaction
- * - read and write of complete parts is much more efficient
* - scan must use the "new" interface NdbScanOperation
- * - scan with blobs applies hold-read-lock (at minimum)
* - to update a blob in a read op requires exclusive tuple lock
* - update op in scan must do its own getBlobHandle
* - delete creates implicit, not-accessible blob handles
@@ -78,12 +87,16 @@ class NdbColumnImpl;
* - scan must use exclusive locking for now
*
* Todo:
- * - add scan method hold-read-lock-until-next + return-keyinfo
- * - better check of keyinfo length when setting keys
- * - better check of allowed blob op vs locking mode
+ * - add scan method hold-read-lock + return-keyinfo
+ * - check keyinfo length when setting keys
+ * - check allowed blob ops vs locking mode
+ * - overload control (too many pending ops)
*/
class NdbBlob {
public:
+ /**
+ * State.
+ */
enum State {
Idle = 0,
Prepared = 1,
@@ -93,8 +106,14 @@ public:
};
State getState();
/**
+ * Inline blob header.
+ */
+ struct Head {
+ Uint64 length;
+ };
+ /**
* Prepare to read blob value. The value is available after execute.
- * Use isNull to check for NULL and getLength to get the real length
+ * Use getNull to check for NULL and getLength to get the real length
* and to check for truncation. Sets current read/write position to
* after the data read.
*/
@@ -107,6 +126,20 @@ public:
*/
int setValue(const void* data, Uint32 bytes);
/**
+ * Callback for setActiveHook. Invoked immediately when the prepared
+ * operation has been executed (but not committed). Any getValue or
+ * setValue is done first. The blob handle is active so readData or
+ * writeData etc can be used to manipulate blob value. A user-defined
+ * argument is passed along. Returns non-zero on error.
+ */
+ typedef int ActiveHook(NdbBlob* me, void* arg);
+ /**
+ * Define callback for blob handle activation. The queue of prepared
+ * operations will be executed in no commit mode up to this point and
+ * then the callback is invoked.
+ */
+ int setActiveHook(ActiveHook* activeHook, void* arg);
+ /**
* Check if blob is null.
*/
int getNull(bool& isNull);
@@ -115,7 +148,7 @@ public:
*/
int setNull();
/**
- * Get current length in bytes. Use isNull to distinguish between
+ * Get current length in bytes. Use getNull to distinguish between
* length 0 blob and NULL blob.
*/
int getLength(Uint64& length);
@@ -180,6 +213,13 @@ public:
static const int ErrAbort = 4268;
// "Unknown blob error"
static const int ErrUnknown = 4269;
+ /**
+ * Return info about all blobs in this operation.
+ */
+ // Get first blob in list
+ NdbBlob* blobsFirstBlob();
+ // Get next blob in list after this one
+ NdbBlob* blobsNextBlob();
private:
friend class Ndb;
@@ -214,10 +254,11 @@ private:
bool theSetFlag;
const char* theSetBuf;
Uint32 theGetSetBytes;
- // head
- struct Head {
- Uint64 length;
- };
+ // pending ops
+ Uint8 thePendingBlobOps;
+ // activation callback
+ ActiveHook* theActiveHook;
+ void* theActiveHookArg;
// buffers
struct Buf {
char* data;
@@ -235,7 +276,6 @@ private:
char* theInlineData;
NdbRecAttr* theHeadInlineRecAttr;
bool theHeadInlineUpdateFlag;
- bool theNewPartFlag;
// length and read/write position
int theNullFlag;
Uint64 theLength;
@@ -276,6 +316,11 @@ private:
int insertParts(const char* buf, Uint32 part, Uint32 count);
int updateParts(const char* buf, Uint32 part, Uint32 count);
int deleteParts(Uint32 part, Uint32 count);
+ // pending ops
+ int executePendingBlobReads();
+ int executePendingBlobWrites();
+ // callbacks
+ int invokeActiveHook();
// blob handle maintenance
int atPrepare(NdbConnection* aCon, NdbOperation* anOp, const NdbColumnImpl* aColumn);
int preExecute(ExecType anExecType, bool& batch);
@@ -287,6 +332,7 @@ private:
void setErrorCode(NdbOperation* anOp, bool invalidFlag = true);
void setErrorCode(NdbConnection* aCon, bool invalidFlag = true);
#ifdef VM_TRACE
+ int getOperationType() const;
friend class NdbOut& operator<<(NdbOut&, const NdbBlob&);
#endif
};
diff --git a/ndb/include/ndbapi/NdbConnection.hpp b/ndb/include/ndbapi/NdbConnection.hpp
index 5d73058cc24..4e0330e3fda 100644
--- a/ndb/include/ndbapi/NdbConnection.hpp
+++ b/ndb/include/ndbapi/NdbConnection.hpp
@@ -431,6 +431,15 @@ public:
/** @} *********************************************************************/
+ /**
+ * Execute the transaction in NoCommit mode if there are any not-yet
+ * executed blob part operations of given types. Otherwise do
+ * nothing. The flags argument is bitwise OR of (1 << optype) where
+ * optype comes from NdbOperation::OperationType. Only the basic PK
+ * ops are used (read, insert, update, delete).
+ */
+ int executePendingBlobOps(Uint8 flags = 0xFF);
+
private:
/**
* Release completed operations
@@ -642,6 +651,7 @@ private:
Uint32 theBuddyConPtr;
// optim: any blobs
bool theBlobFlag;
+ Uint8 thePendingBlobOps;
static void sendTC_COMMIT_ACK(NdbApiSignal *,
Uint32 transId1, Uint32 transId2,
@@ -869,6 +879,21 @@ NdbConnection::OpSent()
theNoOfOpSent++;
}
+/******************************************************************************
+void executePendingBlobOps();
+******************************************************************************/
+#include <stdlib.h>
+inline
+int
+NdbConnection::executePendingBlobOps(Uint8 flags)
+{
+ if (thePendingBlobOps & flags) {
+ // not executeNoBlobs because there can be new ops with blobs
+ return execute(NoCommit);
+ }
+ return 0;
+}
+
inline
Uint32
NdbConnection::ptr2int(){
@@ -876,5 +901,3 @@ NdbConnection::ptr2int(){
}
#endif
-
-
diff --git a/ndb/include/ndbapi/NdbDictionary.hpp b/ndb/include/ndbapi/NdbDictionary.hpp
index 3b38e33ec91..4a3adb61d9e 100644
--- a/ndb/include/ndbapi/NdbDictionary.hpp
+++ b/ndb/include/ndbapi/NdbDictionary.hpp
@@ -183,7 +183,7 @@ public:
Datetime, ///< Precision down to 1 sec (sizeof(Datetime) == 8 bytes )
Timespec, ///< Precision down to 1 nsec(sizeof(Datetime) == 12 bytes )
Blob, ///< Binary large object (see NdbBlob)
- Clob ///< Text blob
+ Text ///< Text blob
};
/**
@@ -309,7 +309,8 @@ public:
/**
* For blob, set or get "part size" i.e. number of bytes to store in
- * each tuple of the "blob table". Must be less than 64k.
+ * each tuple of the "blob table". Can be set to zero to omit parts
+ * and to allow only inline bytes ("tinyblob").
*/
void setPartSize(int size) { setScale(size); }
int getPartSize() const { return getScale(); }
@@ -1060,6 +1061,6 @@ public:
};
};
-class NdbOut& operator <<(class NdbOut& ndbout, const NdbDictionary::Column::Type type);
+class NdbOut& operator <<(class NdbOut& out, const NdbDictionary::Column& col);
#endif
diff --git a/ndb/include/util/NdbSqlUtil.hpp b/ndb/include/util/NdbSqlUtil.hpp
index 841da513d4a..78416fe9d01 100644
--- a/ndb/include/util/NdbSqlUtil.hpp
+++ b/ndb/include/util/NdbSqlUtil.hpp
@@ -80,7 +80,7 @@ public:
Datetime, // Precision down to 1 sec (size 8 bytes)
Timespec, // Precision down to 1 nsec (size 12 bytes)
Blob, // Blob
- Clob // Text blob
+ Text // Text blob
};
Enum m_typeId;
Cmp* m_cmp; // set to NULL if cmp not implemented
@@ -125,7 +125,7 @@ private:
static Cmp cmpDatetime;
static Cmp cmpTimespec;
static Cmp cmpBlob;
- static Cmp cmpClob;
+ static Cmp cmpText;
};
inline int
@@ -344,17 +344,15 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full,
break;
case Type::Blob: // XXX fix
break;
- case Type::Clob:
+ case Type::Text:
{
- // skip blob head, the rest is varchar
+ // skip blob head, the rest is char
const unsigned skip = NDB_BLOB_HEAD_SIZE;
if (size >= skip + 1) {
union { const Uint32* p; const char* v; } u1, u2;
u1.p = p1 + skip;
u2.p = p2 + skip;
- // length in first 2 bytes
- int k = strncmp(u1.v + 2, u2.v + 2, ((size - skip) << 2) - 2);
- return k < 0 ? -1 : k > 0 ? +1 : full == size ? 0 : CmpUnknown;
+ // TODO
}
return CmpUnknown;
}
diff --git a/ndb/src/common/util/NdbSqlUtil.cpp b/ndb/src/common/util/NdbSqlUtil.cpp
index e34d6d18539..f8d993f22f9 100644
--- a/ndb/src/common/util/NdbSqlUtil.cpp
+++ b/ndb/src/common/util/NdbSqlUtil.cpp
@@ -161,8 +161,8 @@ NdbSqlUtil::m_typeList[] = {
NULL // cmpDatetime
},
{
- Type::Clob,
- cmpClob
+ Type::Text,
+ cmpText
}
};
@@ -299,9 +299,9 @@ NdbSqlUtil::cmpBlob(const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size
}
int
-NdbSqlUtil::cmpClob(const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size)
+NdbSqlUtil::cmpText(const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size)
{
- return cmp(Type::Clob, p1, p2, full, size);
+ return cmp(Type::Text, p1, p2, full, size);
}
#ifdef NDB_SQL_UTIL_TEST
diff --git a/ndb/src/ndbapi/NdbBlob.cpp b/ndb/src/ndbapi/NdbBlob.cpp
index 638012b6a00..72990870bf8 100644
--- a/ndb/src/ndbapi/NdbBlob.cpp
+++ b/ndb/src/ndbapi/NdbBlob.cpp
@@ -28,10 +28,11 @@
do { \
static const char* p = getenv("NDB_BLOB_DEBUG"); \
if (p == 0 || *p == 0 || *p == '0') break; \
- const char* cname = theColumn == NULL ? "BLOB" : theColumn->m_name.c_str(); \
- ndbout << cname << " " << __LINE__ << " " << x << " " << *this << endl; \
+ static char* prefix = "BLOB"; \
+ const char* cname = theColumn == NULL ? "-" : theColumn->m_name.c_str(); \
+ ndbout << prefix << " " << hex << (void*)this << " " << cname; \
+ ndbout << " " << dec << __LINE__ << " " << x << " " << *this << endl; \
} while (0)
-#define EXE() assert(theNdbCon->executeNoBlobs(NoCommit) == 0)
#else
#define DBG(x)
#endif
@@ -49,7 +50,7 @@ ndb_blob_debug(const Uint32* data, unsigned size)
/*
* Reading index table directly (as a table) is faster but there are
- * bugs or limitations. Keep the code but make possible to choose.
+ * bugs or limitations. Keep the code and make possible to choose.
*/
static const bool g_ndb_blob_ok_to_read_index_table = false;
@@ -116,7 +117,7 @@ NdbBlob::getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnIm
case NdbDictionary::Column::Blob:
bc.setType(NdbDictionary::Column::Binary);
break;
- case NdbDictionary::Column::Clob:
+ case NdbDictionary::Column::Text:
bc.setType(NdbDictionary::Column::Char);
break;
default:
@@ -155,11 +156,13 @@ NdbBlob::init()
theSetFlag = false;
theSetBuf = NULL;
theGetSetBytes = 0;
+ thePendingBlobOps = 0;
+ theActiveHook = NULL;
+ theActiveHookArg = NULL;
theHead = NULL;
theInlineData = NULL;
theHeadInlineRecAttr = NULL;
theHeadInlineUpdateFlag = false;
- theNewPartFlag = false;
theNullFlag = -1;
theLength = 0;
thePos = 0;
@@ -270,7 +273,7 @@ NdbBlob::isScanOp()
inline Uint32
NdbBlob::getPartNumber(Uint64 pos)
{
- assert(pos >= theInlineSize);
+ assert(thePartSize != 0 && pos >= theInlineSize);
return (pos - theInlineSize) / thePartSize;
}
@@ -322,10 +325,10 @@ int
NdbBlob::setTableKeyValue(NdbOperation* anOp)
{
const Uint32* data = (const Uint32*)theKeyBuf.data;
+ DBG("setTableKeyValue key=" << ndb_blob_debug(data, theTable->m_sizeOfKeysInWords));
+ const unsigned columns = theTable->m_columns.size();
unsigned pos = 0;
- const unsigned size = theTable->m_columns.size();
- DBG("setTableKeyValue key=" << ndb_blob_debug(data, size));
- for (unsigned i = 0; i < size; i++) {
+ for (unsigned i = 0; i < columns; i++) {
NdbColumnImpl* c = theTable->m_columns[i];
assert(c != NULL);
if (c->m_pk) {
@@ -345,10 +348,10 @@ int
NdbBlob::setAccessKeyValue(NdbOperation* anOp)
{
const Uint32* data = (const Uint32*)theAccessKeyBuf.data;
+ DBG("setAccessKeyValue key=" << ndb_blob_debug(data, theAccessTable->m_sizeOfKeysInWords));
+ const unsigned columns = theAccessTable->m_columns.size();
unsigned pos = 0;
- const unsigned size = theAccessTable->m_columns.size();
- DBG("setAccessKeyValue key=" << ndb_blob_debug(data, size));
- for (unsigned i = 0; i < size; i++) {
+ for (unsigned i = 0; i < columns; i++) {
NdbColumnImpl* c = theAccessTable->m_columns[i];
assert(c != NULL);
if (c->m_pk) {
@@ -479,11 +482,27 @@ NdbBlob::setValue(const void* data, Uint32 bytes)
return 0;
}
+// activation hook
+
+int
+NdbBlob::setActiveHook(ActiveHook activeHook, void* arg)
+{
+ DBG("setActiveHook hook=" << hex << (void*)activeHook << " arg=" << hex << arg);
+ if (theState != Prepared) {
+ setErrorCode(ErrState);
+ return -1;
+ }
+ theActiveHook = activeHook;
+ theActiveHookArg = arg;
+ return 0;
+}
+
// misc operations
int
NdbBlob::getNull(bool& isNull)
{
+ DBG("getNull");
if (theState == Prepared && theSetFlag) {
isNull = (theSetBuf == NULL);
return 0;
@@ -520,6 +539,7 @@ NdbBlob::setNull()
int
NdbBlob::getLength(Uint64& len)
{
+ DBG("getLength");
if (theState == Prepared && theSetFlag) {
len = theGetSetBytes;
return 0;
@@ -535,17 +555,17 @@ NdbBlob::getLength(Uint64& len)
int
NdbBlob::truncate(Uint64 length)
{
- DBG("truncate kength=" << length);
+ DBG("truncate length=" << length);
if (theNullFlag == -1) {
setErrorCode(ErrState);
return -1;
}
if (theLength > length) {
- if (length >= theInlineSize) {
- Uint32 part1 = getPartNumber(length);
+ if (length > theInlineSize) {
+ Uint32 part1 = getPartNumber(length - 1);
Uint32 part2 = getPartNumber(theLength - 1);
assert(part2 >= part1);
- if (deleteParts(part1, part2 - part1) == -1)
+ if (part2 > part1 && deleteParts(part1 + 1, part2 - part1) == -1)
return -1;
} else {
if (deleteParts(0, getPartCount()) == -1)
@@ -560,6 +580,7 @@ NdbBlob::truncate(Uint64 length)
int
NdbBlob::getPos(Uint64& pos)
{
+ DBG("getPos");
if (theNullFlag == -1) {
setErrorCode(ErrState);
return -1;
@@ -571,6 +592,7 @@ NdbBlob::getPos(Uint64& pos)
int
NdbBlob::setPos(Uint64 pos)
{
+ DBG("setPos pos=" << pos);
if (theNullFlag == -1) {
setErrorCode(ErrState);
return -1;
@@ -629,6 +651,10 @@ NdbBlob::readDataPrivate(Uint64 pos, char* buf, Uint32& bytes)
len -= n;
}
}
+ if (len > 0 && thePartSize == 0) {
+ setErrorCode(ErrSeek);
+ return -1;
+ }
if (len > 0) {
assert(pos >= theInlineSize);
Uint32 off = (pos - theInlineSize) % thePartSize;
@@ -638,11 +664,10 @@ NdbBlob::readDataPrivate(Uint64 pos, char* buf, Uint32& bytes)
Uint32 part = (pos - theInlineSize) / thePartSize;
if (readParts(thePartBuf.data, part, 1) == -1)
return -1;
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode(theNdbOp);
+ // need result now
+ DBG("execute pending part reads");
+ if (executePendingBlobReads() == -1)
return -1;
- }
Uint32 n = thePartSize - off;
if (n > len)
n = len;
@@ -673,11 +698,10 @@ NdbBlob::readDataPrivate(Uint64 pos, char* buf, Uint32& bytes)
Uint32 part = (pos - theInlineSize) / thePartSize;
if (readParts(thePartBuf.data, part, 1) == -1)
return -1;
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode(theNdbOp);
+ // need result now
+ DBG("execute pending part reads");
+ if (executePendingBlobReads() == -1)
return -1;
- }
memcpy(buf, thePartBuf.data, len);
Uint32 n = len;
pos += n;
@@ -736,29 +760,27 @@ NdbBlob::writeDataPrivate(Uint64 pos, const char* buf, Uint32 bytes)
len -= n;
}
}
+ if (len > 0 && thePartSize == 0) {
+ setErrorCode(ErrSeek);
+ return -1;
+ }
if (len > 0) {
assert(pos >= theInlineSize);
Uint32 off = (pos - theInlineSize) % thePartSize;
// partial first block
if (off != 0) {
DBG("partial first block pos=" << pos << " len=" << len);
- if (theNewPartFlag) {
- // must flush insert to guarantee read
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode(theNdbOp);
- return -1;
- }
- theNewPartFlag = false;
- }
+ // flush writes to guarantee correct read
+ DBG("execute pending part writes");
+ if (executePendingBlobWrites() == -1)
+ return -1;
Uint32 part = (pos - theInlineSize) / thePartSize;
if (readParts(thePartBuf.data, part, 1) == -1)
return -1;
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode(theNdbOp);
+ // need result now
+ DBG("execute pending part reafs");
+ if (executePendingBlobReads() == -1)
return -1;
- }
Uint32 n = thePartSize - off;
if (n > len) {
memset(thePartBuf.data + off + len, theFillChar, n - len);
@@ -799,22 +821,16 @@ NdbBlob::writeDataPrivate(Uint64 pos, const char* buf, Uint32 bytes)
assert((pos - theInlineSize) % thePartSize == 0 && len < thePartSize);
Uint32 part = (pos - theInlineSize) / thePartSize;
if (theLength > pos + len) {
- if (theNewPartFlag) {
- // must flush insert to guarantee read
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode(theNdbOp);
- return -1;
- }
- theNewPartFlag = false;
- }
+ // flush writes to guarantee correct read
+ DBG("execute pending part writes");
+ if (executePendingBlobWrites() == -1)
+ return -1;
if (readParts(thePartBuf.data, part, 1) == -1)
return -1;
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode(theNdbOp);
+ // need result now
+ DBG("execute pending part reads");
+ if (executePendingBlobReads() == -1)
return -1;
- }
memcpy(thePartBuf.data, buf, len);
if (updateParts(thePartBuf.data, part, 1) == -1)
return -1;
@@ -859,6 +875,8 @@ NdbBlob::readParts(char* buf, Uint32 part, Uint32 count)
}
buf += thePartSize;
n++;
+ thePendingBlobOps |= (1 << NdbOperation::ReadRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::ReadRequest);
}
return 0;
}
@@ -879,7 +897,8 @@ NdbBlob::insertParts(const char* buf, Uint32 part, Uint32 count)
}
buf += thePartSize;
n++;
- theNewPartFlag = true;
+ thePendingBlobOps |= (1 << NdbOperation::InsertRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::InsertRequest);
}
return 0;
}
@@ -900,7 +919,8 @@ NdbBlob::updateParts(const char* buf, Uint32 part, Uint32 count)
}
buf += thePartSize;
n++;
- theNewPartFlag = true;
+ thePendingBlobOps |= (1 << NdbOperation::UpdateRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::UpdateRequest);
}
return 0;
}
@@ -919,6 +939,52 @@ NdbBlob::deleteParts(Uint32 part, Uint32 count)
return -1;
}
n++;
+ thePendingBlobOps |= (1 << NdbOperation::DeleteRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::DeleteRequest);
+ }
+ return 0;
+}
+
+// pending ops
+
+int
+NdbBlob::executePendingBlobReads()
+{
+ Uint8 flags = (1 << NdbOperation::ReadRequest);
+ if (thePendingBlobOps & flags) {
+ if (theNdbCon->executeNoBlobs(NoCommit) == -1)
+ return -1;
+ thePendingBlobOps = 0;
+ theNdbCon->thePendingBlobOps = 0;
+ }
+ return 0;
+}
+
+int
+NdbBlob::executePendingBlobWrites()
+{
+ Uint8 flags = 0xFF & ~(1 << NdbOperation::ReadRequest);
+ if (thePendingBlobOps & flags) {
+ if (theNdbCon->executeNoBlobs(NoCommit) == -1)
+ return -1;
+ thePendingBlobOps = 0;
+ theNdbCon->thePendingBlobOps = 0;
+ }
+ return 0;
+}
+
+// callbacks
+
+int
+NdbBlob::invokeActiveHook()
+{
+ DBG("invokeActiveHook");
+ assert(theState == Active && theActiveHook != NULL);
+ int ret = (*theActiveHook)(this, theActiveHookArg);
+ DBG("invokeActiveHook ret=" << ret);
+ if (ret != 0) {
+ // no error is set on blob level
+ return -1;
}
return 0;
}
@@ -948,7 +1014,7 @@ NdbBlob::atPrepare(NdbConnection* aCon, NdbOperation* anOp, const NdbColumnImpl*
partType = NdbDictionary::Column::Binary;
theFillChar = 0x0;
break;
- case NdbDictionary::Column::Clob:
+ case NdbDictionary::Column::Text:
partType = NdbDictionary::Column::Char;
theFillChar = 0x20;
break;
@@ -960,22 +1026,21 @@ NdbBlob::atPrepare(NdbConnection* aCon, NdbOperation* anOp, const NdbColumnImpl*
theInlineSize = theColumn->getInlineSize();
thePartSize = theColumn->getPartSize();
theStripeSize = theColumn->getStripeSize();
- // blob table sanity check
+ // sanity check
assert((NDB_BLOB_HEAD_SIZE << 2) == sizeof(Head));
assert(theColumn->m_attrSize * theColumn->m_arraySize == sizeof(Head) + theInlineSize);
getBlobTableName(theBlobTableName, theTable, theColumn);
const NdbDictionary::Table* bt;
const NdbDictionary::Column* bc;
- if (theInlineSize >= (1 << 16) ||
- thePartSize == 0 ||
- thePartSize >= (1 << 16) ||
- theStripeSize == 0 ||
- (bt = theNdb->theDictionary->getTable(theBlobTableName)) == NULL ||
- (bc = bt->getColumn("DATA")) == NULL ||
- bc->getType() != partType ||
- bc->getLength() != (int)thePartSize) {
- setErrorCode(ErrTable);
- return -1;
+ if (thePartSize > 0) {
+ if (theStripeSize == 0 ||
+ (bt = theNdb->theDictionary->getTable(theBlobTableName)) == NULL ||
+ (bc = bt->getColumn("DATA")) == NULL ||
+ bc->getType() != partType ||
+ bc->getLength() != (int)thePartSize) {
+ setErrorCode(ErrTable);
+ return -1;
+ }
}
// buffers
theKeyBuf.alloc(theTable->m_sizeOfKeysInWords << 2);
@@ -1061,7 +1126,7 @@ NdbBlob::preExecute(ExecType anExecType, bool& batch)
Uint32 bytes = theGetSetBytes - theInlineSize;
if (writeDataPrivate(pos, buf, bytes) == -1)
return -1;
- if (anExecType == Commit && theHeadInlineUpdateFlag) {
+ if (theHeadInlineUpdateFlag) {
// add an operation to update head+inline
NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
if (tOp == NULL ||
@@ -1129,6 +1194,10 @@ NdbBlob::preExecute(ExecType anExecType, bool& batch)
batch = true;
}
}
+ if (theActiveHook != NULL) {
+ // need blob head for callback
+ batch = true;
+ }
DBG("preExecute out batch=" << batch);
return 0;
}
@@ -1145,8 +1214,11 @@ NdbBlob::postExecute(ExecType anExecType)
DBG("postExecute type=" << anExecType);
if (theState == Invalid)
return -1;
- if (theState == Active)
+ if (theState == Active) {
+ setState(anExecType == NoCommit ? Active : Closed);
+ DBG("postExecute skip");
return 0;
+ }
assert(theState == Prepared);
assert(isKeyOp());
if (isIndexOp()) {
@@ -1200,8 +1272,12 @@ NdbBlob::postExecute(ExecType anExecType)
if (deleteParts(0, getPartCount()) == -1)
return -1;
}
- theNewPartFlag = false;
setState(anExecType == NoCommit ? Active : Closed);
+ // activation callback
+ if (theActiveHook != NULL) {
+ if (invokeActiveHook() == -1)
+ return -1;
+ }
DBG("postExecute out");
return 0;
}
@@ -1275,20 +1351,18 @@ NdbBlob::atNextResult()
Uint32 bytes = theGetSetBytes - theInlineSize;
if (readDataPrivate(pos, buf, bytes) == -1)
return -1;
- // must also execute them
- DBG("force execute");
- if (theNdbCon->executeNoBlobs(NoCommit) == -1) {
- setErrorCode((NdbOperation*)0);
- return -1;
- }
}
}
setState(Active);
+ // activation callback
+ if (theActiveHook != NULL) {
+ if (invokeActiveHook() == -1)
+ return -1;
+ }
DBG("atNextResult out");
return 0;
}
-
// misc
const NdbDictionary::Column*
@@ -1304,6 +1378,9 @@ NdbBlob::setErrorCode(int anErrorCode, bool invalidFlag)
{
DBG("setErrorCode code=" << anErrorCode);
theError.code = anErrorCode;
+ // conditionally copy error to operation level
+ if (theNdbOp != NULL && theNdbOp->theError.code == 0)
+ theNdbOp->setErrorCode(theError.code);
if (invalidFlag)
setState(Invalid);
}
@@ -1336,11 +1413,34 @@ NdbBlob::setErrorCode(NdbConnection* aCon, bool invalidFlag)
setErrorCode(code, invalidFlag);
}
+// info about all blobs in this operation
+
+NdbBlob*
+NdbBlob::blobsFirstBlob()
+{
+ return theNdbOp->theBlobList;
+}
+
+NdbBlob*
+NdbBlob::blobsNextBlob()
+{
+ return theNext;
+}
+
+// debug
+
#ifdef VM_TRACE
+inline int
+NdbBlob::getOperationType() const
+{
+ return theNdbOp != NULL ? theNdbOp->theOperationType : -1;
+}
+
NdbOut&
operator<<(NdbOut& out, const NdbBlob& blob)
{
- ndbout << dec << "s=" << blob.theState;
+ ndbout << dec << "o=" << blob.getOperationType();
+ ndbout << dec << " s=" << blob.theState;
ndbout << dec << " n=" << blob.theNullFlag;;
ndbout << dec << " l=" << blob.theLength;
ndbout << dec << " p=" << blob.thePos;
diff --git a/ndb/src/ndbapi/NdbConnection.cpp b/ndb/src/ndbapi/NdbConnection.cpp
index 6f9dbd23372..db6201ee9bb 100644
--- a/ndb/src/ndbapi/NdbConnection.cpp
+++ b/ndb/src/ndbapi/NdbConnection.cpp
@@ -89,7 +89,8 @@ NdbConnection::NdbConnection( Ndb* aNdb ) :
// Scan operations
theScanningOp(NULL),
theBuddyConPtr(0xFFFFFFFF),
- theBlobFlag(false)
+ theBlobFlag(false),
+ thePendingBlobOps(0)
{
theListState = NotInList;
theError.code = 0;
@@ -150,6 +151,7 @@ NdbConnection::init()
theBuddyConPtr = 0xFFFFFFFF;
//
theBlobFlag = false;
+ thePendingBlobOps = 0;
}//NdbConnection::init()
/*****************************************************************************
@@ -269,26 +271,34 @@ NdbConnection::execute(ExecType aTypeOfExec,
if (! theBlobFlag)
return executeNoBlobs(aTypeOfExec, abortOption, forceSend);
- // execute prepared ops in batches, as requested by blobs
+ /*
+ * execute prepared ops in batches, as requested by blobs
+ * - blob error does not terminate execution
+ * - blob error sets error on operation
+ * - if error on operation skip blob calls
+ */
ExecType tExecType;
NdbOperation* tPrepOp;
+ int ret = 0;
do {
tExecType = aTypeOfExec;
tPrepOp = theFirstOpInList;
while (tPrepOp != NULL) {
- bool batch = false;
- NdbBlob* tBlob = tPrepOp->theBlobList;
- while (tBlob != NULL) {
- if (tBlob->preExecute(tExecType, batch) == -1)
- return -1;
- tBlob = tBlob->theNext;
- }
- if (batch) {
- // blob asked to execute all up to here now
- tExecType = NoCommit;
- break;
+ if (tPrepOp->theError.code == 0) {
+ bool batch = false;
+ NdbBlob* tBlob = tPrepOp->theBlobList;
+ while (tBlob != NULL) {
+ if (tBlob->preExecute(tExecType, batch) == -1)
+ ret = -1;
+ tBlob = tBlob->theNext;
+ }
+ if (batch) {
+ // blob asked to execute all up to here now
+ tExecType = NoCommit;
+ break;
+ }
}
tPrepOp = tPrepOp->next();
}
@@ -304,26 +314,30 @@ NdbConnection::execute(ExecType aTypeOfExec,
if (tExecType == Commit) {
NdbOperation* tOp = theCompletedFirstOp;
while (tOp != NULL) {
- NdbBlob* tBlob = tOp->theBlobList;
- while (tBlob != NULL) {
- if (tBlob->preCommit() == -1)
- return -1;
- tBlob = tBlob->theNext;
+ if (tOp->theError.code == 0) {
+ NdbBlob* tBlob = tOp->theBlobList;
+ while (tBlob != NULL) {
+ if (tBlob->preCommit() == -1)
+ ret = -1;
+ tBlob = tBlob->theNext;
+ }
}
tOp = tOp->next();
}
}
if (executeNoBlobs(tExecType, abortOption, forceSend) == -1)
- return -1;
+ ret = -1;
{
NdbOperation* tOp = theCompletedFirstOp;
while (tOp != NULL) {
- NdbBlob* tBlob = tOp->theBlobList;
- while (tBlob != NULL) {
- // may add new operations if batch
- if (tBlob->postExecute(tExecType) == -1)
- return -1;
- tBlob = tBlob->theNext;
+ if (tOp->theError.code == 0) {
+ NdbBlob* tBlob = tOp->theBlobList;
+ while (tBlob != NULL) {
+ // may add new operations if batch
+ if (tBlob->postExecute(tExecType) == -1)
+ ret = -1;
+ tBlob = tBlob->theNext;
+ }
}
tOp = tOp->next();
}
@@ -338,7 +352,7 @@ NdbConnection::execute(ExecType aTypeOfExec,
}
} while (theFirstOpInList != NULL || tExecType != aTypeOfExec);
- return 0;
+ return ret;
}
int
@@ -397,6 +411,7 @@ NdbConnection::executeNoBlobs(ExecType aTypeOfExec,
break;
}
}
+ thePendingBlobOps = 0;
return 0;
}//NdbConnection::execute()
diff --git a/ndb/src/ndbapi/NdbDictionary.cpp b/ndb/src/ndbapi/NdbDictionary.cpp
index 413ad0745db..d5a16546071 100644
--- a/ndb/src/ndbapi/NdbDictionary.cpp
+++ b/ndb/src/ndbapi/NdbDictionary.cpp
@@ -806,73 +806,90 @@ NdbDictionary::Dictionary::getNdbError() const {
return m_impl.getNdbError();
}
-NdbOut& operator <<(NdbOut& ndbout, const NdbDictionary::Column::Type type)
+// printers
+
+NdbOut&
+operator<<(NdbOut& out, const NdbDictionary::Column& col)
{
- switch(type){
- case NdbDictionary::Column::Bigunsigned:
- ndbout << "Bigunsigned";
+ out << col.getName() << " ";
+ switch (col.getType()) {
+ case NdbDictionary::Column::Tinyint:
+ out << "Tinyint";
break;
- case NdbDictionary::Column::Unsigned:
- ndbout << "Unsigned";
+ case NdbDictionary::Column::Tinyunsigned:
+ out << "Tinyunsigned";
+ break;
+ case NdbDictionary::Column::Smallint:
+ out << "Smallint";
break;
case NdbDictionary::Column::Smallunsigned:
- ndbout << "Smallunsigned";
+ out << "Smallunsigned";
break;
- case NdbDictionary::Column::Tinyunsigned:
- ndbout << "Tinyunsigned";
+ case NdbDictionary::Column::Mediumint:
+ out << "Mediumint";
break;
- case NdbDictionary::Column::Bigint:
- ndbout << "Bigint";
+ case NdbDictionary::Column::Mediumunsigned:
+ out << "Mediumunsigned";
break;
case NdbDictionary::Column::Int:
- ndbout << "Int";
+ out << "Int";
break;
- case NdbDictionary::Column::Smallint:
- ndbout << "Smallint";
- break;
- case NdbDictionary::Column::Tinyint:
- ndbout << "Tinyint";
+ case NdbDictionary::Column::Unsigned:
+ out << "Unsigned";
break;
- case NdbDictionary::Column::Char:
- ndbout << "Char";
+ case NdbDictionary::Column::Bigint:
+ out << "Bigint";
break;
- case NdbDictionary::Column::Varchar:
- ndbout << "Varchar";
+ case NdbDictionary::Column::Bigunsigned:
+ out << "Bigunsigned";
break;
case NdbDictionary::Column::Float:
- ndbout << "Float";
+ out << "Float";
break;
case NdbDictionary::Column::Double:
- ndbout << "Double";
+ out << "Double";
break;
- case NdbDictionary::Column::Mediumint:
- ndbout << "Mediumint";
+ case NdbDictionary::Column::Decimal:
+ out << "Decimal(" << col.getScale() << "," << col.getPrecision() << ")";
break;
- case NdbDictionary::Column::Mediumunsigned:
- ndbout << "Mediumunsigend";
+ case NdbDictionary::Column::Char:
+ out << "Char(" << col.getLength() << ")";
+ break;
+ case NdbDictionary::Column::Varchar:
+ out << "Varchar(" << col.getLength() << ")";
break;
case NdbDictionary::Column::Binary:
- ndbout << "Binary";
+ out << "Binary(" << col.getLength() << ")";
break;
case NdbDictionary::Column::Varbinary:
- ndbout << "Varbinary";
+ out << "Varbinary(" << col.getLength() << ")";
break;
- case NdbDictionary::Column::Decimal:
- ndbout << "Decimal";
+ case NdbDictionary::Column::Datetime:
+ out << "Datetime";
break;
case NdbDictionary::Column::Timespec:
- ndbout << "Timespec";
+ out << "Timespec";
break;
case NdbDictionary::Column::Blob:
- ndbout << "Blob";
+ out << "Blob(" << col.getInlineSize() << "," << col.getPartSize()
+ << ";" << col.getStripeSize() << ")";
+ break;
+ case NdbDictionary::Column::Text:
+ out << "Text(" << col.getInlineSize() << "," << col.getPartSize()
+ << ";" << col.getStripeSize() << ")";
break;
case NdbDictionary::Column::Undefined:
- ndbout << "Undefined";
+ out << "Undefined";
break;
default:
- ndbout << "Unknown type=" << (Uint32)type;
+ out << "Type" << (Uint32)col.getType();
break;
}
-
- return ndbout;
+ if (col.getPrimaryKey())
+ out << " PRIMARY KEY";
+ else if (! col.getNullable())
+ out << " NOT NULL";
+ else
+ out << " NULL";
+ return out;
}
diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp
index 71a51efde70..f1091ad5fb3 100644
--- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp
+++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp
@@ -181,7 +181,7 @@ NdbColumnImpl::equal(const NdbColumnImpl& col) const
case NdbDictionary::Column::Timespec:
break;
case NdbDictionary::Column::Blob:
- case NdbDictionary::Column::Clob:
+ case NdbDictionary::Column::Text:
if (m_precision != col.m_precision ||
m_scale != col.m_scale ||
m_length != col.m_length) {
@@ -1088,7 +1088,7 @@ columnTypeMapping[] = {
{ DictTabInfo::ExtDatetime, NdbDictionary::Column::Datetime },
{ DictTabInfo::ExtTimespec, NdbDictionary::Column::Timespec },
{ DictTabInfo::ExtBlob, NdbDictionary::Column::Blob },
- { DictTabInfo::ExtClob, NdbDictionary::Column::Clob },
+ { DictTabInfo::ExtText, NdbDictionary::Column::Text },
{ -1, -1 }
};
@@ -1253,7 +1253,7 @@ NdbDictionaryImpl::createBlobTables(NdbTableImpl &t)
{
for (unsigned i = 0; i < t.m_columns.size(); i++) {
NdbColumnImpl & c = *t.m_columns[i];
- if (! c.getBlobType())
+ if (! c.getBlobType() || c.getPartSize() == 0)
continue;
NdbTableImpl bt;
NdbBlob::getBlobTable(bt, &t, &c);
@@ -1622,7 +1622,7 @@ NdbDictionaryImpl::dropBlobTables(NdbTableImpl & t)
{
for (unsigned i = 0; i < t.m_columns.size(); i++) {
NdbColumnImpl & c = *t.m_columns[i];
- if (! c.getBlobType())
+ if (! c.getBlobType() || c.getPartSize() == 0)
continue;
char btname[NdbBlob::BlobTableNameSize];
NdbBlob::getBlobTableName(btname, &t, &c);
diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/ndb/src/ndbapi/NdbDictionaryImpl.hpp
index 5851c199893..85d334416ce 100644
--- a/ndb/src/ndbapi/NdbDictionaryImpl.hpp
+++ b/ndb/src/ndbapi/NdbDictionaryImpl.hpp
@@ -441,7 +441,7 @@ inline
bool
NdbColumnImpl::getBlobType() const {
return (m_type == NdbDictionary::Column::Blob ||
- m_type == NdbDictionary::Column::Clob);
+ m_type == NdbDictionary::Column::Text);
}
inline
diff --git a/ndb/src/ndbapi/NdbRecAttr.cpp b/ndb/src/ndbapi/NdbRecAttr.cpp
index 99a7c368af7..2e753f13006 100644
--- a/ndb/src/ndbapi/NdbRecAttr.cpp
+++ b/ndb/src/ndbapi/NdbRecAttr.cpp
@@ -29,6 +29,7 @@ Adjust: 971206 UABRONM First version
#include <ndb_global.h>
#include <NdbOut.hpp>
#include <NdbRecAttr.hpp>
+#include <NdbBlob.hpp>
#include "NdbDictionaryImpl.hpp"
#include <NdbTCP.h>
@@ -147,78 +148,100 @@ NdbRecAttr::receive_data(const Uint32 * data, Uint32 sz){
return false;
}
-NdbOut& operator<<(NdbOut& ndbout, const NdbRecAttr &r)
+NdbOut& operator<<(NdbOut& out, const NdbRecAttr &r)
{
if (r.isNULL())
{
- ndbout << "[NULL]";
- return ndbout;
+ out << "[NULL]";
+ return out;
}
if (r.arraySize() > 1)
- ndbout << "[";
+ out << "[";
for (Uint32 j = 0; j < r.arraySize(); j++)
{
if (j > 0)
- ndbout << " ";
+ out << " ";
switch(r.getType())
{
case NdbDictionary::Column::Bigunsigned:
- ndbout << r.u_64_value();
+ out << r.u_64_value();
break;
case NdbDictionary::Column::Unsigned:
- ndbout << r.u_32_value();
+ out << r.u_32_value();
break;
case NdbDictionary::Column::Smallunsigned:
- ndbout << r.u_short_value();
+ out << r.u_short_value();
break;
case NdbDictionary::Column::Tinyunsigned:
- ndbout << (unsigned) r.u_char_value();
+ out << (unsigned) r.u_char_value();
break;
case NdbDictionary::Column::Bigint:
- ndbout << r.int64_value();
+ out << r.int64_value();
break;
case NdbDictionary::Column::Int:
- ndbout << r.int32_value();
+ out << r.int32_value();
break;
case NdbDictionary::Column::Smallint:
- ndbout << r.short_value();
+ out << r.short_value();
break;
case NdbDictionary::Column::Tinyint:
- ndbout << (int) r.char_value();
+ out << (int) r.char_value();
break;
case NdbDictionary::Column::Char:
- ndbout.print("%.*s", r.arraySize(), r.aRef());
+ out.print("%.*s", r.arraySize(), r.aRef());
j = r.arraySize();
break;
case NdbDictionary::Column::Varchar:
{
short len = ntohs(r.u_short_value());
- ndbout.print("%.*s", len, r.aRef()+2);
+ out.print("%.*s", len, r.aRef()+2);
}
j = r.arraySize();
break;
case NdbDictionary::Column::Float:
- ndbout << r.float_value();
+ out << r.float_value();
break;
case NdbDictionary::Column::Double:
- ndbout << r.double_value();
+ out << r.double_value();
break;
+ case NdbDictionary::Column::Blob:
+ {
+ const NdbBlob::Head* h = (const NdbBlob::Head*)r.aRef();
+ out << h->length << ":";
+ const unsigned char* p = (const unsigned char*)(h + 1);
+ unsigned n = r.arraySize() - sizeof(*h);
+ for (unsigned k = 0; k < n && k < h->length; k++)
+ out.print("%02X", (int)p[k]);
+ j = r.arraySize();
+ }
+ break;
+ case NdbDictionary::Column::Text:
+ {
+ const NdbBlob::Head* h = (const NdbBlob::Head*)r.aRef();
+ out << h->length << ":";
+ const unsigned char* p = (const unsigned char*)(h + 1);
+ unsigned n = r.arraySize() - sizeof(*h);
+ for (unsigned k = 0; k < n && k < h->length; k++)
+ out.print("%c", (int)p[k]);
+ j = r.arraySize();
+ }
+ break;
default: /* no print functions for the rest, just print type */
- ndbout << r.getType();
+ out << r.getType();
j = r.arraySize();
if (j > 1)
- ndbout << " %u times" << j;
+ out << " " << j << " times";
break;
}
}
if (r.arraySize() > 1)
{
- ndbout << "]";
+ out << "]";
}
- return ndbout;
+ return out;
}
diff --git a/ndb/src/ndbapi/NdbResultSet.cpp b/ndb/src/ndbapi/NdbResultSet.cpp
index b286c9fd7c9..f270584d227 100644
--- a/ndb/src/ndbapi/NdbResultSet.cpp
+++ b/ndb/src/ndbapi/NdbResultSet.cpp
@@ -55,6 +55,13 @@ int NdbResultSet::nextResult(bool fetchAllowed)
return -1;
tBlob = tBlob->theNext;
}
+ /*
+ * Flush blob part ops on behalf of user because
+ * - nextResult is analogous to execute(NoCommit)
+ * - user is likely to want blob value before next execute
+ */
+ if (m_operation->m_transConnection->executePendingBlobOps() == -1)
+ return -1;
return 0;
}
return res;
diff --git a/ndb/test/include/NDBT_Table.hpp b/ndb/test/include/NDBT_Table.hpp
index eee76773106..bf44e1eb52b 100644
--- a/ndb/test/include/NDBT_Table.hpp
+++ b/ndb/test/include/NDBT_Table.hpp
@@ -23,7 +23,6 @@
#include <NdbOut.hpp>
class NDBT_Attribute : public NdbDictionary::Column {
- friend class NdbOut& operator <<(class NdbOut&, const NDBT_Attribute &);
public:
NDBT_Attribute(const char* _name,
Column::Type _type,
diff --git a/ndb/test/ndbapi/testBlobs.cpp b/ndb/test/ndbapi/testBlobs.cpp
index b880266f8de..6ffac3028b1 100644
--- a/ndb/test/ndbapi/testBlobs.cpp
+++ b/ndb/test/ndbapi/testBlobs.cpp
@@ -38,6 +38,7 @@ struct Bcol {
};
struct Opt {
+ unsigned m_batch;
bool m_core;
bool m_dbg;
bool m_dbgall;
@@ -46,7 +47,8 @@ struct Opt {
unsigned m_parts;
unsigned m_rows;
unsigned m_seed;
- char m_skip[255];
+ const char* m_skip;
+ const char* m_style;
// metadata
const char* m_tname;
const char* m_x1name; // hash index
@@ -60,6 +62,7 @@ struct Opt {
int m_bug;
int (*m_bugtest)();
Opt() :
+ m_batch(7),
m_core(false),
m_dbg(false),
m_dbgall(false),
@@ -68,6 +71,8 @@ struct Opt {
m_parts(10),
m_rows(100),
m_seed(0),
+ m_skip(""),
+ m_style("012"),
// metadata
m_tname("TBLOB1"),
m_x1name("TBLOB1X1"),
@@ -80,7 +85,6 @@ struct Opt {
// bugs
m_bug(0),
m_bugtest(0) {
- memset(m_skip, false, sizeof(m_skip));
}
};
@@ -92,6 +96,7 @@ printusage()
Opt d;
ndbout
<< "usage: testBlobs options [default/max]" << endl
+ << " -batch N number of pk ops in batch [" << d.m_batch << "]" << endl
<< " -core dump core on error" << endl
<< " -dbg print debug" << endl
<< " -dbgall print also NDB API debug (if compiled in)" << endl
@@ -101,7 +106,8 @@ printusage()
<< " -parts N max parts in blob value [" << d.m_parts << "]" << endl
<< " -rows N number of rows [" << d.m_rows << "]" << endl
<< " -seed N random seed 0=loop number [" << d.m_seed << "]" << endl
- << " -skip xxx skip these tests (see list)" << endl
+ << " -skip xxx skip these tests (see list) [" << d.m_skip << endl
+ << " -style xxx access styles to test (see list) [" << d.m_style << "]" << endl
<< "metadata" << endl
<< " -pk2len N length of PK2 [" << d.m_pk2len << "/" << g_max_pk2len <<"]" << endl
<< " -oneblob only 1 blob attribute [default 2]" << endl
@@ -111,8 +117,10 @@ printusage()
<< " s table scans" << endl
<< " r ordered index scans" << endl
<< " u update blob value" << endl
- << " v getValue / setValue" << endl
- << " w readData / writeData" << endl
+ << "access styles for -style" << endl
+ << " 0 getValue / setValue" << endl
+ << " 1 setActiveHook" << endl
+ << " 2 readData / writeData" << endl
<< "bug tests (no blob test)" << endl
<< " -bug 4088 ndb api hang with mixed ops on index table" << endl
<< " -bug 2222 delete + write gives 626" << endl
@@ -122,11 +130,16 @@ printusage()
static Opt g_opt;
-static char&
-skip(unsigned x)
+static bool
+skipcase(int x)
{
- assert(x < sizeof(g_opt.m_skip));
- return g_opt.m_skip[x];
+ return strchr(g_opt.m_skip, x) != 0;
+}
+
+static bool
+skipstyle(int x)
+{
+ return strchr(g_opt.m_style, '0' + x) == 0;
}
static Ndb* g_ndb = 0;
@@ -138,11 +151,12 @@ static NdbScanOperation* g_ops = 0;
static NdbBlob* g_bh1 = 0;
static NdbBlob* g_bh2 = 0;
static bool g_printerror = true;
+static unsigned g_loop = 0;
static void
printerror(int line, const char* msg)
{
- ndbout << "line " << line << ": " << msg << " failed" << endl;
+ ndbout << "line " << line << " FAIL " << msg << endl;
if (! g_printerror) {
return;
}
@@ -205,6 +219,7 @@ static int
createTable()
{
NdbDictionary::Table tab(g_opt.m_tname);
+ tab.setLogging(false);
// col PK1 - Uint32
{ NdbDictionary::Column col("PK1");
col.setType(NdbDictionary::Column::Unsigned);
@@ -228,11 +243,11 @@ createTable()
col.setPrimaryKey(true);
tab.addColumn(col);
}
- // col BL2 - Clob nullable
+ // col BL2 - Text nullable
if (! g_opt.m_oneblob)
{ NdbDictionary::Column col("BL2");
const Bcol& b = g_opt.m_blob2;
- col.setType(NdbDictionary::Column::Clob);
+ col.setType(NdbDictionary::Column::Text);
col.setNullable(true);
col.setInlineSize(b.m_inline);
col.setPartSize(b.m_partsize);
@@ -245,6 +260,7 @@ createTable()
if (g_opt.m_pk2len != 0)
{ NdbDictionary::Index idx(g_opt.m_x1name);
idx.setType(NdbDictionary::Index::UniqueHashIndex);
+ idx.setLogging(false);
idx.setTable(g_opt.m_tname);
idx.addColumnName("PK2");
CHK(g_dic->createIndex(idx) == 0);
@@ -281,7 +297,7 @@ struct Bval {
m_buf = new char [m_buflen];
trash();
}
- void copy(const Bval& v) {
+ void copyfrom(const Bval& v) {
m_len = v.m_len;
delete [] m_val;
if (v.m_val == 0)
@@ -313,10 +329,10 @@ struct Tup {
m_blob1.alloc(g_opt.m_blob1.m_inline + g_opt.m_blob1.m_partsize * g_opt.m_parts);
m_blob2.alloc(g_opt.m_blob2.m_inline + g_opt.m_blob2.m_partsize * g_opt.m_parts);
}
- void copy(const Tup& tup) {
+ void copyfrom(const Tup& tup) {
assert(m_pk1 == tup.m_pk1);
- m_blob1.copy(tup.m_blob1);
- m_blob2.copy(tup.m_blob2);
+ m_blob1.copyfrom(tup.m_blob1);
+ m_blob2.copyfrom(tup.m_blob2);
}
private:
Tup(const Tup&);
@@ -358,6 +374,14 @@ calcBval(const Bcol& b, Bval& v, bool keepsize)
}
static void
+calcBval(Tup& tup, bool keepsize)
+{
+ calcBval(g_opt.m_blob1, tup.m_blob1, keepsize);
+ if (! g_opt.m_oneblob)
+ calcBval(g_opt.m_blob2, tup.m_blob2, keepsize);
+}
+
+static void
calcTups(bool keepsize)
{
for (unsigned k = 0; k < g_opt.m_rows; k++) {
@@ -371,31 +395,59 @@ calcTups(bool keepsize)
tup.m_pk2[i] = 'a' + i % 26;
}
}
- calcBval(g_opt.m_blob1, tup.m_blob1, keepsize);
- if (! g_opt.m_oneblob)
- calcBval(g_opt.m_blob2, tup.m_blob2, keepsize);
+ calcBval(tup, keepsize);
}
}
// blob handle ops
static int
+getBlobHandles(NdbOperation* opr)
+{
+ CHK((g_bh1 = opr->getBlobHandle("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((g_bh2 = opr->getBlobHandle("BL2")) != 0);
+ return 0;
+}
+
+static int
+getBlobHandles(NdbIndexOperation* opx)
+{
+ CHK((g_bh1 = opx->getBlobHandle("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((g_bh2 = opx->getBlobHandle("BL2")) != 0);
+ return 0;
+}
+
+static int
+getBlobHandles(NdbScanOperation* ops)
+{
+ CHK((g_bh1 = ops->getBlobHandle("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((g_bh2 = ops->getBlobHandle("BL2")) != 0);
+ return 0;
+}
+
+static int
getBlobLength(NdbBlob* h, unsigned& len)
{
Uint64 len2 = (unsigned)-1;
CHK(h->getLength(len2) == 0);
len = (unsigned)len2;
assert(len == len2);
+ DBG("getBlobLength " << h->getColumn()->getName() << " len=" << len);
return 0;
}
+// setValue / getValue
+
static int
setBlobValue(NdbBlob* h, const Bval& v)
{
bool null = (v.m_val == 0);
bool isNull;
unsigned len;
- DBG("set " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
+ DBG("setValue " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
if (null) {
CHK(h->setNull() == 0);
isNull = false;
@@ -410,10 +462,19 @@ setBlobValue(NdbBlob* h, const Bval& v)
}
static int
+setBlobValue(const Tup& tup)
+{
+ CHK(setBlobValue(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(setBlobValue(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static int
getBlobValue(NdbBlob* h, const Bval& v)
{
bool null = (v.m_val == 0);
- DBG("get " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
+ DBG("getValue " << h->getColumn()->getName() << " buflen=" << v.m_buflen);
CHK(h->getValue(v.m_buf, v.m_buflen) == 0);
return 0;
}
@@ -456,6 +517,8 @@ verifyBlobValue(const Tup& tup)
return 0;
}
+// readData / writeData
+
static int
writeBlobData(NdbBlob* h, const Bval& v)
{
@@ -469,6 +532,7 @@ writeBlobData(NdbBlob* h, const Bval& v)
CHK(h->getNull(isNull) == 0 && isNull == true);
CHK(getBlobLength(h, len) == 0 && len == 0);
} else {
+ CHK(h->truncate(v.m_len) == 0);
unsigned n = 0;
do {
unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1);
@@ -487,6 +551,15 @@ writeBlobData(NdbBlob* h, const Bval& v)
}
static int
+writeBlobData(const Tup& tup)
+{
+ CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static int
readBlobData(NdbBlob* h, const Bval& v)
{
bool null = (v.m_val == 0);
@@ -531,6 +604,71 @@ readBlobData(const Tup& tup)
return 0;
}
+// hooks
+
+static NdbBlob::ActiveHook blobWriteHook;
+
+static int
+blobWriteHook(NdbBlob* h, void* arg)
+{
+ DBG("blobWriteHook");
+ Bval& v = *(Bval*)arg;
+ CHK(writeBlobData(h, v) == 0);
+ return 0;
+}
+
+static int
+setBlobWriteHook(NdbBlob* h, Bval& v)
+{
+ DBG("setBlobWriteHook");
+ CHK(h->setActiveHook(blobWriteHook, &v) == 0);
+ return 0;
+}
+
+static int
+setBlobWriteHook(Tup& tup)
+{
+ CHK(setBlobWriteHook(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(setBlobWriteHook(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static NdbBlob::ActiveHook blobReadHook;
+
+// no PK yet to identify tuple so just read the value
+static int
+blobReadHook(NdbBlob* h, void* arg)
+{
+ DBG("blobReadHook");
+ Bval& v = *(Bval*)arg;
+ unsigned len;
+ CHK(getBlobLength(h, len) == 0);
+ v.alloc(len);
+ Uint32 maxlen = 0xffffffff;
+ CHK(h->readData(v.m_buf, maxlen) == 0);
+ DBG("read " << maxlen << " bytes");
+ CHK(len == maxlen);
+ return 0;
+}
+
+static int
+setBlobReadHook(NdbBlob* h, Bval& v)
+{
+ DBG("setBlobReadHook");
+ CHK(h->setActiveHook(blobReadHook, &v) == 0);
+ return 0;
+}
+
+static int
+setBlobReadHook(Tup& tup)
+{
+ CHK(setBlobReadHook(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(setBlobReadHook(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
// verify blob data
static int
@@ -540,7 +678,11 @@ verifyHeadInline(const Bcol& c, const Bval& v, NdbRecAttr* ra)
CHK(ra->isNULL() == 1);
} else {
CHK(ra->isNULL() == 0);
- CHK(ra->u_64_value() == v.m_len);
+ const NdbBlob::Head* head = (const NdbBlob::Head*)ra->aRef();
+ CHK(head->length == v.m_len);
+ const char* data = (const char*)(head + 1);
+ for (unsigned i = 0; i < head->length && i < c.m_inline; i++)
+ CHK(data[i] == v.m_val[i]);
}
return 0;
}
@@ -548,7 +690,7 @@ verifyHeadInline(const Bcol& c, const Bval& v, NdbRecAttr* ra)
static int
verifyHeadInline(const Tup& tup)
{
- DBG("verifyHeadInline pk1=" << tup.m_pk1);
+ DBG("verifyHeadInline pk1=" << hex << tup.m_pk1);
CHK((g_con = g_ndb->startTransaction()) != 0);
CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
CHK(g_opr->readTuple() == 0);
@@ -580,7 +722,7 @@ verifyHeadInline(const Tup& tup)
static int
verifyBlobTable(const Bcol& b, const Bval& v, Uint32 pk1, bool exists)
{
- DBG("verify " << b.m_btname << " pk1=" << pk1);
+ DBG("verify " << b.m_btname << " pk1=" << hex << pk1);
NdbRecAttr* ra_pk;
NdbRecAttr* ra_part;
NdbRecAttr* ra_data;
@@ -640,7 +782,7 @@ verifyBlob()
{
for (unsigned k = 0; k < g_opt.m_rows; k++) {
const Tup& tup = g_tups[k];
- DBG("verifyBlob pk1=" << tup.m_pk1);
+ DBG("verifyBlob pk1=" << hex << tup.m_pk1);
CHK(verifyHeadInline(tup) == 0);
CHK(verifyBlobTable(tup) == 0);
}
@@ -649,105 +791,120 @@ verifyBlob()
// operations
+static const char* stylename[3] = {
+ "style=getValue/setValue",
+ "style=setActiveHook",
+ "style=readData/writeData"
+};
+
+// pk ops
+
static int
-insertPk(bool rw)
+insertPk(int style)
{
- DBG("--- insertPk ---");
+ DBG("--- insertPk " << stylename[style] << " ---");
+ unsigned n = 0;
+ CHK((g_con = g_ndb->startTransaction()) != 0);
for (unsigned k = 0; k < g_opt.m_rows; k++) {
Tup& tup = g_tups[k];
- DBG("insertPk pk1=" << tup.m_pk1);
- CHK((g_con = g_ndb->startTransaction()) != 0);
+ DBG("insertPk pk1=" << hex << tup.m_pk1);
CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
CHK(g_opr->insertTuple() == 0);
CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
if (g_opt.m_pk2len != 0)
CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
- CHK((g_bh1 = g_opr->getBlobHandle("BL1")) != 0);
- if (! g_opt.m_oneblob)
- CHK((g_bh2 = g_opr->getBlobHandle("BL2")) != 0);
- if (! rw) {
- CHK(setBlobValue(g_bh1, tup.m_blob1) == 0);
- if (! g_opt.m_oneblob)
- CHK(setBlobValue(g_bh2, tup.m_blob2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(setBlobWriteHook(tup) == 0);
} else {
// non-nullable must be set
CHK(g_bh1->setValue("", 0) == 0);
CHK(g_con->execute(NoCommit) == 0);
- CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
- if (! g_opt.m_oneblob)
- CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ // just another trap
+ if (urandom(10) == 0)
+ CHK(g_con->execute(NoCommit) == 0);
+ if (++n == g_opt.m_batch) {
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ n = 0;
}
- CHK(g_con->execute(Commit) == 0);
- g_ndb->closeTransaction(g_con);
g_opr = 0;
- g_con = 0;
tup.m_exists = true;
}
+ if (n != 0) {
+ CHK(g_con->execute(Commit) == 0);
+ n = 0;
+ }
+ g_ndb->closeTransaction(g_con);
+ g_con = 0;
return 0;
}
static int
-updatePk(bool rw)
+readPk(int style)
{
- DBG("--- updatePk ---");
+ DBG("--- readPk " << stylename[style] << " ---");
for (unsigned k = 0; k < g_opt.m_rows; k++) {
Tup& tup = g_tups[k];
- DBG("updatePk pk1=" << tup.m_pk1);
+ DBG("readPk pk1=" << hex << tup.m_pk1);
CHK((g_con = g_ndb->startTransaction()) != 0);
CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
- CHK(g_opr->updateTuple() == 0);
+ CHK(g_opr->readTuple() == 0);
CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
if (g_opt.m_pk2len != 0)
CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
- CHK((g_bh1 = g_opr->getBlobHandle("BL1")) != 0);
- if (! g_opt.m_oneblob)
- CHK((g_bh2 = g_opr->getBlobHandle("BL2")) != 0);
- if (! rw) {
- CHK(setBlobValue(g_bh1, tup.m_blob1) == 0);
- if (! g_opt.m_oneblob)
- CHK(setBlobValue(g_bh2, tup.m_blob2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(getBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobReadHook(tup) == 0);
} else {
CHK(g_con->execute(NoCommit) == 0);
- CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
- if (! g_opt.m_oneblob)
- CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
+ CHK(readBlobData(tup) == 0);
}
CHK(g_con->execute(Commit) == 0);
+ if (style == 0 || style == 1) {
+ CHK(verifyBlobValue(tup) == 0);
+ }
g_ndb->closeTransaction(g_con);
g_opr = 0;
g_con = 0;
- tup.m_exists = true;
}
return 0;
}
static int
-updateIdx(bool rw)
+updatePk(int style)
{
- DBG("--- updateIdx ---");
+ DBG("--- updatePk " << stylename[style] << " ---");
for (unsigned k = 0; k < g_opt.m_rows; k++) {
Tup& tup = g_tups[k];
- DBG("updateIdx pk1=" << tup.m_pk1);
+ DBG("updatePk pk1=" << hex << tup.m_pk1);
CHK((g_con = g_ndb->startTransaction()) != 0);
- CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
- CHK(g_opx->updateTuple() == 0);
- CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
- CHK((g_bh1 = g_opx->getBlobHandle("BL1")) != 0);
- if (! g_opt.m_oneblob)
- CHK((g_bh2 = g_opx->getBlobHandle("BL2")) != 0);
- if (! rw) {
- CHK(setBlobValue(g_bh1, tup.m_blob1) == 0);
- if (! g_opt.m_oneblob)
- CHK(setBlobValue(g_bh2, tup.m_blob2) == 0);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->updateTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobWriteHook(tup) == 0);
} else {
CHK(g_con->execute(NoCommit) == 0);
- CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
- if (! g_opt.m_oneblob)
- CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
+ CHK(writeBlobData(tup) == 0);
}
CHK(g_con->execute(Commit) == 0);
g_ndb->closeTransaction(g_con);
- g_opx = 0;
+ g_opr = 0;
g_con = 0;
tup.m_exists = true;
}
@@ -755,74 +912,115 @@ updateIdx(bool rw)
}
static int
-readPk(bool rw)
+deletePk()
{
- DBG("--- readPk ---");
+ DBG("--- deletePk ---");
for (unsigned k = 0; k < g_opt.m_rows; k++) {
Tup& tup = g_tups[k];
- DBG("readPk pk1=" << tup.m_pk1);
+ DBG("deletePk pk1=" << hex << tup.m_pk1);
CHK((g_con = g_ndb->startTransaction()) != 0);
CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
- CHK(g_opr->readTuple() == 0);
+ CHK(g_opr->deleteTuple() == 0);
CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
if (g_opt.m_pk2len != 0)
CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
- CHK((g_bh1 = g_opr->getBlobHandle("BL1")) != 0);
- if (! g_opt.m_oneblob)
- CHK((g_bh2 = g_opr->getBlobHandle("BL2")) != 0);
- if (! rw) {
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opr = 0;
+ g_con = 0;
+ tup.m_exists = false;
+ }
+ return 0;
+}
+
+// hash index ops
+
+static int
+readIdx(int style)
+{
+ DBG("--- readIdx " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("readIdx pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->readTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opx) == 0);
+ if (style == 0) {
CHK(getBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobReadHook(tup) == 0);
} else {
CHK(g_con->execute(NoCommit) == 0);
CHK(readBlobData(tup) == 0);
}
CHK(g_con->execute(Commit) == 0);
- if (! rw) {
+ if (style == 0 || style == 1) {
CHK(verifyBlobValue(tup) == 0);
}
g_ndb->closeTransaction(g_con);
- g_opr = 0;
+ g_opx = 0;
g_con = 0;
}
return 0;
}
static int
-readIdx(bool rw)
+updateIdx(int style)
{
- DBG("--- readIdx ---");
+ DBG("--- updateIdx " << stylename[style] << " ---");
for (unsigned k = 0; k < g_opt.m_rows; k++) {
Tup& tup = g_tups[k];
- DBG("readIdx pk1=" << tup.m_pk1);
+ DBG("updateIdx pk1=" << hex << tup.m_pk1);
CHK((g_con = g_ndb->startTransaction()) != 0);
CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
- CHK(g_opx->readTuple() == 0);
+ CHK(g_opx->updateTuple() == 0);
CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
- CHK((g_bh1 = g_opx->getBlobHandle("BL1")) != 0);
- if (! g_opt.m_oneblob)
- CHK((g_bh2 = g_opx->getBlobHandle("BL2")) != 0);
- if (! rw) {
- CHK(getBlobValue(tup) == 0);
+ CHK(getBlobHandles(g_opx) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobWriteHook(tup) == 0);
} else {
CHK(g_con->execute(NoCommit) == 0);
- CHK(readBlobData(tup) == 0);
+ CHK(writeBlobData(tup) == 0);
}
CHK(g_con->execute(Commit) == 0);
- if (! rw) {
- CHK(verifyBlobValue(tup) == 0);
- }
g_ndb->closeTransaction(g_con);
g_opx = 0;
g_con = 0;
+ tup.m_exists = true;
+ }
+ return 0;
+}
+
+static int
+deleteIdx()
+{
+ DBG("--- deleteIdx ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("deleteIdx pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->deleteTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opx = 0;
+ g_con = 0;
+ tup.m_exists = false;
}
return 0;
}
+// scan ops table and index
+
static int
-readScan(bool rw, bool idx)
+readScan(int style, bool idx)
{
- const char* func = ! idx ? "scan read table" : "scan read index";
- DBG("--- " << func << " ---");
+ DBG("--- " << "readScan" << (idx ? "Idx" : "") << " " << stylename[style] << " ---");
Tup tup;
tup.alloc(); // allocate buffers
NdbResultSet* rs;
@@ -836,11 +1034,11 @@ readScan(bool rw, bool idx)
CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
if (g_opt.m_pk2len != 0)
CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
- CHK((g_bh1 = g_ops->getBlobHandle("BL1")) != 0);
- if (! g_opt.m_oneblob)
- CHK((g_bh2 = g_ops->getBlobHandle("BL2")) != 0);
- if (! rw) {
+ CHK(getBlobHandles(g_ops) == 0);
+ if (style == 0) {
CHK(getBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobReadHook(tup) == 0);
}
CHK(g_con->execute(NoCommit) == 0);
unsigned rows = 0;
@@ -851,11 +1049,14 @@ readScan(bool rw, bool idx)
CHK((ret = rs->nextResult(true)) == 0 || ret == 1);
if (ret == 1)
break;
- DBG(func << " pk1=" << tup.m_pk1);
+ DBG("readScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
CHK(k < g_opt.m_rows && g_tups[k].m_exists);
- tup.copy(g_tups[k]);
- if (! rw) {
+ tup.copyfrom(g_tups[k]);
+ if (style == 0) {
+ CHK(verifyBlobValue(tup) == 0);
+ } else if (style == 1) {
+ // execute ops generated by callbacks, if any
CHK(verifyBlobValue(tup) == 0);
} else {
CHK(readBlobData(tup) == 0);
@@ -870,52 +1071,63 @@ readScan(bool rw, bool idx)
}
static int
-deletePk()
+updateScan(int style, bool idx)
{
- DBG("--- deletePk ---");
- for (unsigned k = 0; k < g_opt.m_rows; k++) {
- Tup& tup = g_tups[k];
- DBG("deletePk pk1=" << tup.m_pk1);
- CHK((g_con = g_ndb->startTransaction()) != 0);
- CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
- CHK(g_opr->deleteTuple() == 0);
- CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
- if (g_opt.m_pk2len != 0)
- CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
- CHK(g_con->execute(Commit) == 0);
- g_ndb->closeTransaction(g_con);
- g_opr = 0;
- g_con = 0;
- tup.m_exists = false;
+ DBG("--- " << "updateScan" << (idx ? "Idx" : "") << " " << stylename[style] << " ---");
+ Tup tup;
+ tup.alloc(); // allocate buffers
+ NdbResultSet* rs;
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ if (! idx) {
+ CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
+ } else {
+ CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
}
- return 0;
-}
-
-static int
-deleteIdx()
-{
- DBG("--- deleteIdx ---");
- for (unsigned k = 0; k < g_opt.m_rows; k++) {
- Tup& tup = g_tups[k];
- DBG("deleteIdx pk1=" << tup.m_pk1);
- CHK((g_con = g_ndb->startTransaction()) != 0);
- CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
- CHK(g_opx->deleteTuple() == 0);
- CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
- CHK(g_con->execute(Commit) == 0);
- g_ndb->closeTransaction(g_con);
- g_opx = 0;
- g_con = 0;
- tup.m_exists = false;
+ CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Exclusive)) != 0);
+ CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned rows = 0;
+ while (1) {
+ int ret;
+ tup.m_pk1 = (Uint32)-1;
+ memset(tup.m_pk2, 'x', g_opt.m_pk2len);
+ CHK((ret = rs->nextResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ DBG("updateScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
+ Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
+ CHK(k < g_opt.m_rows && g_tups[k].m_exists);
+ // calculate new blob values
+ calcBval(g_tups[k], false);
+ tup.copyfrom(g_tups[k]);
+ CHK((g_opr = rs->updateTuple()) != 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(NoCommit) == 0);
+ g_opr = 0;
+ rows++;
}
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_con = 0;
+ g_ops = 0;
+ CHK(g_opt.m_rows == rows);
return 0;
}
static int
deleteScan(bool idx)
{
- const char* func = ! idx ? "scan delete table" : "scan delete index";
- DBG("--- " << func << " ---");
+ DBG("--- " << "deleteScan" << (idx ? "Idx" : "") << " ---");
Tup tup;
NdbResultSet* rs;
CHK((g_con = g_ndb->startTransaction()) != 0);
@@ -937,7 +1149,7 @@ deleteScan(bool idx)
CHK((ret = rs->nextResult()) == 0 || ret == 1);
if (ret == 1)
break;
- DBG(func << " pk1=" << tup.m_pk1);
+ DBG("deleteScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
CHK(rs->deleteTuple() == 0);
CHK(g_con->execute(NoCommit) == 0);
Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
@@ -948,7 +1160,6 @@ deleteScan(bool idx)
CHK(g_con->execute(Commit) == 0);
g_ndb->closeTransaction(g_con);
g_con = 0;
- g_opr = 0;
g_ops = 0;
CHK(g_opt.m_rows == rows);
return 0;
@@ -981,69 +1192,75 @@ testmain()
}
if (g_opt.m_seed != 0)
srandom(g_opt.m_seed);
- for (unsigned loop = 0; g_opt.m_loop == 0 || loop < g_opt.m_loop; loop++) {
- DBG("=== loop " << loop << " ===");
+ for (g_loop = 0; g_opt.m_loop == 0 || g_loop < g_opt.m_loop; g_loop++) {
+ DBG("=== loop " << g_loop << " ===");
if (g_opt.m_seed == 0)
- srandom(loop);
- bool llim = skip('v') ? true : false;
- bool ulim = skip('w') ? false : true;
+ srandom(g_loop);
// pk
- for (int rw = llim; rw <= ulim; rw++) {
- if (skip('k'))
+ for (int style = 0; style <= 2; style++) {
+ if (skipcase('k') || skipstyle(style))
continue;
- DBG("--- pk ops " << (! rw ? "get/set" : "read/write") << " ---");
+ DBG("--- pk ops " << stylename[style] << " ---");
calcTups(false);
- CHK(insertPk(rw) == 0);
+ CHK(insertPk(style) == 0);
CHK(verifyBlob() == 0);
- CHK(readPk(rw) == 0);
- if (! skip('u')) {
- calcTups(rw);
- CHK(updatePk(rw) == 0);
+ CHK(readPk(style) == 0);
+ if (! skipcase('u')) {
+ calcTups(style);
+ CHK(updatePk(style) == 0);
CHK(verifyBlob() == 0);
}
- CHK(readPk(rw) == 0);
+ CHK(readPk(style) == 0);
CHK(deletePk() == 0);
CHK(verifyBlob() == 0);
}
// hash index
- for (int rw = llim; rw <= ulim; rw++) {
- if (skip('i'))
+ for (int style = 0; style <= 2; style++) {
+ if (skipcase('i') || skipstyle(style))
continue;
- DBG("--- idx ops " << (! rw ? "get/set" : "read/write") << " ---");
+ DBG("--- idx ops " << stylename[style] << " ---");
calcTups(false);
- CHK(insertPk(rw) == 0);
+ CHK(insertPk(style) == 0);
CHK(verifyBlob() == 0);
- CHK(readIdx(rw) == 0);
- calcTups(rw);
- if (! skip('u')) {
- CHK(updateIdx(rw) == 0);
+ CHK(readIdx(style) == 0);
+ calcTups(style);
+ if (! skipcase('u')) {
+ CHK(updateIdx(style) == 0);
CHK(verifyBlob() == 0);
- CHK(readIdx(rw) == 0);
+ CHK(readIdx(style) == 0);
}
CHK(deleteIdx() == 0);
CHK(verifyBlob() == 0);
}
// scan table
- for (int rw = llim; rw <= ulim; rw++) {
- if (skip('s'))
+ for (int style = 0; style <= 2; style++) {
+ if (skipcase('s') || skipstyle(style))
continue;
- DBG("--- table scan " << (! rw ? "get/set" : "read/write") << " ---");
+ DBG("--- table scan " << stylename[style] << " ---");
calcTups(false);
- CHK(insertPk(rw) == 0);
+ CHK(insertPk(style) == 0);
CHK(verifyBlob() == 0);
- CHK(readScan(rw, false) == 0);
+ CHK(readScan(style, false) == 0);
+ if (! skipcase('u')) {
+ CHK(updateScan(style, false) == 0);
+ CHK(verifyBlob() == 0);
+ }
CHK(deleteScan(false) == 0);
CHK(verifyBlob() == 0);
}
// scan index
- for (int rw = llim; rw <= ulim; rw++) {
- if (skip('r'))
+ for (int style = 0; style <= 2; style++) {
+ if (skipcase('r') || skipstyle(style))
continue;
- DBG("--- index scan " << (! rw ? "get/set" : "read/write") << " ---");
+ DBG("--- index scan " << stylename[style] << " ---");
calcTups(false);
- CHK(insertPk(rw) == 0);
+ CHK(insertPk(style) == 0);
CHK(verifyBlob() == 0);
- CHK(readScan(rw, true) == 0);
+ CHK(readScan(style, true) == 0);
+ if (! skipcase('u')) {
+ CHK(updateScan(style, true) == 0);
+ CHK(verifyBlob() == 0);
+ }
CHK(deleteScan(true) == 0);
CHK(verifyBlob() == 0);
}
@@ -1121,6 +1338,12 @@ NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
{
while (++argv, --argc > 0) {
const char* arg = argv[0];
+ if (strcmp(arg, "-batch") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_batch = atoi(argv[0]);
+ continue;
+ }
+ }
if (strcmp(arg, "-core") == 0) {
g_opt.m_core = true;
continue;
@@ -1165,9 +1388,13 @@ NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
}
if (strcmp(arg, "-skip") == 0) {
if (++argv, --argc > 0) {
- for (const char* p = argv[0]; *p != 0; p++) {
- skip(*p) = true;
- }
+ g_opt.m_skip = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-style") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_style = strdup(argv[0]);
continue;
}
}
@@ -1175,10 +1402,6 @@ NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
if (strcmp(arg, "-pk2len") == 0) {
if (++argv, --argc > 0) {
g_opt.m_pk2len = atoi(argv[0]);
- if (g_opt.m_pk2len == 0) {
- skip('i') = true;
- skip('r') = true;
- }
if (g_opt.m_pk2len <= g_max_pk2len)
continue;
}
@@ -1205,7 +1428,15 @@ NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
printusage();
return NDBT_ProgramExit(NDBT_WRONGARGS);
}
+ if (g_opt.m_pk2len == 0) {
+ char b[100];
+ strcpy(b, g_opt.m_skip);
+ strcat(b, "i");
+ strcat(b, "r");
+ g_opt.m_skip = strdup(b);
+ }
if (testmain() == -1) {
+ ndbout << "line " << __LINE__ << " FAIL loop=" << g_loop << endl;
return NDBT_ProgramExit(NDBT_FAILED);
}
return NDBT_ProgramExit(NDBT_OK);
diff --git a/ndb/test/src/NDBT_Table.cpp b/ndb/test/src/NDBT_Table.cpp
index 485377e690a..d283cdf5912 100644
--- a/ndb/test/src/NDBT_Table.cpp
+++ b/ndb/test/src/NDBT_Table.cpp
@@ -19,35 +19,6 @@
#include <NDBT.hpp>
class NdbOut&
-operator <<(class NdbOut& ndbout, const NDBT_Attribute & attr){
-
- NdbDictionary::Column::Type type = attr.getType();
-
- ndbout << attr.getName() << " " << type;
-
- switch(type){
- case NdbDictionary::Column::Decimal:
- ndbout << "(" << attr.getScale() << ", " << attr.getPrecision() << ")";
- break;
- default:
- break;
- }
-
- if(attr.getLength() != 1)
- ndbout << "[" << attr.getLength() << "]";
-
- if(attr.getNullable())
- ndbout << " NULL";
- else
- ndbout << " NOT NULL";
-
- if(attr.getPrimaryKey())
- ndbout << " PRIMARY KEY";
-
- return ndbout;
-}
-
-class NdbOut&
operator <<(class NdbOut& ndbout, const NDBT_Table & tab)
{
ndbout << "-- " << tab.getName() << " --" << endl;
diff --git a/ndb/test/src/NDBT_Test.cpp b/ndb/test/src/NDBT_Test.cpp
index af4e3ff3550..06eb3f4e9e2 100644
--- a/ndb/test/src/NDBT_Test.cpp
+++ b/ndb/test/src/NDBT_Test.cpp
@@ -830,7 +830,8 @@ void NDBT_TestSuite::execute(Ndb* ndb, const NdbDictionary::Table* pTab,
if(pTab2 == 0 && pDict->createTable(* pTab) != 0){
numTestsFail++;
numTestsExecuted++;
- g_err << "ERROR1: Failed to create table " << pTab->getName() << endl;
+ g_err << "ERROR1: Failed to create table " << pTab->getName()
+ << pDict->getNdbError() << endl;
tests[t]->saveTestResult(pTab, FAILED_TO_CREATE);
continue;
}