Skip to content

Commit 3bcc9ab

Browse files
committed
Bug#36989337 NDB Varbinary like columns fail with OO_SETVALUE
Problem: When writing values to varbinary columns using NdbRecord and OO_SETVALUE in the NDBAPI the write will fail with error 829 "Corrupt data received for insert/update". This since the value when sent to NDB cluster will be wrapped within a too small attribute which is checked in DBTUP in data nodes. The bug affects columns that uses the NDB Varchar, Varbinary, Longvarchar, and, Longvarbinary types. Fix: Make sure that the attribute will have room for both the bare value and the extra length bytes. Test: A new test is added to demonstrate failure: testNdbApi -n SetVarbinaryWithSetValue WIDE_COL Change-Id: I1dc2885457c59e7f19ea71637240504d57fd69a1
1 parent 130e8fc commit 3bcc9ab

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

storage/ndb/src/ndbapi/NdbOperationExec.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,7 @@ int NdbOperation::buildSignalsNdbRecord(Uint32 aTC_ConnectPtr, Uint64 aTransId,
12071207
setErrorCodeAbort(4209);
12081208
return -1;
12091209
}
1210+
length += lengthInfoBytes;
12101211
}
12111212
}
12121213

storage/ndb/test/ndbapi/testNdbApi.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7456,6 +7456,89 @@ int testSlowConnectEnable(NDBT_Context *ctx, NDBT_Step *step) {
74567456
return result;
74577457
}
74587458

