=== modified file 'storage/ndb/src/ndbapi/NdbOperationExec.cpp' --- storage/ndb/src/ndbapi/NdbOperationExec.cpp 2009-05-26 18:53:34 +0000 +++ storage/ndb/src/ndbapi/NdbOperationExec.cpp 2009-10-05 22:59:00 +0000 @@ -350,7 +350,7 @@ Uint32 NdbOperation::repack_read(Uint32 len) { Uint32 i; - Uint32 maxId = 0, check = 0; + Uint32 check = 0, prevId = 0; Uint32 save = len; Bitmask mask; NdbApiSignal *tSignal = theFirstATTRINFO; @@ -362,14 +362,16 @@ NdbOperation::repack_read(Uint32 len) { AttributeHeader tmp(* ptr++); Uint32 id = tmp.getAttributeId(); - if (id >= NDB_MAX_ATTRIBUTES_IN_TABLE) + if (((i > 0) && // No prevId for first attrId + (id <= prevId)) || + (id >= NDB_MAX_ATTRIBUTES_IN_TABLE)) { - // Dont support == fallback + // AttrIds not strictly ascending with no duplicates + // and no pseudo-columns == fallback return save; } + prevId = id; mask.set(id); - maxId = (id > maxId) ? id : maxId; - check |= (id - maxId); } Uint32 cnt = 0; @@ -382,20 +384,20 @@ NdbOperation::repack_read(Uint32 len) { AttributeHeader tmp(* ptr++); Uint32 id = tmp.getAttributeId(); - if (id >= NDB_MAX_ATTRIBUTES_IN_TABLE) + if ((id <= prevId) || + (id >= NDB_MAX_ATTRIBUTES_IN_TABLE)) { - // Dont support == fallback + // AttrIds not strictly ascending with no duplicates + // and no pseudo-columns == fallback return save; } + prevId = id; mask.set(id); - - maxId = (id > maxId) ? id : maxId; - check |= (id - maxId); } tSignal = tSignal->next(); } - const Uint32 newlen = 1 + (maxId >> 5); + const Uint32 newlen = 1 + (prevId >> 5); const bool all = cols == save; if (check == 0) { === modified file 'storage/ndb/test/include/HugoCalculator.hpp' --- storage/ndb/test/include/HugoCalculator.hpp 2009-05-26 18:53:34 +0000 +++ storage/ndb/test/include/HugoCalculator.hpp 2009-10-05 22:47:46 +0000 @@ -37,6 +37,9 @@ public: int len, Uint32* real_len) const; int verifyRowValues(NDBT_ResultRow* const pRow) const; + int verifyRecAttr(int record, int updates, const NdbRecAttr* recAttr); + int verifyColValue(int record, int attrib, int updates, + const char* valPtr, Uint32 valLen); int getIdValue(NDBT_ResultRow* const pRow) const; int getUpdatesValue(NDBT_ResultRow* const pRow) const; int isIdCol(int colId) { return m_idCol == colId; }; === modified file 'storage/ndb/test/ndbapi/testNdbApi.cpp' --- storage/ndb/test/ndbapi/testNdbApi.cpp 2009-09-22 13:04:32 +0000 +++ storage/ndb/test/ndbapi/testNdbApi.cpp 2009-10-05 22:48:08 +0000 @@ -3121,6 +3121,157 @@ int runBulkPkReads(NDBT_Context* ctx, ND return NDBT_OK; } +int runReadColumnDuplicates(NDBT_Context* ctx, NDBT_Step* step){ + + int result = NDBT_OK; + const NdbDictionary::Table* pTab = ctx->getTab(); + HugoCalculator hc(*pTab); + Uint32 numRecords = ctx->getNumRecords(); + + Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB"); + if (pNdb == NULL){ + ndbout << "pNdb == NULL" << endl; + return NDBT_FAILED; + } + if (pNdb->init()){ + ERR(pNdb->getNdbError()); + delete pNdb; + return NDBT_FAILED; + } + + HugoOperations hugoOps(*pTab); + + for (int m = 1; m < 100; m++){ + Uint32 record = (100 - m) % numRecords; + NdbConnection* pCon = pNdb->startTransaction(); + if (pCon == NULL){ + delete pNdb; + return NDBT_FAILED; + } + + NdbOperation* pOp = pCon->getNdbOperation(pTab->getName()); + if (pOp == NULL){ + pNdb->closeTransaction(pCon); + delete pNdb; + return NDBT_FAILED; + } + + if (pOp->readTuple() != 0){ + pNdb->closeTransaction(pCon); + delete pNdb; + return NDBT_FAILED; + } + + int numCols= pTab->getNoOfColumns(); + + for(int a = 0; a < numCols; a++){ + if (pTab->getColumn(a)->getPrimaryKey() == true){ + if(hugoOps.equalForAttr(pOp, a, record) != 0){ + ERR(pCon->getNdbError()); + pNdb->closeTransaction(pCon); + delete pNdb; + return NDBT_FAILED; + } + } + } + + int dupColNum = m % numCols; + int numReads = m + 1; + + NdbRecAttr* first = NULL; + ndbout << "Reading record " + << record << " Column " + << dupColNum << " " << numReads + << " times" << endl; + while (numReads--) + { + NdbRecAttr* recAttr = pOp->getValue(dupColNum); + if (recAttr == NULL) { + const NdbError err = pCon->getNdbError(); + ERR(err); + result = NDBT_FAILED; + pNdb->closeTransaction(pCon); + break; + } + first = (first == NULL) ? recAttr : first; + }; + + if (result == NDBT_FAILED) + break; + + if (pCon->execute(Commit) != 0){ + const NdbError err = pCon->getNdbError(); + ERR(err); + result = NDBT_FAILED; + pNdb->closeTransaction(pCon); + break; + } + + if (pCon->getNdbError().code != 0) + { + NdbError err = pCon->getNdbError(); + if (err.code == 880) + { + /* Tried to read too much error - this column + * is probably too large. + * Skip to next iteration + */ + ndbout << "Reading too much in one op, skipping..." << endl; + pNdb->closeTransaction(pCon); + continue; + } + ndbout << "Error at execute time : " << err.code + << ":" << err.message << endl; + pNdb->closeTransaction(pCon); + result = NDBT_FAILED; + break; + } + + /* Let's check the results */ + + + const NdbRecAttr* curr = first; + + for (int c= 0; c < (m+1); c++) + { + if (hc.verifyRecAttr(record, + 0, + curr)) + { + ndbout << "Mismatch on record " + << record << " column " + << dupColNum << " read number " + << c+1 << endl; + result = NDBT_FAILED; + break; + } + + ndbout << "/"; + + curr = curr->next(); + } + + ndbout << endl; + + pNdb->closeTransaction(pCon); + + if (result == NDBT_FAILED) + break; + + if (curr != NULL) + { + ndbout << "Error - extra RecAttr(s) found" << endl; + result = NDBT_FAILED; + break; + } + + }// m + + delete pNdb; + + return result; +} + NDBT_TESTSUITE(testNdbApi); @@ -3290,6 +3441,12 @@ TESTCASE("ApiFailReqBehaviour", STEP(testApiFailReq); FINALIZER(runClearTable); } +TESTCASE("ReadColumnDuplicates", + "Check NdbApi behaves ok when reading same column multiple times") { + INITIALIZER(runLoadTable); + STEP(runReadColumnDuplicates); + FINALIZER(runClearTable); +} NDBT_TESTSUITE_END(testNdbApi); int main(int argc, const char** argv){ === modified file 'storage/ndb/test/run-test/daily-basic-tests.txt' --- storage/ndb/test/run-test/daily-basic-tests.txt 2009-10-05 10:38:50 +0000 +++ storage/ndb/test/run-test/daily-basic-tests.txt 2009-10-05 23:07:15 +0000 @@ -1362,3 +1362,7 @@ max-time: 300 cmd: testNdbApi args: -n ApiFailReqBehaviour T1 +max-time: 300 +cmd: testNdbApi +args: -n ReadColumnDuplicates + === modified file 'storage/ndb/test/src/HugoCalculator.cpp' --- storage/ndb/test/src/HugoCalculator.cpp 2009-05-26 18:53:34 +0000 +++ storage/ndb/test/src/HugoCalculator.cpp 2009-10-05 22:48:23 +0000 @@ -304,6 +304,105 @@ HugoCalculator::verifyRowValues(NDBT_Res return result; } + +int +HugoCalculator::verifyRecAttr(int record, + int updates, + const NdbRecAttr* recAttr) +{ + const char* valPtr = NULL; + int attrib = recAttr->getColumn()->getAttrId(); + Uint32 valLen = recAttr->get_size_in_bytes(); + if (!recAttr->isNULL()) + valPtr= (const char*) recAttr->aRef(); + + return verifyColValue(record, + attrib, + updates, + valPtr, + valLen); +} + +int +HugoCalculator::verifyColValue(int record, + int attrib, + int updates, + const char* valPtr, + Uint32 valLen) +{ + int result = 0; + + if (attrib == m_updatesCol) + { + int val= *((const int*) valPtr); + if (val != updates) + { + g_err << "|- Updates column (" << attrib << ")" << endl; + g_err << "|- Expected " << updates << " but found " << val << endl; + result = -1; + } + } + else if (attrib == m_idCol) + { + int val= *((const int*) valPtr); + if (val != record) + { + g_err << "|- Identity column (" << attrib << ")" << endl; + g_err << "|- Expected " << record << " but found " << val << endl; + result = -1; + } + } + else + { + /* 'Normal' data column */ + const NdbDictionary::Column* attr = m_tab.getColumn(attrib); + Uint32 len = attr->getSizeInBytes(), real_len; + char buf[NDB_MAX_TUPLE_SIZE]; + const char* res = calcValue(record, attrib, updates, buf, len, &real_len); + if (res == NULL){ + if (valPtr != NULL){ + g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl; + g_err << "|- Column length is " << valLen << " bytes" << endl; + g_err << "|- Column data follows :" << endl; + for (Uint32 j = 0; j < valLen; j ++) + { + g_err << j << ":" << hex << (Uint32)(Uint8)valPtr[j] << endl; + } + result = -1; + } + } else{ + if (real_len != valLen) + { + g_err << "|- Invalid data found in attribute " << attrib << ": \"" + << "Length of expected=" << real_len << endl + << "Length of passed=" + << valLen << endl; + result= -1; + } + else if (memcmp(res, valPtr, real_len) != 0) + { + g_err << "|- Expected data mismatch on column " + << attr->getName() << " length " << real_len + << " bytes " << endl; + g_err << "|- Bytewise comparison follows :" << endl; + for (Uint32 j = 0; j < real_len; j++) + { + g_err << j << ":" << hex << (Uint32)(Uint8)buf[j] << "[" << hex << (Uint32)(Uint8)valPtr[j] << "]"; + if (buf[j] != valPtr[j]) + { + g_err << "==>Match failed!"; + } + g_err << endl; + } + g_err << endl; + result = -1; + } + } + } + + return result; +} + int HugoCalculator::getIdValue(NDBT_ResultRow* const pRow) const { return pRow->attributeStore(m_idCol)->u_32_value();