7459+
int testSetVarbinaryWithSetValue(NDBT_Context *ctx, NDBT_Step *step) {
7460+
// Test for bug#36989337 NDB Varbinary like columns fail with OO_SETVALUE
7461+
Ndb *pNdb = GETNDB(step);
7462+
const NdbDictionary::Table *pTab = ctx->getTab();
7463+
7464+
// Run as a 'WIDE_2COL' testcase - do nothing for other tables
7465+
if (strcmp(pTab->getName(), "WIDE_2COL") != 0) return NDBT_SKIPPED;
7466+
7467+
NdbTransaction *pTrans;
7468+
NdbOperation *pOp;
7469+
NdbDictionary::Dictionary *dict = pNdb->getDictionary();
7470+
char readbuf[NDB_MAX_TUPLE_SIZE_IN_WORDS << 2];
7471+
7472+
// Insert the test tuple ['xyz','abc']
7473+
CHECKE(pTrans = pNdb->startTransaction(), (*pNdb));
7474+
CHECKE(pOp = pTrans->getNdbOperation(pTab->getName()), (*pTrans));
7475+
CHECKE(pOp->insertTuple() == 0, (*pOp));
7476+
CHECKE(pOp->setValue("KEY", "\003\000xyz") == 0, (*pOp));
7477+
CHECKE(pOp->setValue("ATTR", "\003\000abc") == 0, (*pOp));
7478+
CHECKE(pTrans->execute(Commit) == 0, (*pTrans));
7479+
pTrans->close();
7480+
7481+
// Read and verify ATTR value
7482+
CHECKE(pTrans = pNdb->startTransaction(), (*pNdb));
7483+
CHECKE(pOp = pTrans->getNdbOperation(pTab->getName()), (*pTrans));
7484+
CHECKE(pOp->readTuple() == 0, (*pOp));
7485+
CHECKE(pOp->equal("KEY", "\003\000xyz") == 0, (*pOp));
7486+
CHECKE(pOp->getValue("ATTR", readbuf) != nullptr, (*pOp));
7487+
CHECKE(pTrans->execute(NoCommit) == 0, (*pTrans));
7488+
CHECK(memcmp(readbuf, "\003\000abc", 5) == 0);
7489+
CHECKE(pTrans->getNdbError().code == 0, (*pTrans));
7490+
CHECKE(pTrans->execute(Rollback) == 0, (*pTrans));
7491+
pTrans->close();
7492+
7493+
// Insert the test tuple ['XYZ','ABC'] using NdbRecord and OO_SETVALUE
7494+
// Whole key must be in record for writeTuple
7495+
const NdbRecord *tabRec;
7496+
NdbDictionary::RecordSpecification spec[1];
7497+
spec->column =
7498+
pTab->getColumn("KEY"); // Whole key must be in record for writeTuple
7499+
spec->offset = 0;
7500+
spec->nullbit_byte_offset = 0;
7501+
spec->nullbit_bit_in_byte = 0;
7502+
spec->column_flags = 0;
7503+
tabRec = dict->createRecord(
7504+
pTab, spec, 1, sizeof(NdbDictionary::RecordSpecification),
7505+
NdbDictionary::RecMysqldBitfield | NdbDictionary::RecPerColumnFlags);
7506+
CHECKE(tabRec != nullptr, (*dict));
7507+
7508+
const Uint32 rowLen = NDB_MAX_TUPLE_SIZE_IN_WORDS << 2;
7509+
char rowBuf[rowLen] = "\003\000XYZ";
7510+
unsigned char mask[1] = {(1 << 0) | (1 << 1)};
7511+
7512+
NdbOperation::SetValueSpec setValueSpec[1];
7513+
setValueSpec[0].column = pTab->getColumn("ATTR");
7514+
setValueSpec[0].value = "\003\000ABC";
7515+
7516+
NdbOperation::OperationOptions opts;
7517+
opts.optionsPresent = NdbOperation::OperationOptions::OO_SETVALUE;
7518+
opts.extraSetValues = setValueSpec;
7519+
opts.numExtraSetValues = 1;
7520+
7521+
CHECKE(pTrans = pNdb->startTransaction(), (*pNdb));
7522+
auto writeOp = pTrans->writeTuple(tabRec, rowBuf, tabRec, NULL, mask, &opts,
7523+
sizeof(opts));
7524+
CHECKE(writeOp != nullptr, (*pTrans));
7525+
CHECKE(pTrans->execute(Commit) == 0, (*pTrans)); // Failed by bug#xyz
7526+
pTrans->close();
7527+
7528+
// Read and verify ATTR value
7529+
CHECKE(pTrans = pNdb->startTransaction(), (*pNdb));
7530+
CHECKE(pOp = pTrans->getNdbOperation(pTab->getName()), (*pTrans));
7531+
CHECKE(pOp->readTuple() == 0, (*pOp));
7532+
CHECKE(pOp->equal("KEY", "\003\000XYZ") == 0, (*pOp));
7533+
CHECKE(pOp->getValue("ATTR", readbuf) != nullptr, (*pOp));
7534+
CHECKE(pTrans->execute(NoCommit) == 0, (*pTrans));
7535+
CHECK(memcmp(readbuf, "\003\000ABC", 5) == 0);
7536+
CHECKE(pTrans->execute(Rollback) == 0, (*pTrans));
7537+
pTrans->close();
7538+
7539+
return NDBT_OK;
7540+
}
7541+
74597542
NDBT_TESTSUITE(testNdbApi);
74607543
TESTCASE("MaxNdb", "Create Ndb objects until no more can be created\n") {
74617544
INITIALIZER(runTestMaxNdb);
@@ -7835,6 +7918,9 @@ TESTCASE("DatabaseAndSchemaName",
78357918
TESTCASE("TestSlowConnectEnable", "Test behaviour with slow connection enale") {
78367919
STEP(testSlowConnectEnable);
78377920
}
7921+
TESTCASE("SetVarbinaryWithSetValue", "Check OO_SETVALUE works with Varbinary") {
7922+
STEP(testSetVarbinaryWithSetValue);
7923+
}
78387924

78397925
NDBT_TESTSUITE_END(testNdbApi)
78407926

storage/ndb/test/run-test/daily-devel--07-tests.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,7 @@ cmd: testDict
341341
args: -n IndexStatNodeFailures T1
342342
max-time: 360
343343

344+
cmd: testNdbApi
345+
args: -n SetVarbinaryWithSetValue WIDE_COL
346+
max-time: 180
347+

0 commit comments

Comments
 (0)