8144 lines
219 KiB
C++
8144 lines
219 KiB
C++
/*
|
|
Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <NDBT.hpp>
|
|
#include <NDBT_Test.hpp>
|
|
#include <HugoTransactions.hpp>
|
|
#include <UtilTransactions.hpp>
|
|
#include <NdbRestarter.hpp>
|
|
#include <NdbRestarts.hpp>
|
|
#include <Vector.hpp>
|
|
#include <random.h>
|
|
#include <NdbTick.h>
|
|
#include <my_sys.h>
|
|
#include "../../src/ndbapi/SignalSender.hpp"
|
|
#include <GlobalSignalNumbers.h>
|
|
|
|
#define MAX_NDB_OBJECTS 32678
|
|
|
|
#define CHECK(b) if (!(b)) { \
|
|
g_err.println("ERR: failed on line %u", __LINE__); \
|
|
return -1; }
|
|
|
|
#define CHECKE(b,obj) if (!(b)) { \
|
|
g_err.println("ERR:failed on line %u with err %u %s", \
|
|
__LINE__, \
|
|
obj.getNdbError().code, \
|
|
obj.getNdbError().message); \
|
|
return -1; }
|
|
|
|
static const char* ApiFailTestRun = "ApiFailTestRun";
|
|
static const char* ApiFailTestComplete = "ApiFailTestComplete";
|
|
static const char* ApiFailTestsRunning = "ApiFailTestsRunning";
|
|
static const char* ApiFailNumberPkSteps = "ApiFailNumberPkSteps";
|
|
static const int MAX_STEPS = 10;
|
|
static Ndb_cluster_connection* otherConnection = NULL;
|
|
static Ndb* stepNdbs[MAX_STEPS];
|
|
|
|
|
|
int runTestMaxNdb(NDBT_Context* ctx, NDBT_Step* step){
|
|
Uint32 loops = ctx->getNumLoops();
|
|
Uint32 l = 0;
|
|
int oldi = 0;
|
|
int result = NDBT_OK;
|
|
|
|
while (l < loops && result == NDBT_OK){
|
|
ndbout_c("loop %d", l + 1);
|
|
int errors = 0;
|
|
|
|
Vector<Ndb*> ndbVector;
|
|
int i = 0;
|
|
int init = 0;
|
|
do {
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
errors++;
|
|
continue;
|
|
|
|
}
|
|
i++;
|
|
|
|
ndbVector.push_back(pNdb);
|
|
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
init++;
|
|
|
|
} while (errors == 0);
|
|
|
|
ndbout << i << " ndb objects created" << endl;
|
|
|
|
if (l > 0 && i != oldi && init != MAX_NDB_OBJECTS){
|
|
ndbout << l << ": not as manyNdb objects created" << endl
|
|
<< i << " != " << oldi << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
oldi = i;
|
|
|
|
|
|
for(unsigned j = 0; j < ndbVector.size(); j++){
|
|
delete ndbVector[j];
|
|
if(((j+1) % 250) == 0){
|
|
ndbout << "Deleted " << (Uint64) j << " ndb objects " << endl;
|
|
}
|
|
}
|
|
ndbVector.clear();
|
|
|
|
l++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int runTestMaxTransaction(NDBT_Context* ctx, NDBT_Step* step){
|
|
Uint32 loops = ctx->getNumLoops();
|
|
Uint32 l = 0;
|
|
int oldi = 0;
|
|
int result = NDBT_OK;
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(2048)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
if (pTab == 0) abort();
|
|
|
|
while (l < loops && result == NDBT_OK){
|
|
int errors = 0;
|
|
int maxErrors = 5;
|
|
|
|
Vector<NdbConnection*> conVector;
|
|
|
|
|
|
int i = 0;
|
|
do {
|
|
|
|
NdbConnection* pCon;
|
|
|
|
int type = i%2;
|
|
switch (type){
|
|
case 0:
|
|
pCon = pNdb->startTransaction();
|
|
break;
|
|
case 1:
|
|
{
|
|
BaseString key;
|
|
key.appfmt("DATA-%d", i);
|
|
ndbout_c("%s", key.c_str());
|
|
pCon = pNdb->startTransaction(pTab,
|
|
key.c_str(),
|
|
key.length());
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if (pCon == NULL){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
conVector.push_back(pCon);
|
|
|
|
i++;
|
|
} while (errors < maxErrors);
|
|
|
|
ndbout << i << " connections created" << endl;
|
|
|
|
if (l > 0 && i != oldi){
|
|
ndbout << l << ": not as many transactions created" << endl
|
|
<< i << " != " << oldi << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
oldi = i;
|
|
|
|
|
|
for(unsigned j = 0; j < conVector.size(); j++){
|
|
pNdb->closeTransaction(conVector[j]);
|
|
}
|
|
conVector.clear();
|
|
l++;
|
|
|
|
}
|
|
|
|
// BONUS Test closeTransaction with null trans
|
|
pNdb->closeTransaction(NULL);
|
|
|
|
delete pNdb;
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
int runTestMaxOperations(NDBT_Context* ctx, NDBT_Step* step){
|
|
Uint32 l = 1;
|
|
int result = NDBT_OK;
|
|
int maxOpsLimit = 1;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(2048)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
ndbout << "pNdb.init() failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
/**
|
|
* Finding max operations is done in two phases.
|
|
* First double transaction size until a transaction fail.
|
|
* Then bisect between last working transaction size and failing transaction
|
|
* size to find biggest working transaction.
|
|
*/
|
|
int lower_max_ops = 0;
|
|
int next_max_ops = 1000;
|
|
int upper_max_ops = -1;
|
|
bool endTest;
|
|
while (upper_max_ops == -1 ||
|
|
(upper_max_ops - lower_max_ops) >= 1000)
|
|
{
|
|
int errors = 0;
|
|
const int maxErrors = 5;
|
|
|
|
endTest = false;
|
|
|
|
if (hugoOps.startTransaction(pNdb) != NDBT_OK){
|
|
delete pNdb;
|
|
ndbout << "startTransaction failed, line: " << __LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (int i = 1; !endTest && i <= next_max_ops; i++)
|
|
{
|
|
const int rowNo = (i % 256);
|
|
if(hugoOps.pkReadRecord(pNdb, rowNo, 1) != NDBT_OK){
|
|
errors++;
|
|
ndbout << "ReadRecord failed at line: " << __LINE__ << ", row: " << rowNo << endl;
|
|
if (errors >= maxErrors){
|
|
result = NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
// Avoid Transporter overload by executing after max 1000 ops.
|
|
int execResult = 0;
|
|
if (i == next_max_ops)
|
|
{
|
|
execResult = hugoOps.execute_Commit(pNdb); //Commit after last op
|
|
}
|
|
else if ((i%1000) == 0)
|
|
{
|
|
execResult = hugoOps.execute_NoCommit(pNdb);
|
|
}
|
|
else
|
|
{
|
|
require(i < next_max_ops);
|
|
continue;
|
|
}
|
|
|
|
switch(execResult){
|
|
case NDBT_OK:
|
|
break;
|
|
|
|
default:
|
|
result = NDBT_FAILED;
|
|
// Fall through - to '233' which also terminate test, but not 'FAILED'
|
|
case 233: // Out of operation records in transaction coordinator
|
|
case 1217: // Out of operation records in local data manager (increase MaxNoOfLocalOperations)
|
|
case 261: //Increased beyond MaxDMLOperationsPerTransaction or MaxNoOfConcurrentOperations
|
|
// OK - end test
|
|
endTest = true;
|
|
break;
|
|
}
|
|
}
|
|
ndbout << next_max_ops
|
|
<< " operations used"
|
|
<< (endTest ? " failed" : " succeeded")
|
|
<< endl;
|
|
if (upper_max_ops == -1 && !endTest)
|
|
{
|
|
lower_max_ops = next_max_ops;
|
|
if (next_max_ops == INT_MAX)
|
|
{
|
|
upper_max_ops = next_max_ops;
|
|
}
|
|
else if (next_max_ops <= INT_MAX / 2)
|
|
{
|
|
next_max_ops = next_max_ops * 2;
|
|
}
|
|
else
|
|
{
|
|
next_max_ops = INT_MAX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!endTest)
|
|
{
|
|
lower_max_ops = next_max_ops;
|
|
}
|
|
else
|
|
{
|
|
upper_max_ops = next_max_ops;
|
|
}
|
|
next_max_ops = (lower_max_ops + upper_max_ops) / 2;
|
|
}
|
|
|
|
hugoOps.closeTransaction(pNdb);
|
|
|
|
l++;
|
|
}
|
|
maxOpsLimit = lower_max_ops;
|
|
ndbout << "Found max operations limit " << maxOpsLimit << endl;
|
|
|
|
/**
|
|
* After the peak usage of NdbOperations comes a cool down periode
|
|
* with lower usage. Check that the NdbOperations free list manager
|
|
* will gradually reduce number of free NdbOperations kept for
|
|
* later reuse.
|
|
*/
|
|
Uint32 hiFreeOperations = 0;
|
|
Uint32 freeOperations = 0;
|
|
{
|
|
Ndb::Free_list_usage usage_stat;
|
|
usage_stat.m_name= NULL;
|
|
while (pNdb->get_free_list_usage(&usage_stat))
|
|
{
|
|
if (strcmp(usage_stat.m_name, "NdbOperation") == 0)
|
|
{
|
|
hiFreeOperations = usage_stat.m_free;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
maxOpsLimit = 100;
|
|
Uint32 coolDownLoops = 25;
|
|
while (coolDownLoops-- > 0){
|
|
int errors = 0;
|
|
const int maxErrors = 5;
|
|
|
|
if (hugoOps.startTransaction(pNdb) != NDBT_OK){
|
|
delete pNdb;
|
|
ndbout << "startTransaction failed, line: " << __LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (int rowNo = 0; rowNo < 100; rowNo++)
|
|
{
|
|
if(hugoOps.pkReadRecord(pNdb, rowNo, 1) != NDBT_OK){
|
|
errors++;
|
|
ndbout << "ReadRecord failed at line: " << __LINE__ << ", row: " << rowNo << endl;
|
|
if (errors >= maxErrors){
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const int execResult = hugoOps.execute_Commit(pNdb);
|
|
if (execResult != NDBT_OK)
|
|
{
|
|
ndbout << "execute failed at line: " << __LINE__
|
|
<< ", with execResult: " << execResult << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
hugoOps.closeTransaction(pNdb);
|
|
|
|
{
|
|
Ndb::Free_list_usage usage_stat;
|
|
usage_stat.m_name= NULL;
|
|
while (pNdb->get_free_list_usage(&usage_stat))
|
|
{
|
|
if (strcmp(usage_stat.m_name, "NdbOperation") == 0)
|
|
{
|
|
freeOperations = usage_stat.m_free;
|
|
ndbout << usage_stat.m_name << ", free: " << usage_stat.m_free
|
|
<< endl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} //while (coolDownLoops...
|
|
|
|
/**
|
|
* It is a pass criteria that cool down periode
|
|
* reduced the number of free NdbOperations kept.
|
|
*/
|
|
if (freeOperations >= hiFreeOperations)
|
|
{
|
|
ndbout << "Cool down periode didn't shrink NdbOperation free-list" << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
if (result != NDBT_OK)
|
|
ndbout << "Test case failed with result: " << result << endl;
|
|
|
|
delete pNdb;
|
|
return result;
|
|
}
|
|
|
|
int runTestGetValue(NDBT_Context* ctx, NDBT_Step* step){
|
|
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(2048)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
for (int m = 1; m < 100; m++){
|
|
int errors = 0;
|
|
int maxErrors = 5;
|
|
|
|
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;
|
|
}
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
int maxLimit = 1000*m;
|
|
do {
|
|
|
|
if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0)
|
|
result = NDBT_FAILED;
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
i++;
|
|
|
|
} while (errors < maxErrors && i < maxLimit);
|
|
|
|
ndbout << i << " getValues called" << endl;
|
|
|
|
|
|
if (pCon->execute(Commit) != 0){
|
|
const NdbError err = pCon->getNdbError();
|
|
switch(err.code){
|
|
case 880: // TUP - Read too much
|
|
case 823: // TUP - Too much AI
|
|
case 4257: // NDBAPI - Too much AI
|
|
case 4002: // NDBAPI - send problem
|
|
// OK errors
|
|
NDB_ERR(pCon->getNdbError());
|
|
break;
|
|
default:
|
|
NDB_ERR(pCon->getNdbError());
|
|
ndbout << "Illegal error" << endl;
|
|
result= NDBT_FAILED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
}// m
|
|
|
|
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
int runTestEqual(NDBT_Context* ctx, NDBT_Step* step){
|
|
Uint32 loops = ctx->getNumLoops();
|
|
Uint32 l = 0;
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(2048)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
while (l < loops){
|
|
for(int m = 1; m < 10; m++){
|
|
int errors = 0;
|
|
int maxErrors = 5;
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
ndbout << "Could not start transaction" << endl;
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->readTuple() != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int i = 0;
|
|
int maxLimit = 1000*m;
|
|
do {
|
|
|
|
if ((l%2)!=0){
|
|
// Forward
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0)
|
|
result = NDBT_FAILED;
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Backward
|
|
for(int a = pTab->getNoOfColumns()-1; a>=0; a--){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0)
|
|
result = NDBT_FAILED;
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
i++;
|
|
|
|
} while (errors < maxErrors && i < maxLimit);
|
|
|
|
if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
if (err.code == 4225) {
|
|
return NDBT_OK;
|
|
} else {
|
|
return NDBT_FAILED;
|
|
}//if
|
|
}
|
|
|
|
ndbout << i << " equal called" << endl;
|
|
|
|
|
|
int check = pCon->execute(Commit);
|
|
if (check != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
}// m
|
|
l++;
|
|
|
|
}// l
|
|
|
|
delete pNdb;
|
|
return result;
|
|
}
|
|
|
|
int runTestDeleteNdb(NDBT_Context* ctx, NDBT_Step* step){
|
|
Uint32 loops = ctx->getNumLoops();
|
|
Uint32 l = 0;
|
|
int result = NDBT_OK;
|
|
NdbRestarts restarts;
|
|
Vector<Ndb*> ndbVector;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
HugoTransactions hugoTrans(*pTab);
|
|
int records = ctx->getNumRecords();
|
|
|
|
while (l < loops && result == NDBT_OK){
|
|
|
|
// Create 5 ndb objects
|
|
for( int i = 0; i < 5; i++){
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
result = NDBT_FAILED;
|
|
goto end_test;
|
|
}
|
|
ndbVector.push_back(pNdb);
|
|
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
result = NDBT_FAILED;
|
|
goto end_test;
|
|
}
|
|
if (pNdb->waitUntilReady() != 0){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
result = NDBT_FAILED;
|
|
goto end_test;
|
|
}
|
|
if (hugoTrans.pkReadRecords(pNdb, records) != 0){
|
|
result = NDBT_FAILED;
|
|
goto end_test;
|
|
}
|
|
}
|
|
|
|
if ((l % 2) == 0){
|
|
// Restart random node
|
|
ndbout << "Restart random node " << endl;
|
|
if(restarts.executeRestart(ctx, "RestartRandomNodeAbort", 120) != 0){
|
|
g_err << "Failed to executeRestart(RestartRandomNode)"<<endl;
|
|
result = NDBT_FAILED;
|
|
goto end_test;
|
|
}
|
|
} else {
|
|
// Restart all nodes
|
|
ndbout << "Restart all nodes " << endl;
|
|
if(restarts.executeRestart(ctx, "RestartAllNodesAbort", 120) != 0){
|
|
g_err << "Failed to executeRestart(RestartAllNodes)"<<endl;
|
|
result = NDBT_FAILED;
|
|
goto end_test;
|
|
}
|
|
}
|
|
|
|
// Delete the ndb objects
|
|
for(unsigned j = 0; j < ndbVector.size(); j++)
|
|
delete ndbVector[j];
|
|
ndbVector.clear();
|
|
l++;
|
|
}
|
|
|
|
|
|
end_test:
|
|
|
|
for(unsigned i = 0; i < ndbVector.size(); i++)
|
|
delete ndbVector[i];
|
|
ndbVector.clear();
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
|
|
int records = ctx->getNumRecords();
|
|
|
|
UtilTransactions utilTrans(*ctx->getTab());
|
|
if (utilTrans.clearTable2(GETNDB(step), records) != 0){
|
|
return NDBT_FAILED;
|
|
}
|
|
return NDBT_OK;
|
|
}
|
|
int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
|
|
|
|
int records = ctx->getNumRecords();
|
|
HugoTransactions hugoTrans(*ctx->getTab());
|
|
if (hugoTrans.loadTable(GETNDB(step), records) != 0){
|
|
return NDBT_FAILED;
|
|
}
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runTestWaitUntilReady(NDBT_Context* ctx, NDBT_Step* step){
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
|
|
// Forget about calling pNdb->init();
|
|
|
|
if (pNdb->waitUntilReady() == 0){
|
|
ndbout << "waitUntilReady returned OK" << endl;
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
const NdbError err = pNdb->getNdbError();
|
|
delete pNdb;
|
|
|
|
NDB_ERR(err);
|
|
if (err.code != 4256)
|
|
return NDBT_FAILED;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runGetNdbOperationNoTab(NDBT_Context* ctx, NDBT_Step* step){
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Call getNdbOperation on an unknown table
|
|
NdbOperation* pOp = pCon->getNdbOperation("HUPP76");
|
|
if (pOp == NULL){
|
|
NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
delete pNdb;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runBadColNameHandling(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
const int CASES= 5;
|
|
int i;
|
|
|
|
for (i= 0; i < CASES; i++)
|
|
{
|
|
ndbout << "Case " << i << endl;
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* Cases 0-3 use PK ops, 4 + use scans */
|
|
NdbOperation* pOp = (i < 4 ? pCon->getNdbOperation(pTab->getName()):
|
|
pCon->getNdbScanOperation(pTab->getName()));
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
bool failed= false;
|
|
int expectedError= 0;
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
switch(i) {
|
|
case 0:
|
|
if (pOp->readTuple() != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// getValue should fail, we check that we get correct errors
|
|
// in expected places.
|
|
expectedError= 4004;
|
|
failed= (pOp->getValue("MOST_IMPROBABLE2") == NULL);
|
|
break;
|
|
|
|
case 1:
|
|
if (pOp->readTuple() != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// equal should fail, we check that we get correct errors
|
|
// in expected places.
|
|
expectedError= 4004;
|
|
failed= (pOp->equal("MOST_IMPROBABLE2", 0) != 0);
|
|
break;
|
|
|
|
case 2:
|
|
if (pOp->writeTuple() != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// set equality on pk columns
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// setValue should fail, we check that we get correct errors
|
|
// in expected places.
|
|
expectedError= 4004;
|
|
failed= (pOp->setValue("MOST_IMPROBABLE2", 0) != 0);
|
|
break;
|
|
|
|
case 3:
|
|
if (pOp->readTuple() != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// getBlobHandle should fail, we check that we get correct errors
|
|
// in expected places.
|
|
expectedError= 4004;
|
|
failed= (pOp->getBlobHandle("MOST_IMPROBABLE2") == NULL);
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
NdbScanOperation* sop= (NdbScanOperation*) pOp;
|
|
if (sop->readTuples() != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// getBlobHandle should fail, we check that we get correct errors
|
|
// in expected places.
|
|
expectedError= 4004;
|
|
ndbout << "About to call getBlobHandle" << endl;
|
|
failed= (sop->getBlobHandle("MOST_IMPROBABLE2") == NULL);
|
|
|
|
sop->close();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
const NdbError opErr= pOp->getNdbError();
|
|
const NdbError transErr = pCon->getNdbError();
|
|
NDB_ERR(opErr);
|
|
NDB_ERR(transErr);
|
|
if (opErr.code != transErr.code) {
|
|
ndbout << "Error reporting mismatch, expected "
|
|
<< expectedError << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
if (opErr.code != expectedError){
|
|
ndbout << "No or bad error detected, expected "
|
|
<< expectedError << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
} else {
|
|
ndbout << "Case " << i << " did not fail" << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
if (result == NDBT_FAILED)
|
|
break;
|
|
} // for
|
|
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
int runMissingOperation(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Forget about calling pOp->insertTuple();
|
|
|
|
// Call getValue should not work
|
|
if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0){
|
|
ndbout << "hupp" << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
} else {
|
|
ndbout << "hupp2" << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
int runGetValueInUpdate(NDBT_Context* ctx, NDBT_Step* step){
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->updateTuple() != 0){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Call getValue should not work
|
|
if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
|
|
// It didn't work
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
} else {
|
|
// It worked, not good!
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int check = pCon->execute(Commit);
|
|
if (check != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runUpdateWithoutValues(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->updateTuple() != 0){
|
|
pNdb->closeTransaction(pCon);
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dont' call any setValues
|
|
|
|
// Execute should work
|
|
int check = pCon->execute(Commit);
|
|
if (check == 0){
|
|
ndbout << "execute worked" << endl;
|
|
} else {
|
|
NDB_ERR(pCon->getNdbError());
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
int runUpdateWithoutKeys(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->updateTuple() != 0){
|
|
pNdb->closeTransaction(pCon);
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Dont' call any equal or setValues
|
|
|
|
// Execute should not work
|
|
int check = pCon->execute(Commit);
|
|
if (check == 0){
|
|
ndbout << "execute worked" << endl;
|
|
result = NDBT_FAILED;
|
|
} else {
|
|
NDB_ERR(pCon->getNdbError());
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int runReadWithoutGetValue(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
Ndb* pNdb = GETNDB(step);
|
|
Uint32 lm;
|
|
|
|
for(Uint32 cm= 0; cm < 2; cm++)
|
|
{
|
|
for(lm= 0; lm <= NdbOperation::LM_CommittedRead; lm++)
|
|
{
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->readTuple((NdbOperation::LockMode)lm) != 0){
|
|
pNdb->closeTransaction(pCon);
|
|
NDB_ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dont' call any getValues
|
|
|
|
// Execute should work
|
|
int check = pCon->execute(cm == 0 ? NoCommit : Commit);
|
|
if (check == 0){
|
|
ndbout << "execute worked" << endl;
|
|
} else {
|
|
NDB_ERR(pCon->getNdbError());
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Now test scans
|
|
*/
|
|
for(lm= 0; lm <= NdbOperation::LM_CommittedRead; lm++)
|
|
{
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if ((pOp->readTuples((NdbOperation::LockMode)lm)) != 0){
|
|
pNdb->closeTransaction(pCon);
|
|
NDB_ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
|
|
// Dont' call any getValues
|
|
|
|
// Execute should work
|
|
int check = pCon->execute(NoCommit);
|
|
if (check == 0){
|
|
ndbout << "execute worked" << endl;
|
|
} else {
|
|
NDB_ERR(pCon->getNdbError());
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
int res;
|
|
while((res = pOp->nextResult()) == 0);
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
if(res != 1)
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int runCheckGetNdbErrorOperation(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(2048)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
ndbout << "Could not start transaction" << endl;
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Dont call readTuple here
|
|
// That's the error!
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, 1) != 0){
|
|
// An error has occurred, check that
|
|
// it's possible to get the NdbErrorOperation
|
|
const NdbError err = pCon->getNdbError();
|
|
NDB_ERR(err);
|
|
if (err.code == 0)
|
|
result = NDBT_FAILED;
|
|
|
|
NdbOperation* pOp2 = pCon->getNdbErrorOperation();
|
|
if (pOp2 == NULL)
|
|
result = NDBT_FAILED;
|
|
else {
|
|
const NdbError err2 = pOp2->getNdbError();
|
|
NDB_ERR(err2);
|
|
if (err.code == 0)
|
|
result = NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
delete pNdb;
|
|
return result;
|
|
}
|
|
|
|
#define C2(x) { int _x= (x); if(_x == 0){ ndbout << "line: " << __LINE__ << endl; return NDBT_FAILED;} }
|
|
|
|
int runBug_11133(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
Ndb* pNdb = GETNDB(step);
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkInsertRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkWriteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkWriteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkInsertRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkWriteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkWriteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkInsertRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkReadRecord(pNdb, 0, 1, NdbOperation::LM_Exclusive) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkWriteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkWriteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
|
|
Ndb ndb2(&ctx->m_cluster_connection, "TEST_DB");
|
|
C2(ndb2.init() == 0);
|
|
C2(ndb2.waitUntilReady() == 0);
|
|
HugoOperations hugoOps2(*pTab);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkInsertRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps2.startTransaction(&ndb2) == 0);
|
|
C2(hugoOps2.pkWritePartialRecord(&ndb2, 0) == 0);
|
|
C2(hugoOps2.execute_async(&ndb2, NdbTransaction::NoCommit) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps2.wait_async(&ndb2) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
C2(hugoOps2.closeTransaction(&ndb2) == 0);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps2.startTransaction(&ndb2) == 0);
|
|
C2(hugoOps2.pkWriteRecord(&ndb2, 0, 1) == 0);
|
|
C2(hugoOps2.execute_async(&ndb2, NdbTransaction::NoCommit) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps2.wait_async(&ndb2) == 0);
|
|
C2(hugoOps2.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
C2(hugoOps2.closeTransaction(&ndb2) == 0);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkUpdateRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps2.startTransaction(&ndb2) == 0);
|
|
C2(hugoOps2.pkWritePartialRecord(&ndb2, 0) == 0);
|
|
C2(hugoOps2.execute_async(&ndb2, NdbTransaction::NoCommit) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps2.wait_async(&ndb2) == 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
C2(hugoOps2.closeTransaction(&ndb2) == 0);
|
|
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkDeleteRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_NoCommit(pNdb) == 0);
|
|
C2(hugoOps2.startTransaction(&ndb2) == 0);
|
|
C2(hugoOps2.pkWritePartialRecord(&ndb2, 0) == 0);
|
|
C2(hugoOps2.execute_async(&ndb2, NdbTransaction::NoCommit) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb) == 0);
|
|
C2(hugoOps2.wait_async(&ndb2) != 0);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
C2(hugoOps2.closeTransaction(&ndb2) == 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int runBug_WritePartialIgnoreError(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
Ndb* pNdb = GETNDB(step);
|
|
C2(hugoOps.startTransaction(pNdb) == 0);
|
|
C2(hugoOps.pkWritePartialRecord(pNdb, 0, 1) == 0);
|
|
C2(hugoOps.execute_Commit(pNdb, AO_IgnoreError) == 839);
|
|
C2(hugoOps.closeTransaction(pNdb) == 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int runScan_4006(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const Uint32 max= 5;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(max)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
Uint32 i;
|
|
Vector<NdbScanOperation*> scans;
|
|
for(i = 0; i<10*max; i++)
|
|
{
|
|
NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->readTuples() != 0){
|
|
pNdb->closeTransaction(pCon);
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
scans.push_back(pOp);
|
|
}
|
|
|
|
// Dont' call any equal or setValues
|
|
|
|
// Execute should not work
|
|
int check = pCon->execute(NoCommit);
|
|
if (check == 0){
|
|
ndbout << "execute worked" << endl;
|
|
} else {
|
|
NDB_ERR(pCon->getNdbError());
|
|
}
|
|
|
|
for(i= 0; i<scans.size(); i++)
|
|
{
|
|
NdbScanOperation* pOp= scans[i];
|
|
while((check= pOp->nextResult()) == 0);
|
|
if(check != 1)
|
|
{
|
|
NDB_ERR(pOp->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
Vector<NdbConnection*> cons;
|
|
for(i= 0; i<10*max; i++)
|
|
{
|
|
pCon= pNdb->startTransaction();
|
|
if(pCon)
|
|
cons.push_back(pCon);
|
|
else
|
|
break;
|
|
}
|
|
|
|
for(i= 0; i<cons.size(); i++)
|
|
{
|
|
cons[i]->close();
|
|
}
|
|
|
|
if(cons.size() != max)
|
|
{
|
|
result= NDBT_FAILED;
|
|
}
|
|
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
char pkIdxName[255];
|
|
|
|
int createPkIndex(NDBT_Context* ctx, NDBT_Step* step){
|
|
bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
|
|
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
bool logged = ctx->getProperty("LoggedIndexes", 1);
|
|
|
|
// Create index
|
|
BaseString::snprintf(pkIdxName, 255, "IDC_PK_%s", pTab->getName());
|
|
if (orderedIndex)
|
|
ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "ordered index "
|
|
<< pkIdxName << " (";
|
|
else
|
|
ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "unique index "
|
|
<< pkIdxName << " (";
|
|
|
|
NdbDictionary::Index pIdx(pkIdxName);
|
|
pIdx.setTable(pTab->getName());
|
|
if (orderedIndex)
|
|
pIdx.setType(NdbDictionary::Index::OrderedIndex);
|
|
else
|
|
pIdx.setType(NdbDictionary::Index::UniqueHashIndex);
|
|
for (int c = 0; c< pTab->getNoOfColumns(); c++){
|
|
const NdbDictionary::Column * col = pTab->getColumn(c);
|
|
if(col->getPrimaryKey()){
|
|
pIdx.addIndexColumn(col->getName());
|
|
ndbout << col->getName() <<" ";
|
|
}
|
|
}
|
|
|
|
pIdx.setStoredIndex(logged);
|
|
ndbout << ") ";
|
|
if (pNdb->getDictionary()->createIndex(pIdx) != 0){
|
|
ndbout << "FAILED!" << endl;
|
|
const NdbError err = pNdb->getDictionary()->getNdbError();
|
|
NDB_ERR(err);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
ndbout << "OK!" << endl;
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int createPkIndex_Drop(NDBT_Context* ctx, NDBT_Step* step){
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
// Drop index
|
|
ndbout << "Dropping index " << pkIdxName << " ";
|
|
if (pNdb->getDictionary()->dropIndex(pkIdxName,
|
|
pTab->getName()) != 0){
|
|
ndbout << "FAILED!" << endl;
|
|
NDB_ERR(pNdb->getDictionary()->getNdbError());
|
|
return NDBT_FAILED;
|
|
} else {
|
|
ndbout << "OK!" << endl;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
static
|
|
int
|
|
op_row(NdbTransaction* pTrans, HugoOperations& hugoOps,
|
|
const NdbDictionary::Table* pTab, int op, int row)
|
|
{
|
|
NdbOperation * pOp = 0;
|
|
switch(op){
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 12:
|
|
pOp = pTrans->getNdbOperation(pTab->getName());
|
|
break;
|
|
case 9:
|
|
return 0;
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 10:
|
|
case 11:
|
|
pOp = pTrans->getNdbIndexOperation(pkIdxName, pTab->getName());
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(op){
|
|
case 0:
|
|
case 6:
|
|
pOp->readTuple();
|
|
break;
|
|
case 1:
|
|
case 7:
|
|
pOp->committedRead();
|
|
break;
|
|
case 2:
|
|
case 8:
|
|
pOp->readTupleExclusive();
|
|
break;
|
|
case 3:
|
|
case 9:
|
|
pOp->insertTuple();
|
|
break;
|
|
case 4:
|
|
case 10:
|
|
pOp->updateTuple();
|
|
break;
|
|
case 5:
|
|
case 11:
|
|
pOp->deleteTuple();
|
|
break;
|
|
case 12:
|
|
CHECK(!pOp->simpleRead());
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(pOp, a, row) != 0){
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(op){
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 12:
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
CHECK(pOp->getValue(a));
|
|
}
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 10:
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == false){
|
|
if(hugoOps.setValueForAttr(pOp, a, row, 2) != 0){
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 5:
|
|
case 11:
|
|
pOp->deleteTuple();
|
|
break;
|
|
case 9:
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
static void print(int op)
|
|
{
|
|
const char * str = 0;
|
|
switch(op){
|
|
case 0: str = "pk read-sh"; break;
|
|
case 1: str = "pk read-nl"; break;
|
|
case 2: str = "pk read-ex"; break;
|
|
case 3: str = "pk insert "; break;
|
|
case 4: str = "pk update "; break;
|
|
case 5: str = "pk delete "; break;
|
|
case 6: str = "uk read-sh"; break;
|
|
case 7: str = "uk read-nl"; break;
|
|
case 8: str = "uk read-ex"; break;
|
|
case 9: str = "noop "; break;
|
|
case 10: str = "uk update "; break;
|
|
case 11: str = "uk delete "; break;
|
|
case 12: str = "pk read-si"; break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
printf("%s ", str);
|
|
}
|
|
|
|
int
|
|
runTestIgnoreError(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Uint32 loops = ctx->getNumRecords();
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
HugoTransactions hugoTrans(*pTab);
|
|
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
struct {
|
|
ExecType et;
|
|
AbortOption ao;
|
|
} tests[] = {
|
|
{ Commit, AbortOnError },
|
|
{ Commit, AO_IgnoreError },
|
|
{ NoCommit, AbortOnError },
|
|
{ NoCommit, AO_IgnoreError },
|
|
};
|
|
|
|
printf("case: <op1> <op2> c/nc ao/ie\n");
|
|
Uint32 tno = 0;
|
|
for (Uint32 op1 = 0; op1 < 13; op1++)
|
|
{
|
|
// NOTE : I get a node crash if the following loop starts from 0!
|
|
for (Uint32 op2 = op1; op2 < 13; op2++)
|
|
{
|
|
int ret;
|
|
NdbTransaction* pTrans = 0;
|
|
|
|
for (Uint32 i = 0; i<4; i++, tno++)
|
|
{
|
|
if (loops != 1000 && loops != tno)
|
|
continue;
|
|
ExecType et = tests[i].et;
|
|
AbortOption ao = tests[i].ao;
|
|
|
|
printf("%.3d : ", tno);
|
|
print(op1);
|
|
print(op2);
|
|
switch(et){
|
|
case Commit: printf("c "); break;
|
|
case NoCommit: printf("nc "); break;
|
|
default: printf("bad exectype : %d\n", et); return NDBT_FAILED;
|
|
}
|
|
switch(ao){
|
|
case AbortOnError: printf("aoe "); break;
|
|
case AO_IgnoreError: printf("ie "); break;
|
|
default: printf("bad abortoption : %d\n", ao); return NDBT_FAILED;
|
|
}
|
|
printf(": ");
|
|
|
|
|
|
hugoTrans.loadTable(pNdb, 1);
|
|
CHECK(pTrans = pNdb->startTransaction());
|
|
CHECK(!op_row(pTrans, hugoOps, pTab, op1, 0));
|
|
ret = pTrans->execute(et, ao);
|
|
pTrans->close();
|
|
printf("%d ", ret);
|
|
hugoTrans.clearTable(pNdb);
|
|
|
|
hugoTrans.loadTable(pNdb, 1);
|
|
CHECK(pTrans = pNdb->startTransaction());
|
|
CHECK(!op_row(pTrans, hugoOps, pTab, op1, 1));
|
|
ret = pTrans->execute(et, ao);
|
|
pTrans->close();
|
|
printf("%d ", ret);
|
|
hugoTrans.clearTable(pNdb);
|
|
|
|
hugoTrans.loadTable(pNdb, 1);
|
|
CHECK(pTrans = pNdb->startTransaction());
|
|
CHECK(!op_row(pTrans, hugoOps, pTab, op1, 0));
|
|
CHECK(!op_row(pTrans, hugoOps, pTab, op2, 1));
|
|
ret = pTrans->execute(et, ao);
|
|
pTrans->close();
|
|
printf("%d\n", ret);
|
|
hugoTrans.clearTable(pNdb);
|
|
|
|
hugoTrans.clearTable(pNdb);
|
|
}
|
|
}
|
|
}
|
|
return NDBT_OK;
|
|
}
|
|
|
|
static
|
|
Uint32
|
|
do_cnt(Ndb_cluster_connection* con)
|
|
{
|
|
Uint32 cnt = 0;
|
|
const Ndb* p = 0;
|
|
con->lock_ndb_objects();
|
|
while ((p = con->get_next_ndb_object(p)) != 0) cnt++;
|
|
con->unlock_ndb_objects();
|
|
return cnt;
|
|
}
|
|
|
|
int runCheckNdbObjectList(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb_cluster_connection* con = &ctx->m_cluster_connection;
|
|
|
|
Uint32 cnt1 = do_cnt(con);
|
|
Vector<Ndb*> objs;
|
|
for (Uint32 i = 0; i<100; i++)
|
|
{
|
|
Uint32 add = 1 + (rand() % 5);
|
|
for (Uint32 j = 0; j<add; j++)
|
|
{
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
objs.push_back(pNdb);
|
|
}
|
|
if (do_cnt(con) != (cnt1 + objs.size()))
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (Uint32 i = 0; i<100 && objs.size(); i++)
|
|
{
|
|
Uint32 sub = 1 + rand() % objs.size();
|
|
for (Uint32 j = 0; j<sub && objs.size(); j++)
|
|
{
|
|
Uint32 idx = rand() % objs.size();
|
|
delete objs[idx];
|
|
objs.erase(idx);
|
|
}
|
|
if (do_cnt(con) != (cnt1 + objs.size()))
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (Uint32 i = 0; i<objs.size(); i++)
|
|
delete objs[i];
|
|
|
|
return (cnt1 == do_cnt(con)) ? NDBT_OK : NDBT_FAILED;
|
|
}
|
|
|
|
|
|
static Ndb_cluster_connection* g_cluster_connection;
|
|
|
|
int runNdbClusterConnectionDelete_connection_owner(NDBT_Context* ctx,
|
|
NDBT_Step* step)
|
|
{
|
|
// Get connectstring from main connection
|
|
char constr[256];
|
|
if (!ctx->m_cluster_connection.get_connectstring(constr,
|
|
sizeof(constr)))
|
|
{
|
|
g_err << "Too short buffer for connectstring" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Create a new cluster connection, connect it and assign
|
|
// to pointer so the other thread can access it.
|
|
Ndb_cluster_connection* con = new Ndb_cluster_connection(constr);
|
|
|
|
const int retries = 12;
|
|
const int retry_delay = 5;
|
|
const int verbose = 1;
|
|
if (con->connect(retries, retry_delay, verbose) != 0)
|
|
{
|
|
delete con;
|
|
g_err << "Ndb_cluster_connection.connect failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
g_cluster_connection = con;
|
|
|
|
// Signal other thread that cluster connection has been creted
|
|
ctx->setProperty("CREATED", 1);
|
|
|
|
// Now wait for the other thread to use the connection
|
|
// until it signals this thread to continue and
|
|
// delete the cluster connection(since the
|
|
// other thread still have live Ndb objects created
|
|
// in the connection, this thread should hang in
|
|
// the delete until other thread has finished cleaning up)
|
|
ctx->getPropertyWait("CREATED", 2);
|
|
|
|
g_cluster_connection = NULL;
|
|
delete con;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runNdbClusterConnectionDelete_connection_user(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
// Wait for the cluster connection to be created by other thread
|
|
ctx->getPropertyWait("CREATED", 1);
|
|
|
|
Ndb_cluster_connection* con = g_cluster_connection;
|
|
|
|
// Create some Ndb objects and start transactions
|
|
class ActiveTransactions
|
|
{
|
|
Vector<NdbTransaction*> m_transactions;
|
|
|
|
public:
|
|
void release()
|
|
{
|
|
while(m_transactions.size())
|
|
{
|
|
NdbTransaction* trans = m_transactions[0];
|
|
Ndb* ndb = trans->getNdb();
|
|
g_info << "Deleting Ndb object " << ndb <<
|
|
"and transaction " << trans << endl;
|
|
ndb->closeTransaction(trans);
|
|
delete ndb;
|
|
m_transactions.erase(0);
|
|
}
|
|
// The list should be empty
|
|
assert(m_transactions.size() == 0);
|
|
}
|
|
|
|
~ActiveTransactions()
|
|
{
|
|
release();
|
|
}
|
|
|
|
void push_back(NdbTransaction* trans)
|
|
{
|
|
m_transactions.push_back(trans);
|
|
}
|
|
} active_transactions;
|
|
|
|
g_info << "Creating Ndb objects and transactions.." << endl;
|
|
for (Uint32 i = 0; i<100; i++)
|
|
{
|
|
Ndb* ndb = new Ndb(con, "TEST_DB");
|
|
if (ndb == NULL){
|
|
g_err << "ndb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (ndb->init(256) != 0){
|
|
NDB_ERR(ndb->getNdbError());
|
|
delete ndb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (ndb->waitUntilReady() != 0){
|
|
NDB_ERR(ndb->getNdbError());
|
|
delete ndb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbTransaction* trans = ndb->startTransaction();
|
|
if (trans == NULL){
|
|
g_err << "trans == NULL" << endl;
|
|
NDB_ERR(ndb->getNdbError());
|
|
delete ndb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
active_transactions.push_back(trans);
|
|
}
|
|
g_info << " ok!" << endl;
|
|
|
|
// Signal to cluster connection owner that Ndb objects have been created
|
|
ctx->setProperty("CREATED", 2);
|
|
|
|
// Delay a little and then start closing transactions and
|
|
// deleting the Ndb objects
|
|
NdbSleep_SecSleep(1);
|
|
|
|
g_info << "Releasing transactions and related Ndb objects..." << endl;
|
|
active_transactions.release();
|
|
g_info << " ok!" << endl;
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
testExecuteAsynchCallback(int res, NdbTransaction *con, void *data_ptr)
|
|
{
|
|
int *res_ptr= (int *)data_ptr;
|
|
|
|
*res_ptr= res;
|
|
}
|
|
|
|
int runTestExecuteAsynch(NDBT_Context* ctx, NDBT_Step* step){
|
|
/* Test that NdbTransaction::executeAsynch() works (BUG#27495). */
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init(2048)){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pOp->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->readTuples() != 0){
|
|
NDB_ERR(pOp->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->getValue(NdbDictionary::Column::FRAGMENT) == 0){
|
|
NDB_ERR(pOp->getNdbError());
|
|
pNdb->closeTransaction(pCon);
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
int res= 42;
|
|
pCon->executeAsynch(NoCommit, testExecuteAsynchCallback, &res);
|
|
while(pNdb->pollNdb(100000) == 0)
|
|
;
|
|
if (res != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
ndbout << "Error returned from execute: " << res << endl;
|
|
result= NDBT_FAILED;
|
|
}
|
|
|
|
pNdb->closeTransaction(pCon);
|
|
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
template class Vector<NdbScanOperation*>;
|
|
|
|
int
|
|
runBug28443(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
int result = NDBT_OK;
|
|
int records = ctx->getNumRecords();
|
|
|
|
NdbRestarter restarter;
|
|
|
|
restarter.insertErrorInAllNodes(9003);
|
|
|
|
for (int i = 0; i<ctx->getNumLoops(); i++)
|
|
{
|
|
HugoTransactions hugoTrans(*ctx->getTab());
|
|
if (hugoTrans.loadTable(GETNDB(step), records, 2048) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
if (runClearTable(ctx, step) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
restarter.insertErrorInAllNodes(9003);
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
runBug37158(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
int result = NDBT_OK;
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
for (int i = 0; i<ctx->getNumLoops(); i++)
|
|
{
|
|
HugoOperations hugoOps(*ctx->getTab());
|
|
hugoOps.startTransaction(pNdb);
|
|
if (hugoOps.pkWriteRecord(pNdb, 0) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
|
|
if (hugoOps.pkWritePartialRecord(pNdb, 1) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if (hugoOps.pkWriteRecord(pNdb, 2) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if (hugoOps.pkUpdateRecord(pNdb, 0) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if (hugoOps.execute_Commit(pNdb, AO_IgnoreError) == 4011)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
hugoOps.closeTransaction(pNdb);
|
|
|
|
if (runClearTable(ctx, step) != 0)
|
|
{
|
|
result = NDBT_FAILED;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
simpleReadAbortOnError(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Simple read has some error handling issues
|
|
* Setting the operation to be AbortOnError can expose these
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
HugoOperations hugoOps(*pTab);
|
|
NdbRestarter restarter;
|
|
|
|
hugoOps.startTransaction(pNdb);
|
|
CHECK(!hugoOps.pkWriteRecord(pNdb,0));
|
|
CHECK(!hugoOps.execute_Commit(pNdb, AbortOnError));
|
|
|
|
NdbTransaction* trans;
|
|
|
|
CHECK(trans= pNdb->startTransaction());
|
|
|
|
/* Insert error 5047 which causes next LQHKEYREQ to fail due
|
|
* to 'transporter overload'
|
|
* Error insert is self-clearing
|
|
*/
|
|
restarter.insertErrorInAllNodes(5047);
|
|
|
|
/* Create SimpleRead on row 0, which exists (though we'll get
|
|
* 'transporter overload for this'
|
|
*/
|
|
NdbOperation* op;
|
|
CHECK(op= trans->getNdbOperation(pTab));
|
|
|
|
CHECK(!op->simpleRead());
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(op, a, 0) != 0){
|
|
restarter.insertErrorInAllNodes(0);
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
CHECK(op->getValue(a));
|
|
}
|
|
|
|
CHECK(!op->setAbortOption(NdbOperation::AbortOnError));
|
|
|
|
/* Create normal read on row 0 which will succeed */
|
|
NdbOperation* op2;
|
|
CHECK(op2= trans->getNdbOperation(pTab));
|
|
|
|
CHECK(!op2->readTuple());
|
|
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
if (pTab->getColumn(a)->getPrimaryKey() == true){
|
|
if(hugoOps.equalForAttr(op2, a, 0) != 0){
|
|
restarter.insertErrorInAllNodes(0);
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
for(int a = 0; a<pTab->getNoOfColumns(); a++){
|
|
CHECK(op2->getValue(a));
|
|
}
|
|
|
|
CHECK(!op2->setAbortOption(NdbOperation::AbortOnError));
|
|
|
|
|
|
CHECK(trans->execute(NoCommit) == -1);
|
|
|
|
CHECK(trans->getNdbError().code == 1218); // Transporter Overload
|
|
|
|
restarter.insertErrorInAllNodes(0);
|
|
|
|
return NDBT_OK;
|
|
|
|
}
|
|
|
|
|
|
int
|
|
testNdbRecordPkAmbiguity(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* NdbRecord Insert and Write can take 2 record and row ptrs
|
|
* In all cases, the AttrInfo sent to TC for PK columns
|
|
* should be the same as the KeyInfo sent to TC to avoid
|
|
* inconsistency
|
|
* Approach :
|
|
* 1) Use Insert/Write to insert tuple with different
|
|
* values for pks in attr row
|
|
* 2) Read back all data, including PKs
|
|
* 3) Verify all values.
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
const NdbRecord* tabRec= pTab->getDefaultRecord();
|
|
const Uint32 sizeOfTabRec= NdbDictionary::getRecordRowLength(tabRec);
|
|
char keyRowBuf[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
char attrRowBuf[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
bzero(keyRowBuf, sizeof(keyRowBuf));
|
|
bzero(attrRowBuf, sizeof(attrRowBuf));
|
|
|
|
HugoCalculator calc(*pTab);
|
|
|
|
const int numRecords= 100;
|
|
|
|
for (int optype=0; optype < 2; optype++)
|
|
{
|
|
/* First, let's calculate the correct Hugo values for this row */
|
|
|
|
for (int record=0; record < numRecords; record++)
|
|
{
|
|
int updates= 0;
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
keyRowBuf,
|
|
col);
|
|
CHECK(valPtr != NULL);
|
|
|
|
int len= pTab->getColumn(col)->getSizeInBytes();
|
|
Uint32 real_len;
|
|
bool isNull= (calc.calcValue(record, col, updates, valPtr,
|
|
len, &real_len) == NULL);
|
|
if (pTab->getColumn(col)->getNullable())
|
|
{
|
|
NdbDictionary::setNull(tabRec,
|
|
keyRowBuf,
|
|
col,
|
|
isNull);
|
|
}
|
|
}
|
|
|
|
/* Now copy the values to the Attr record */
|
|
memcpy(attrRowBuf, keyRowBuf, sizeOfTabRec);
|
|
|
|
Uint32 mippleAttempts= 3;
|
|
|
|
while (memcmp(keyRowBuf, attrRowBuf, sizeOfTabRec) == 0)
|
|
{
|
|
/* Now doctor the PK values in the Attr record */
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
if (pTab->getColumn(col)->getPrimaryKey())
|
|
{
|
|
char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
attrRowBuf,
|
|
col);
|
|
CHECK(valPtr != NULL);
|
|
|
|
int len= pTab->getColumn(col)->getSizeInBytes();
|
|
Uint32 real_len;
|
|
/* We use the PK value for some other record */
|
|
int badRecord= record + (rand() % 1000);
|
|
bool isNull= (calc.calcValue(badRecord, col, updates, valPtr,
|
|
len, &real_len) == NULL);
|
|
CHECK(! isNull);
|
|
}
|
|
}
|
|
|
|
/* Can try to get variance only a limited number of times */
|
|
CHECK(mippleAttempts-- != 0);
|
|
}
|
|
|
|
/* Ok, now have key and attr records with different values for
|
|
* PK cols, let's try to insert
|
|
*/
|
|
NdbTransaction* trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
|
|
const NdbOperation* op= NULL;
|
|
if (optype == 0)
|
|
{
|
|
// ndbout << "Using insertTuple" << endl;
|
|
op= trans->insertTuple(tabRec,
|
|
keyRowBuf,
|
|
tabRec,
|
|
attrRowBuf);
|
|
}
|
|
else
|
|
{
|
|
// ndbout << "Using writeTuple" << endl;
|
|
op= trans->writeTuple(tabRec,
|
|
keyRowBuf,
|
|
tabRec,
|
|
attrRowBuf);
|
|
}
|
|
CHECK(op != 0);
|
|
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Now read back */
|
|
memset(attrRowBuf, 0, sizeOfTabRec);
|
|
|
|
Uint32 pkVal= 0;
|
|
memcpy(&pkVal, NdbDictionary::getValuePtr(tabRec,
|
|
keyRowBuf,
|
|
0),
|
|
sizeof(pkVal));
|
|
|
|
trans= pNdb->startTransaction();
|
|
op= trans->readTuple(tabRec,
|
|
keyRowBuf,
|
|
tabRec,
|
|
attrRowBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
CHECK(trans->getNdbError().code == 0);
|
|
trans->close();
|
|
|
|
/* Verify the values read back */
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
const char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
attrRowBuf,
|
|
col);
|
|
CHECK(valPtr != NULL);
|
|
|
|
char calcBuff[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
int len= pTab->getColumn(col)->getSizeInBytes();
|
|
Uint32 real_len;
|
|
bool isNull= (calc.calcValue(record, col, updates, calcBuff,
|
|
len, &real_len) == NULL);
|
|
bool colIsNullable= pTab->getColumn(col)->getNullable();
|
|
if (isNull)
|
|
{
|
|
CHECK(colIsNullable);
|
|
if (!NdbDictionary::isNull(tabRec,
|
|
attrRowBuf,
|
|
col))
|
|
{
|
|
ndbout << "Error, col " << col
|
|
<< " (pk=" << pTab->getColumn(col)->getPrimaryKey()
|
|
<< ") should be Null, but is not" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (colIsNullable)
|
|
{
|
|
if (NdbDictionary::isNull(tabRec,
|
|
attrRowBuf,
|
|
col))
|
|
{
|
|
ndbout << "Error, col " << col
|
|
<< " (pk=" << pTab->getColumn(col)->getPrimaryKey()
|
|
<< ") should be non-Null but is null" << endl;
|
|
return NDBT_FAILED;
|
|
};
|
|
}
|
|
|
|
/* Compare actual data read back */
|
|
if( memcmp(calcBuff, valPtr, real_len) != 0 )
|
|
{
|
|
ndbout << "Error, col " << col
|
|
<< " (pk=" << pTab->getColumn(col)->getPrimaryKey()
|
|
<< ") should be equal, but isn't for record "
|
|
<< record << endl;
|
|
ndbout << "Expected :";
|
|
for (Uint32 i=0; i < real_len; i++)
|
|
{
|
|
ndbout_c("%x ", calcBuff[i]);
|
|
}
|
|
ndbout << endl << "Received :";
|
|
for (Uint32 i=0; i < real_len; i++)
|
|
{
|
|
ndbout_c("%x ", valPtr[i]);
|
|
}
|
|
ndbout << endl;
|
|
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now delete the tuple */
|
|
trans= pNdb->startTransaction();
|
|
op= trans->deleteTuple(tabRec,
|
|
keyRowBuf,
|
|
tabRec);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
|
|
trans->close();
|
|
}
|
|
}
|
|
|
|
return NDBT_OK;
|
|
|
|
}
|
|
|
|
int
|
|
testNdbRecordPKUpdate(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* In general, we should be able to update primary key
|
|
* values. We cannot *change* them, but for cases where
|
|
* a collation maps several discrete values to a single
|
|
* normalised value, it should be possible to modify
|
|
* the discrete value of the key, as the normalised
|
|
* key value is unchanged.
|
|
* Rather than testing with such a collation here, we
|
|
* cop out and test for errors with a 'null' change.
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
const NdbRecord* tabRec= pTab->getDefaultRecord();
|
|
char rowBuf[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
char badKeyRowBuf[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
|
|
HugoCalculator calc(*pTab);
|
|
|
|
const int numRecords= 100;
|
|
|
|
/* First, let's calculate the correct Hugo values for this row */
|
|
for (int record=0; record < numRecords; record++)
|
|
{
|
|
int updates= 0;
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
rowBuf,
|
|
col);
|
|
CHECK(valPtr != NULL);
|
|
|
|
int len= pTab->getColumn(col)->getSizeInBytes();
|
|
Uint32 real_len;
|
|
bool isNull= (calc.calcValue(record, col, updates, valPtr,
|
|
len, &real_len) == NULL);
|
|
if (pTab->getColumn(col)->getNullable())
|
|
{
|
|
NdbDictionary::setNull(tabRec,
|
|
rowBuf,
|
|
col,
|
|
isNull);
|
|
}
|
|
}
|
|
|
|
/* Create similar row, but with different id col (different
|
|
* PK from p.o.v. of PK column update
|
|
*/
|
|
memcpy(badKeyRowBuf, rowBuf, NDB_MAX_TUPLE_SIZE_IN_WORDS << 2);
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
if (calc.isIdCol(col))
|
|
{
|
|
char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
badKeyRowBuf,
|
|
col);
|
|
Uint32 badId= record+333;
|
|
memcpy(valPtr, &badId, sizeof(badId));
|
|
}
|
|
}
|
|
|
|
NdbTransaction* trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
|
|
const NdbOperation* op= trans->insertTuple(tabRec,
|
|
rowBuf);
|
|
CHECK(op != 0);
|
|
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Now update the PK columns */
|
|
trans= pNdb->startTransaction();
|
|
op= trans->updateTuple(tabRec,
|
|
rowBuf,
|
|
tabRec,
|
|
rowBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
CHECK(trans->getNdbError().code == 0);
|
|
trans->close();
|
|
|
|
/* Now update PK with scan takeover op */
|
|
trans= pNdb->startTransaction();
|
|
|
|
NdbScanOperation* scanOp=trans->scanTable(tabRec,
|
|
NdbOperation::LM_Exclusive);
|
|
CHECK(scanOp != 0);
|
|
|
|
CHECK(trans->execute(NoCommit) == 0);
|
|
|
|
/* Now update PK with lock takeover op */
|
|
const char* rowPtr;
|
|
CHECK(scanOp->nextResult(&rowPtr, true, true) == 0);
|
|
|
|
op= scanOp->updateCurrentTuple(trans,
|
|
tabRec,
|
|
rowBuf);
|
|
CHECK(op != NULL);
|
|
|
|
CHECK(trans->execute(Commit) == 0);
|
|
|
|
trans->close();
|
|
|
|
/* Now attempt bad PK update with lock takeover op
|
|
* This is interesting as NDBAPI normally takes the
|
|
* value of PK columns in an update from the key
|
|
* row - so it's not possible to pass a 'different'
|
|
* value (except when collations are used).
|
|
* Scan Takeover update takes the PK values from the
|
|
* attribute record and so different values can
|
|
* be supplied.
|
|
* Here we check that different values result in the
|
|
* kernel complaining.
|
|
*/
|
|
trans= pNdb->startTransaction();
|
|
|
|
scanOp=trans->scanTable(tabRec,
|
|
NdbOperation::LM_Exclusive);
|
|
CHECK(scanOp != 0);
|
|
|
|
CHECK(trans->execute(NoCommit) == 0);
|
|
|
|
/* Now update PK with lock takeover op */
|
|
CHECK(scanOp->nextResult(&rowPtr, true, true) == 0);
|
|
|
|
op= scanOp->updateCurrentTuple(trans,
|
|
tabRec,
|
|
badKeyRowBuf);
|
|
CHECK(op != NULL);
|
|
|
|
CHECK(trans->execute(Commit) == -1);
|
|
CHECK(trans->getNdbError().code == 897);
|
|
|
|
trans->close();
|
|
|
|
/* Now delete the tuple */
|
|
trans= pNdb->startTransaction();
|
|
op= trans->deleteTuple(tabRec,
|
|
rowBuf,
|
|
tabRec);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
|
|
trans->close();
|
|
}
|
|
|
|
return NDBT_OK;
|
|
|
|
}
|
|
|
|
static
|
|
BaseString getKeyVal(int record, bool upper)
|
|
{
|
|
/* Create VARCHAR format key with upper or
|
|
* lower case leading char
|
|
*/
|
|
BaseString keyData;
|
|
char c= 'a' + (record % ('z' - 'a'));
|
|
|
|
keyData.appfmt("%cblahblah%d", c, record);
|
|
|
|
if (upper)
|
|
keyData.ndb_toupper();
|
|
|
|
BaseString varCharKey;
|
|
varCharKey.appfmt("%c%s", keyData.length(), keyData.c_str());
|
|
|
|
return varCharKey;
|
|
}
|
|
|
|
int
|
|
testNdbRecordCICharPKUpdate(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Test a change to a CHAR primary key with a case insensitive
|
|
* collation.
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
|
|
/* Run as a 'T1' testcase - do nothing for other tables */
|
|
if (strcmp(pTab->getName(), "T1") != 0)
|
|
return NDBT_OK;
|
|
|
|
CHARSET_INFO* charset= NULL;
|
|
const char* csname="latin1_general_ci";
|
|
charset= get_charset_by_name(csname, MYF(0));
|
|
|
|
if (charset == NULL)
|
|
{
|
|
ndbout << "Couldn't get charset " << csname << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* Create table with required schema */
|
|
NdbDictionary::Table tab;
|
|
tab.setName("TAB_CICHARPKUPD");
|
|
|
|
NdbDictionary::Column pk;
|
|
pk.setName("PK");
|
|
pk.setType(NdbDictionary::Column::Varchar);
|
|
pk.setLength(20);
|
|
pk.setNullable(false);
|
|
pk.setPrimaryKey(true);
|
|
pk.setCharset(charset);
|
|
tab.addColumn(pk);
|
|
|
|
NdbDictionary::Column data;
|
|
data.setName("DATA");
|
|
data.setType(NdbDictionary::Column::Unsigned);
|
|
data.setNullable(false);
|
|
data.setPrimaryKey(false);
|
|
tab.addColumn(data);
|
|
|
|
pNdb->getDictionary()->dropTable(tab.getName());
|
|
if(pNdb->getDictionary()->createTable(tab) != 0)
|
|
{
|
|
ndbout << "Create table failed with error : "
|
|
<< pNdb->getDictionary()->getNdbError().code
|
|
<< pNdb->getDictionary()->getNdbError().message
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
ndbout << (NDBT_Table&)tab << endl;
|
|
|
|
pTab= pNdb->getDictionary()->getTable(tab.getName());
|
|
|
|
const NdbRecord* tabRec= pTab->getDefaultRecord();
|
|
const Uint32 rowLen= NDB_MAX_TUPLE_SIZE_IN_WORDS << 2;
|
|
char ucRowBuf[ rowLen ];
|
|
char lcRowBuf[ rowLen ];
|
|
char readBuf[ rowLen ];
|
|
char* ucPkPtr= NdbDictionary::getValuePtr(tabRec,
|
|
ucRowBuf,
|
|
0);
|
|
Uint32* ucDataPtr= (Uint32*) NdbDictionary::getValuePtr(tabRec,
|
|
ucRowBuf,
|
|
1);
|
|
char* lcPkPtr= NdbDictionary::getValuePtr(tabRec,
|
|
lcRowBuf,
|
|
0);
|
|
Uint32* lcDataPtr= (Uint32*) NdbDictionary::getValuePtr(tabRec,
|
|
lcRowBuf,
|
|
1);
|
|
|
|
char* readPkPtr= NdbDictionary::getValuePtr(tabRec,
|
|
readBuf,
|
|
0);
|
|
Uint32* readDataPtr= (Uint32*) NdbDictionary::getValuePtr(tabRec,
|
|
readBuf,
|
|
1);
|
|
|
|
|
|
const int numRecords= 100;
|
|
BaseString upperKey;
|
|
BaseString lowerKey;
|
|
|
|
for (int record=0; record < numRecords; record++)
|
|
{
|
|
upperKey.assign(getKeyVal(record, true).c_str());
|
|
lowerKey.assign(getKeyVal(record, false).c_str());
|
|
|
|
memcpy(ucPkPtr, upperKey.c_str(), upperKey.length());
|
|
memcpy(lcPkPtr, lowerKey.c_str(), lowerKey.length());
|
|
memcpy(ucDataPtr, &record, sizeof(record));
|
|
memcpy(lcDataPtr, &record, sizeof(record));
|
|
|
|
/* Insert with upper case */
|
|
NdbTransaction* trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
|
|
const NdbOperation* op= trans->insertTuple(tabRec,
|
|
ucRowBuf);
|
|
CHECK(op != 0);
|
|
|
|
int rc= trans->execute(Commit);
|
|
if (rc != 0)
|
|
ndbout << "Error " << trans->getNdbError().message << endl;
|
|
CHECK(rc == 0);
|
|
trans->close();
|
|
|
|
/* Read with upper case */
|
|
trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
op= trans->readTuple(tabRec,
|
|
ucRowBuf,
|
|
tabRec,
|
|
readBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Check key and data read */
|
|
CHECK(memcmp(ucPkPtr, readPkPtr, ucPkPtr[0]) == 0);
|
|
CHECK(memcmp(ucDataPtr, readDataPtr, sizeof(int)) == 0);
|
|
|
|
memset(readBuf, 0, NDB_MAX_TUPLE_SIZE_IN_WORDS << 2);
|
|
|
|
/* Read with lower case */
|
|
trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
op= trans->readTuple(tabRec,
|
|
lcRowBuf,
|
|
tabRec,
|
|
readBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Check key and data read */
|
|
CHECK(memcmp(ucPkPtr, readPkPtr, ucPkPtr[0]) == 0);
|
|
CHECK(memcmp(ucDataPtr, readDataPtr, sizeof(int)) == 0);
|
|
|
|
memset(readBuf, 0, NDB_MAX_TUPLE_SIZE_IN_WORDS << 2);
|
|
|
|
/* Now update just the PK column to lower case */
|
|
trans= pNdb->startTransaction();
|
|
unsigned char mask[1];
|
|
mask[0]= 1;
|
|
op= trans->updateTuple(tabRec,
|
|
lcRowBuf,
|
|
tabRec,
|
|
lcRowBuf,
|
|
mask);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
CHECK(trans->getNdbError().code == 0);
|
|
trans->close();
|
|
|
|
/* Now check that we can read with the upper case key */
|
|
memset(readBuf, 0, NDB_MAX_TUPLE_SIZE_IN_WORDS << 2);
|
|
|
|
trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
op= trans->readTuple(tabRec,
|
|
ucRowBuf,
|
|
tabRec,
|
|
readBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Check key and data read */
|
|
CHECK(memcmp(lcPkPtr, readPkPtr, lcPkPtr[0]) == 0);
|
|
CHECK(memcmp(lcDataPtr, readDataPtr, sizeof(int)) == 0);
|
|
|
|
/* Now check that we can read with the lower case key */
|
|
memset(readBuf, 0, NDB_MAX_TUPLE_SIZE_IN_WORDS << 2);
|
|
|
|
trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
op= trans->readTuple(tabRec,
|
|
lcRowBuf,
|
|
tabRec,
|
|
readBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Check key and data read */
|
|
CHECK(memcmp(lcPkPtr, readPkPtr, lcPkPtr[0]) == 0);
|
|
CHECK(memcmp(lcDataPtr, readDataPtr, sizeof(int)) == 0);
|
|
|
|
|
|
/* Now delete the tuple */
|
|
trans= pNdb->startTransaction();
|
|
op= trans->deleteTuple(tabRec,
|
|
ucRowBuf,
|
|
tabRec);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
|
|
trans->close();
|
|
}
|
|
|
|
pNdb->getDictionary()->dropTable(tab.getName());
|
|
|
|
return NDBT_OK;
|
|
|
|
}
|
|
|
|
int
|
|
testNdbRecordRowLength(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Bug#43891 ignored null bits at the end of an row
|
|
* when calculating the row length, leading to various
|
|
* problems
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
int numCols= pTab->getNoOfColumns();
|
|
const NdbRecord* defaultRecord= pTab->getDefaultRecord();
|
|
|
|
/* Create an NdbRecord structure with all the Null
|
|
* bits at the end - to test that they are included
|
|
* correctly in row length calculations.
|
|
*/
|
|
NdbDictionary::RecordSpecification rsArray[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
|
|
|
|
bool hasNullable= false;
|
|
Uint32 highestUsed= 9000;
|
|
for (int attrId=0; attrId< numCols; attrId++)
|
|
{
|
|
NdbDictionary::RecordSpecification& rs= rsArray[attrId];
|
|
|
|
rs.column= pTab->getColumn(attrId);
|
|
CHECK(NdbDictionary::getOffset(defaultRecord,
|
|
attrId,
|
|
rs.offset));
|
|
CHECK(NdbDictionary::getNullBitOffset(defaultRecord,
|
|
attrId,
|
|
rs.nullbit_byte_offset,
|
|
rs.nullbit_bit_in_byte));
|
|
if (rs.column->getNullable())
|
|
{
|
|
/* Shift null bit(s) to bytes beyond the end of the record */
|
|
hasNullable= true;
|
|
rs.nullbit_byte_offset= highestUsed++;
|
|
rs.nullbit_bit_in_byte= 0;
|
|
}
|
|
}
|
|
|
|
if (hasNullable)
|
|
{
|
|
printf("Testing");
|
|
const NdbRecord* myRecord= pNdb->getDictionary()->createRecord(pTab,
|
|
rsArray,
|
|
numCols,
|
|
sizeof(NdbDictionary::RecordSpecification));
|
|
CHECK(myRecord != 0);
|
|
Uint32 rowLength= NdbDictionary::getRecordRowLength(myRecord);
|
|
if (rowLength != highestUsed)
|
|
{
|
|
ndbout << "Failure, expected row length " << highestUsed
|
|
<< " got row length " << rowLength
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runBug44015(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* testNdbApi -n WeirdAssertFail
|
|
* Generates phrase "here2" on 6.3 which is
|
|
* output by DbtupExecQuery::handleReadReq()
|
|
* detecting that the record's tuple checksum
|
|
* is incorrect.
|
|
* Later can generate assertion failure in
|
|
* prepare_read
|
|
* ndbassert(src_len >= (dynstart - src_data));
|
|
* resulting in node failure
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
|
|
int numIterations= 100;
|
|
int numRecords= 1024;
|
|
|
|
NdbTransaction* trans;
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
for (int iter=0; iter < numIterations; iter++)
|
|
{
|
|
ndbout << "Iter : " << iter << endl;
|
|
CHECK((trans= pNdb->startTransaction()) != 0);
|
|
|
|
CHECK(hugoOps.setTransaction(trans) == 0);
|
|
|
|
CHECK(hugoOps.pkInsertRecord(pNdb,
|
|
0,
|
|
numRecords) == 0);
|
|
|
|
/* Now execute the transaction */
|
|
if ((trans->execute(NdbTransaction::NoCommit) != 0))
|
|
{
|
|
ndbout << "Execute failed, error is "
|
|
<< trans->getNdbError().code << " "
|
|
<< trans->getNdbError().message << endl;
|
|
CHECK(0);
|
|
}
|
|
|
|
CHECK(trans->getNdbError().code == 0);
|
|
|
|
/* Now delete the records in the same transaction
|
|
* Need to do this manually as Hugo doesn't support it
|
|
*/
|
|
CHECK(hugoOps.pkDeleteRecord(pNdb,
|
|
0,
|
|
numRecords) == 0);
|
|
|
|
CHECK(trans->execute(NdbTransaction::NoCommit) == 0);
|
|
CHECK(trans->getNdbError().code == 0);
|
|
|
|
/* Now abort the transaction by closing it */
|
|
trans->close();
|
|
|
|
/* Force Hugo Transaction back to NULL */
|
|
hugoOps.setTransaction(NULL, true);
|
|
}
|
|
|
|
ctx->stopTest();
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runScanReadUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
int i = 0;
|
|
int scan_flags = NdbScanOperation::SF_TupScan;
|
|
NdbOperation::LockMode lm =
|
|
(NdbOperation::LockMode)
|
|
ctx->getProperty("ReadLockMode", (Uint32)NdbOperation::LM_CommittedRead);
|
|
|
|
HugoTransactions hugoTrans(*ctx->getTab());
|
|
while (ctx->isTestStopped() == false) {
|
|
g_info << i << ": ";
|
|
if (hugoTrans.scanReadRecords(GETNDB(step), 0, 0, 0,
|
|
lm, scan_flags) != 0){
|
|
return NDBT_FAILED;
|
|
}
|
|
i++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int
|
|
runBug44065_org(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* testNdbApi -n WeirdAssertFail2
|
|
* Results in assertion failure in DbtupCommit::execTUP_DEALLOCREQ()
|
|
* ndbassert(ptr->m_header_bits & Tuple_header::FREE);
|
|
* Results in node failure
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
|
|
int numOuterIterations= 50;
|
|
int numInnerIterations= 20;
|
|
int numRecords= 200;
|
|
|
|
NdbTransaction* trans;
|
|
|
|
for (int outerIter=0; outerIter < numOuterIterations; outerIter++)
|
|
{
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
int offset= (outerIter * numRecords);
|
|
ndbout << "Outer Iter : " << outerIter
|
|
<< " " << offset << "-" << (offset + numRecords - 1) << endl;
|
|
|
|
{
|
|
HugoTransactions trans(*pTab);
|
|
CHECK(trans.loadTableStartFrom(pNdb, offset, numRecords) == 0);
|
|
}
|
|
|
|
for (int iter=0; iter < numInnerIterations; iter++)
|
|
{
|
|
//ndbout << "Inner Iter : " << iter << endl;
|
|
CHECK((trans= pNdb->startTransaction()) != 0);
|
|
|
|
CHECK(hugoOps.setTransaction(trans) == 0);
|
|
|
|
/* Delete the records */
|
|
CHECK(hugoOps.pkDeleteRecord(pNdb,
|
|
offset,
|
|
numRecords) == 0);
|
|
|
|
/* Re-insert them */
|
|
CHECK(hugoOps.pkInsertRecord(pNdb,
|
|
offset,
|
|
numRecords) == 0);
|
|
|
|
/* Now execute the transaction, with IgnoreError */
|
|
if ((trans->execute(NdbTransaction::NoCommit,
|
|
NdbOperation::AO_IgnoreError) != 0))
|
|
{
|
|
NdbError err = trans->getNdbError();
|
|
ndbout << "Execute failed, error is "
|
|
<< err.code << " " << endl;
|
|
CHECK((err.classification == NdbError::TemporaryResourceError ||
|
|
err.classification == NdbError::OverloadError ||
|
|
err.classification == NdbError::TimeoutExpired));
|
|
NdbSleep_MilliSleep(50);
|
|
}
|
|
|
|
/* Now abort the transaction by closing it without committing */
|
|
trans->close();
|
|
|
|
/* Force Hugo Transaction back to NULL */
|
|
hugoOps.setTransaction(NULL, true);
|
|
}
|
|
}
|
|
|
|
ctx->stopTest();
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
static volatile int aValue = 0;
|
|
|
|
void
|
|
a_callback(int, NdbTransaction*, void*)
|
|
{
|
|
ndbout_c("callback received!");
|
|
aValue = 1;
|
|
}
|
|
|
|
int
|
|
runBug44065(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* testNdbApi -n WeirdAssertFail2
|
|
* Results in assertion failure in DbtupCommit::execTUP_DEALLOCREQ()
|
|
* ndbassert(ptr->m_header_bits & Tuple_header::FREE);
|
|
* Results in node failure
|
|
*/
|
|
int rowno = 0;
|
|
aValue = 0;
|
|
Ndb* pNdb = GETNDB(step);
|
|
Ndb * pNdb2 = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
pNdb2->init();
|
|
pNdb2->waitUntilReady();
|
|
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
|
|
HugoOperations hugoOps1(*pTab);
|
|
CHECK(hugoOps1.startTransaction(pNdb) == 0);
|
|
CHECK(hugoOps1.pkInsertRecord(pNdb, rowno) == 0);
|
|
CHECK(hugoOps1.execute_NoCommit(pNdb) == 0);
|
|
|
|
{
|
|
HugoOperations hugoOps2(*pTab);
|
|
CHECK(hugoOps2.startTransaction(pNdb2) == 0);
|
|
|
|
CHECK(hugoOps2.pkDeleteRecord(pNdb2, rowno) == 0);
|
|
CHECK(hugoOps2.pkInsertRecord(pNdb2, rowno) == 0);
|
|
|
|
NdbTransaction* trans = hugoOps2.getTransaction();
|
|
aValue = 0;
|
|
|
|
trans->executeAsynch(NdbTransaction::NoCommit, a_callback, 0);
|
|
pNdb2->sendPreparedTransactions(1);
|
|
CHECK(hugoOps1.execute_Commit(pNdb) == 0);
|
|
ndbout_c("waiting for callback");
|
|
while (aValue == 0)
|
|
{
|
|
pNdb2->pollNdb();
|
|
NdbSleep_MilliSleep(100);
|
|
}
|
|
CHECK(hugoOps2.execute_Rollback(pNdb2) == 0);
|
|
}
|
|
|
|
delete pNdb2; // need to delete hugoOps2 before pNdb2
|
|
ctx->stopTest();
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int testApiFailReqImpl(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Setup a separate connection for running PK updates
|
|
* with that will be disconnected without affecting
|
|
* the test framework
|
|
*/
|
|
if (otherConnection != NULL)
|
|
{
|
|
ndbout << "Connection not null" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
char connectString[256];
|
|
ctx->m_cluster_connection.get_connectstring(connectString,
|
|
sizeof(connectString));
|
|
|
|
otherConnection= new Ndb_cluster_connection(connectString);
|
|
|
|
if (otherConnection == NULL)
|
|
{
|
|
ndbout << "Connection is null" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int rc= otherConnection->connect();
|
|
|
|
if (rc!= 0)
|
|
{
|
|
ndbout << "Connect failed with rc " << rc << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* Check that all nodes are alive - if one has failed
|
|
* then probably we exposed bad API_FAILREQ handling
|
|
*/
|
|
if (otherConnection->wait_until_ready(10,10) != 0)
|
|
{
|
|
ndbout << "Cluster connection was not ready" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (int i=0; i < MAX_STEPS; i++)
|
|
{
|
|
/* We must create the Ndb objects here as we
|
|
* are still single threaded
|
|
*/
|
|
stepNdbs[i]= new Ndb(otherConnection,
|
|
"TEST_DB");
|
|
stepNdbs[i]->init();
|
|
int rc= stepNdbs[i]->waitUntilReady(10);
|
|
|
|
if (rc != 0)
|
|
{
|
|
ndbout << "Ndb " << i << " was not ready" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
}
|
|
|
|
/* Now signal the 'worker' threads to start sending Pk
|
|
* reads
|
|
*/
|
|
ctx->setProperty(ApiFailTestRun, 1);
|
|
|
|
/* Wait until all of them are running before proceeding */
|
|
ctx->getPropertyWait(ApiFailTestsRunning,
|
|
ctx->getProperty(ApiFailNumberPkSteps));
|
|
|
|
if (ctx->isTestStopped())
|
|
{
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/* Clear the test-run flag so that they'll wait after
|
|
* they hit an error
|
|
*/
|
|
ctx->setProperty(ApiFailTestRun, (Uint32)0);
|
|
|
|
/* Wait a little */
|
|
sleep(1);
|
|
|
|
/* Active more stringent checking of behaviour after
|
|
* API_FAILREQ
|
|
*/
|
|
NdbRestarter restarter;
|
|
|
|
/* Activate 8078 - TCs will abort() if they get a TCKEYREQ
|
|
* from the failed API after an API_FAILREQ message
|
|
*/
|
|
ndbout << "Activating 8078" << endl;
|
|
restarter.insertErrorInAllNodes(8078);
|
|
|
|
/* Wait a little longer */
|
|
sleep(1);
|
|
|
|
/* Now cause our connection to disconnect
|
|
* This results in TC receiving an API_FAILREQ
|
|
* If there's an issue with API_FAILREQ 'cleanly'
|
|
* stopping further signals, there should be
|
|
* an assertion failure in TC
|
|
*/
|
|
int otherNodeId = otherConnection->node_id();
|
|
|
|
ndbout << "Forcing disconnect of node "
|
|
<< otherNodeId << endl;
|
|
|
|
/* All dump 900 <nodeId> */
|
|
int args[2]= {900, otherNodeId};
|
|
|
|
restarter.dumpStateAllNodes( args, 2 );
|
|
|
|
|
|
/* Now wait for all workers to finish
|
|
* (Running worker count to get down to zero
|
|
*/
|
|
ctx->getPropertyWait(ApiFailTestsRunning, (Uint32)0);
|
|
|
|
if (ctx->isTestStopped())
|
|
{
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/* Clean up error insert */
|
|
restarter.insertErrorInAllNodes(0);
|
|
|
|
/* Clean up allocated resources */
|
|
for (int i= 0; i < MAX_STEPS; i++)
|
|
{
|
|
delete stepNdbs[i];
|
|
stepNdbs[i]= NULL;
|
|
}
|
|
|
|
delete otherConnection;
|
|
otherConnection= NULL;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int testApiFailReq(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Perform a number of iterations, connecting,
|
|
* sending lots of PK updates, inserting error
|
|
* and then causing node failure
|
|
*/
|
|
Uint32 iterations = 10;
|
|
int rc = NDBT_OK;
|
|
|
|
while (iterations --)
|
|
{
|
|
rc= testApiFailReqImpl(ctx, step);
|
|
|
|
if (rc == NDBT_FAILED)
|
|
{
|
|
break;
|
|
}
|
|
} // while(iterations --)
|
|
|
|
/* Avoid PkRead worker threads getting stuck */
|
|
ctx->setProperty(ApiFailTestComplete, (Uint32) 1);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int runBulkPkReads(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Run batched Pk reads */
|
|
|
|
while(true)
|
|
{
|
|
/* Wait to be signalled to start running */
|
|
while ((ctx->getProperty(ApiFailTestRun) == 0) &&
|
|
(ctx->getProperty(ApiFailTestComplete) == 0) &&
|
|
!ctx->isTestStopped())
|
|
{
|
|
ctx->wait_timeout(500); /* 500 millis */
|
|
}
|
|
|
|
if (ctx->isTestStopped() ||
|
|
(ctx->getProperty(ApiFailTestComplete) != 0))
|
|
{
|
|
/* Asked to stop by main test thread */
|
|
return NDBT_OK;
|
|
}
|
|
/* Indicate that we're underway */
|
|
ctx->incProperty(ApiFailTestsRunning);
|
|
|
|
Ndb* otherNdb = stepNdbs[step->getStepNo()];
|
|
HugoOperations hugoOps(*ctx->getTab());
|
|
Uint32 numRecords = ctx->getNumRecords();
|
|
Uint32 batchSize = (1000 < numRecords)? 1000 : numRecords;
|
|
|
|
ndbout << "Step number " << step->getStepNo()
|
|
<< " reading batches of " << batchSize
|
|
<< " rows " << endl;
|
|
|
|
while(true)
|
|
{
|
|
if (hugoOps.startTransaction(otherNdb) != 0)
|
|
{
|
|
if (otherNdb->getNdbError().code == 4009)
|
|
{
|
|
/* Api disconnect sometimes manifests as Cluster failure
|
|
* from API's point of view as it cannot seize() a
|
|
* transaction from any Ndbd node
|
|
* We treat this the same way as the later error cases
|
|
*/
|
|
break;
|
|
}
|
|
|
|
ndbout << "Failed to start transaction. Error : "
|
|
<< otherNdb->getNdbError().message << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (Uint32 op = 0; op < batchSize; op++)
|
|
{
|
|
if (hugoOps.pkReadRecord(otherNdb,
|
|
op) != 0)
|
|
{
|
|
ndbout << "Failed to define read of record number " << op << endl;
|
|
ndbout << "Error : " << hugoOps.getTransaction()->getNdbError().message
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
if (hugoOps.execute_Commit(otherNdb) != 0)
|
|
{
|
|
NdbError err = hugoOps.getTransaction()->getNdbError();
|
|
ndbout << "Execute failed with Error : "
|
|
<< err.message
|
|
<< endl;
|
|
|
|
hugoOps.closeTransaction(otherNdb);
|
|
|
|
if ((err.code == 4002) || // send failed
|
|
(err.code == 4010) || // Node failure
|
|
(err.code == 4025) || // Node failure
|
|
(err.code == 1218)) // Send buffer overload (reading larger tables)
|
|
{
|
|
/* Expected scenario due to injected Api disconnect
|
|
* If there was a node failure due to assertion failure
|
|
* then we'll detect it when we try to setup a new
|
|
* connection
|
|
*/
|
|
break;
|
|
}
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
hugoOps.closeTransaction(otherNdb);
|
|
}
|
|
|
|
/* Signal that we've finished running this iteration */
|
|
ctx->decProperty(ApiFailTestsRunning);
|
|
}
|
|
|
|
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()){
|
|
NDB_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){
|
|
NDB_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();
|
|
NDB_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();
|
|
NDB_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;
|
|
}
|
|
|
|
class TransGuard
|
|
{
|
|
NdbTransaction* pTrans;
|
|
public:
|
|
TransGuard(NdbTransaction * p) : pTrans(p) {}
|
|
~TransGuard() { if (pTrans) pTrans->close(); pTrans = 0; }
|
|
};
|
|
|
|
int
|
|
runBug51775(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
NdbTransaction * pTrans1 = pNdb->startTransaction();
|
|
if (pTrans1 == NULL)
|
|
{
|
|
NDB_ERR(pNdb->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
TransGuard g1(pTrans1);
|
|
|
|
NdbTransaction * pTrans2 = pNdb->startTransaction();
|
|
if (pTrans2 == NULL)
|
|
{
|
|
pTrans1->close();
|
|
NDB_ERR(pNdb->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
TransGuard g2(pTrans2);
|
|
|
|
{
|
|
NdbOperation * pOp = pTrans1->getNdbOperation(ctx->getTab()->getName());
|
|
if (pOp == NULL)
|
|
{
|
|
NDB_ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->insertTuple() != 0)
|
|
{
|
|
NDB_ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
HugoOperations hugoOps(* ctx->getTab());
|
|
hugoOps.setValues(pOp, 0, 0);
|
|
}
|
|
|
|
{
|
|
NdbOperation * pOp = pTrans2->getNdbOperation(ctx->getTab()->getName());
|
|
if (pOp == NULL)
|
|
{
|
|
NDB_ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (pOp->readTuple() != 0)
|
|
{
|
|
NDB_ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
HugoOperations hugoOps(* ctx->getTab());
|
|
hugoOps.equalForRow(pOp, 0);
|
|
pOp->getValue(NdbDictionary::Column::FRAGMENT);
|
|
}
|
|
|
|
|
|
pTrans1->execute(NoCommit); // We now have un uncommitted insert
|
|
|
|
/**
|
|
* Now send a read...which will get 266
|
|
*/
|
|
pTrans2->executeAsynch(NoCommit, 0, 0);
|
|
int res = pNdb->pollNdb(1, 1000);
|
|
ndbout_c("res: %u", res);
|
|
|
|
NdbSleep_SecSleep(10);
|
|
ndbout_c("pollNdb()");
|
|
while (pNdb->pollNdb() + res == 0);
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int setupOtherConnection(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Setup a separate connection for running operations
|
|
* that can be disconnected without affecting
|
|
* the test framework
|
|
*/
|
|
if (otherConnection != NULL)
|
|
{
|
|
g_err.println("otherConnection not null");
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
char connectString[256];
|
|
ctx->m_cluster_connection.get_connectstring(connectString,
|
|
sizeof(connectString));
|
|
|
|
otherConnection= new Ndb_cluster_connection(connectString);
|
|
|
|
if (otherConnection == NULL)
|
|
{
|
|
g_err.println("otherConnection is null");
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int rc= otherConnection->connect();
|
|
|
|
if (rc!= 0)
|
|
{
|
|
g_err.println("Connect failed with rc %d", rc);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* Check that all nodes are alive */
|
|
if (otherConnection->wait_until_ready(10,10) != 0)
|
|
{
|
|
g_err.println("Cluster connection was not ready");
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int tearDownOtherConnection(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
if (otherConnection == NULL)
|
|
{
|
|
g_err << "otherConnection is NULL" << endl;
|
|
return NDBT_OK;
|
|
}
|
|
|
|
delete otherConnection;
|
|
otherConnection = NULL;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int testFragmentedApiFailImpl(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Setup a separate connection for running scan operations
|
|
* that will be disconnected without affecting
|
|
* the test framework
|
|
*/
|
|
if (setupOtherConnection(ctx, step) != NDBT_OK)
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (int i=0; i < MAX_STEPS; i++)
|
|
{
|
|
/* We must create the Ndb objects here as we
|
|
* are still single threaded
|
|
*/
|
|
stepNdbs[i]= new Ndb(otherConnection,
|
|
"TEST_DB");
|
|
stepNdbs[i]->init();
|
|
int rc= stepNdbs[i]->waitUntilReady(10);
|
|
|
|
if (rc != 0)
|
|
{
|
|
g_err.println("FragApiFail : Ndb %d was not ready", i);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
}
|
|
|
|
/* Now signal the 'worker' threads to start sending Pk
|
|
* reads
|
|
*/
|
|
ctx->setProperty(ApiFailTestRun, 1);
|
|
|
|
/* Wait until all of them are running before proceeding */
|
|
ctx->getPropertyWait(ApiFailTestsRunning,
|
|
ctx->getProperty(ApiFailNumberPkSteps));
|
|
|
|
if (ctx->isTestStopped())
|
|
{
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/* Clear the test-run flag so that they'll wait after
|
|
* they hit an error
|
|
*/
|
|
ctx->setProperty(ApiFailTestRun, (Uint32)0);
|
|
|
|
/* Wait a little */
|
|
sleep(1);
|
|
|
|
/* Now cause our connection to disconnect
|
|
* This results in NDBD running API failure
|
|
* code and cleaning up any in-assembly fragmented
|
|
* signals
|
|
*/
|
|
int otherNodeId = otherConnection->node_id();
|
|
|
|
g_info.println("FragApiFail : Forcing disconnect of node %u", otherNodeId);
|
|
|
|
/* All dump 900 <nodeId> */
|
|
int args[2]= {900, otherNodeId};
|
|
|
|
NdbRestarter restarter;
|
|
restarter.dumpStateAllNodes( args, 2 );
|
|
|
|
/* Now wait for all workers to finish
|
|
* (Running worker count to get down to zero
|
|
*/
|
|
ctx->getPropertyWait(ApiFailTestsRunning, (Uint32)0);
|
|
|
|
if (ctx->isTestStopped())
|
|
{
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/* Clean up allocated resources */
|
|
for (int i= 0; i < MAX_STEPS; i++)
|
|
{
|
|
delete stepNdbs[i];
|
|
stepNdbs[i]= NULL;
|
|
}
|
|
|
|
tearDownOtherConnection(ctx, step);
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int testFragmentedApiFail(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Perform a number of iterations, connecting,
|
|
* sending lots of PK updates, inserting error
|
|
* and then causing node failure
|
|
*/
|
|
Uint32 iterations = 10;
|
|
int rc = NDBT_OK;
|
|
|
|
while (iterations --)
|
|
{
|
|
rc= testFragmentedApiFailImpl(ctx, step);
|
|
|
|
if (rc == NDBT_FAILED)
|
|
{
|
|
break;
|
|
}
|
|
} // while(iterations --)
|
|
|
|
/* Avoid scan worker threads getting stuck */
|
|
ctx->setProperty(ApiFailTestComplete, (Uint32) 1);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int runFragmentedScanOtherApi(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* We run a loop sending large scan requests that will be
|
|
* fragmented.
|
|
* The requests are so large that they actually fail on
|
|
* arrival at TUP as there is too much ATTRINFO
|
|
* That doesn't affect this testcase though, as it is
|
|
* testing TC cleanup of fragmented signals from a
|
|
* failed API
|
|
*/
|
|
/* SEND > ((2 * MAX_SEND_MESSAGE_BYTESIZE) + SOME EXTRA)
|
|
* This way we get at least 3 fragments
|
|
* However, as this is generally > 64kB, it's too much AttrInfo for
|
|
* a ScanTabReq, so the 'success' case returns error 874
|
|
*/
|
|
const Uint32 PROG_WORDS= 16500;
|
|
|
|
/* Use heap rather than stack as stack is too small in
|
|
* STEP thread
|
|
*/
|
|
Uint32* buff= new Uint32[ PROG_WORDS + 10 ]; // 10 extra for final 'return' etc.
|
|
Uint32 stepNo = step->getStepNo();
|
|
|
|
while(true)
|
|
{
|
|
/* Wait to be signalled to start running */
|
|
while ((ctx->getProperty(ApiFailTestRun) == 0) &&
|
|
(ctx->getProperty(ApiFailTestComplete) == 0) &&
|
|
!ctx->isTestStopped())
|
|
{
|
|
ctx->wait_timeout(500); /* 500 millis */
|
|
}
|
|
|
|
if (ctx->isTestStopped() ||
|
|
(ctx->getProperty(ApiFailTestComplete) != 0))
|
|
{
|
|
g_info.println("%u: Test stopped, exiting thread", stepNo);
|
|
/* Asked to stop by main test thread */
|
|
delete[] buff;
|
|
return NDBT_OK;
|
|
}
|
|
/* Indicate that we're underway */
|
|
ctx->incProperty(ApiFailTestsRunning);
|
|
|
|
Ndb* otherNdb = stepNdbs[stepNo];
|
|
|
|
while (true)
|
|
{
|
|
/* Start a transaction */
|
|
NdbTransaction* trans= otherNdb->startTransaction();
|
|
if (!trans)
|
|
{
|
|
const NdbError err = otherNdb->getNdbError();
|
|
|
|
/* During this test, if we attempt to get a transaction
|
|
* when the API is disconnected, we can get error 4009
|
|
* (Cluster failure) or 4035 (Cluster temporarily unavailable).
|
|
* We treat this similarly to the
|
|
* "Node failure caused abort of transaction" case
|
|
*/
|
|
if (err.code == 4009 || err.code == 4035)
|
|
{
|
|
g_info.println("%u: Failed to start transaction from Ndb object Error : %u %s",
|
|
stepNo, err.code, err.message);
|
|
break;
|
|
}
|
|
g_err.println("ERR: %u: %u: Failed to start transaction from Ndb object Error : %u %s",
|
|
__LINE__, stepNo, err.code, err.message);
|
|
delete[] buff;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* scan= trans->getNdbScanOperation(ctx->getTab());
|
|
|
|
if (scan == NULL)
|
|
{
|
|
/* getNdbScanOperation can fail in same way as startTransaction
|
|
* since it starts a buddy transaction for scan operations.
|
|
*/
|
|
const NdbError err = trans->getNdbError();
|
|
if (err.code == 4009 || err.code == 4035)
|
|
{
|
|
g_info.println("%u: Failed to get scan operation transaction Error : %u %s",
|
|
stepNo, err.code, err.message);
|
|
trans->close();
|
|
break;
|
|
}
|
|
g_err.println("ERR: %u: %u: Failed to get scan operation transaction Error : %u %s",
|
|
__LINE__, stepNo, err.code, err.message);
|
|
trans->close();
|
|
delete[] buff;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
CHECK(0 == scan->readTuples());
|
|
|
|
/* Create a large program, to give a large SCANTABREQ */
|
|
NdbInterpretedCode prog(ctx->getTab(),
|
|
buff, PROG_WORDS + 10);
|
|
|
|
for (Uint32 w=0; w < PROG_WORDS; w++)
|
|
CHECK(0 == prog.load_const_null(1));
|
|
|
|
CHECK(0 == prog.interpret_exit_ok());
|
|
CHECK(0 == prog.finalise());
|
|
|
|
CHECK(0 == scan->setInterpretedCode(&prog));
|
|
|
|
int ret = trans->execute(NdbTransaction::NoCommit);
|
|
|
|
const NdbError execError= trans->getNdbError();
|
|
|
|
if (ret != 0)
|
|
{
|
|
/* Transaction was aborted. Should be due to node disconnect. */
|
|
if(execError.classification != NdbError::NodeRecoveryError)
|
|
{
|
|
g_err.println("ERR: %u: %u: Execute aborted transaction with invalid error code: %u",
|
|
__LINE__, stepNo, execError.code);
|
|
NDB_ERR_OUT(g_err, execError);
|
|
trans->close();
|
|
delete[] buff;
|
|
return NDBT_FAILED;
|
|
}
|
|
g_info.println("%u: Execute aborted transaction with NR error code: %u",
|
|
stepNo, execError.code);
|
|
trans->close();
|
|
break;
|
|
}
|
|
|
|
/* Can get success (0), or 874 for too much AttrInfo, depending
|
|
* on timing
|
|
*/
|
|
if ((execError.code != 0) &&
|
|
(execError.code != 874) &&
|
|
(execError.code != 4002))
|
|
{
|
|
g_err.println("ERR: %u: %u: incorrect error code: %u", __LINE__, stepNo, execError.code);
|
|
NDB_ERR_OUT(g_err, execError);
|
|
trans->close();
|
|
delete[] buff;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* nextResult will always fail */
|
|
CHECK(-1 == scan->nextResult());
|
|
|
|
NdbError scanError= scan->getNdbError();
|
|
|
|
/* 'Success case' is 874 for too much AttrInfo */
|
|
if (scanError.code != 874)
|
|
{
|
|
/* When disconnected, we get should get a node failure related error */
|
|
if (scanError.classification == NdbError::NodeRecoveryError)
|
|
{
|
|
g_info.println("%u: Scan failed due to node failure/disconnect with error code %u",
|
|
stepNo, scanError.code);
|
|
trans->close();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
g_err.println("ERR: %u: %u: incorrect error code: %u", __LINE__, stepNo, scanError.code);
|
|
NDB_ERR_OUT(g_err, scanError);
|
|
trans->close();
|
|
delete[] buff;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
scan->close();
|
|
|
|
trans->close();
|
|
} // while (true)
|
|
|
|
/* Node failure case - as expected */
|
|
g_info.println("%u: Scan thread finished iteration", stepNo);
|
|
|
|
/* Signal that we've finished running this iteration */
|
|
ctx->decProperty(ApiFailTestsRunning);
|
|
}
|
|
|
|
delete[] buff;
|
|
return NDBT_OK;
|
|
}
|
|
|
|
void outputLockMode(NdbOperation::LockMode lm)
|
|
{
|
|
switch(lm)
|
|
{
|
|
case NdbOperation::LM_Exclusive:
|
|
ndbout << "LM_Exclusive";
|
|
break;
|
|
case NdbOperation::LM_Read:
|
|
ndbout << "LM_Read";
|
|
break;
|
|
case NdbOperation::LM_SimpleRead:
|
|
ndbout << "LM_SimpleRead";
|
|
break;
|
|
case NdbOperation::LM_CommittedRead:
|
|
ndbout << "LM_CommittedRead";
|
|
break;
|
|
}
|
|
}
|
|
|
|
NdbOperation::LockMode chooseLockMode(bool onlyRealLocks = false)
|
|
{
|
|
Uint32 choice;
|
|
|
|
if (onlyRealLocks)
|
|
{
|
|
choice = rand() % 2;
|
|
}
|
|
else
|
|
{
|
|
choice = rand() % 4;
|
|
}
|
|
|
|
NdbOperation::LockMode lm = NdbOperation::LM_Exclusive;
|
|
|
|
switch(choice)
|
|
{
|
|
case 0:
|
|
lm = NdbOperation::LM_Exclusive;
|
|
break;
|
|
case 1:
|
|
lm = NdbOperation::LM_Read;
|
|
break;
|
|
case 2:
|
|
lm = NdbOperation::LM_SimpleRead;
|
|
break;
|
|
case 3:
|
|
default:
|
|
lm = NdbOperation::LM_CommittedRead;
|
|
break;
|
|
}
|
|
|
|
outputLockMode(lm);
|
|
ndbout << endl;
|
|
|
|
return lm;
|
|
}
|
|
|
|
NdbOperation::LockMode chooseConflictingLockMode(NdbOperation::LockMode lm)
|
|
{
|
|
NdbOperation::LockMode conflicting = NdbOperation::LM_Exclusive;
|
|
|
|
switch (lm)
|
|
{
|
|
case NdbOperation::LM_Exclusive:
|
|
conflicting = (((rand() % 2) == 0) ?
|
|
NdbOperation::LM_Exclusive :
|
|
NdbOperation::LM_Read);
|
|
|
|
break;
|
|
case NdbOperation::LM_Read:
|
|
conflicting = NdbOperation::LM_Exclusive;
|
|
break;
|
|
default:
|
|
abort(); // SimpleRead + CommittedRead can't conflict reliably
|
|
}
|
|
|
|
ndbout << "conflicting with ";
|
|
outputLockMode(lm);
|
|
ndbout << " using ";
|
|
outputLockMode(conflicting);
|
|
ndbout << endl;
|
|
return conflicting;
|
|
}
|
|
|
|
#define CHECKN(c, o, e) { if (!(c)) { \
|
|
ndbout << "Failed on line " << __LINE__ << endl; \
|
|
ndbout << (o)->getNdbError() << endl; \
|
|
return e; } }
|
|
|
|
NdbOperation* defineReadAllColsOp(HugoOperations* hugoOps,
|
|
NdbTransaction* trans,
|
|
const NdbDictionary::Table* pTab,
|
|
NdbOperation::LockMode lm,
|
|
Uint32 rowNum)
|
|
{
|
|
NdbOperation* op = trans->getNdbOperation(pTab);
|
|
CHECKN(op != NULL, trans, NULL);
|
|
|
|
CHECKN(op->readTuple(lm) == 0, op, NULL);
|
|
|
|
hugoOps->equalForRow(op, rowNum);
|
|
|
|
for(int c = 0; c < pTab->getNoOfColumns(); c++)
|
|
{
|
|
if(!pTab->getColumn(c)->getPrimaryKey())
|
|
{
|
|
CHECKN(op->getValue(pTab->getColumn(c)->getName()) != NULL, op, NULL);
|
|
}
|
|
}
|
|
|
|
return op;
|
|
}
|
|
|
|
bool checkReadRc(HugoOperations* hugoOps,
|
|
Ndb* ndb,
|
|
const NdbDictionary::Table* pTab,
|
|
NdbOperation::LockMode lm,
|
|
Uint32 rowNum,
|
|
int expectedRc)
|
|
{
|
|
NdbTransaction* trans = ndb->startTransaction();
|
|
CHECKN(trans != NULL, ndb, false);
|
|
|
|
NdbOperation* readOp = defineReadAllColsOp(hugoOps,
|
|
trans,
|
|
pTab,
|
|
lm,
|
|
rowNum);
|
|
CHECKN(readOp != NULL, trans, false);
|
|
|
|
int execRc = trans->execute(Commit);
|
|
|
|
if (expectedRc)
|
|
{
|
|
/* Here we assume that the error is on the transaction
|
|
* which may not be the case for some errors
|
|
*/
|
|
if (trans->getNdbError().code != expectedRc)
|
|
{
|
|
ndbout << "Expected " << expectedRc << " at " << __LINE__ << endl;
|
|
ndbout << "Got " << trans->getNdbError() << endl;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CHECKN(execRc == 0, trans, false);
|
|
CHECKN(readOp->getNdbError().code == 0, readOp, false);
|
|
}
|
|
|
|
trans->close();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool checkReadDeadlocks(HugoOperations* hugoOps,
|
|
Ndb* ndb,
|
|
const NdbDictionary::Table* pTab,
|
|
NdbOperation::LockMode lm,
|
|
Uint32 rowNum)
|
|
{
|
|
return checkReadRc(hugoOps, ndb, pTab, lm, rowNum, 266);
|
|
}
|
|
|
|
bool checkReadSucceeds(HugoOperations* hugoOps,
|
|
Ndb* ndb,
|
|
const NdbDictionary::Table* pTab,
|
|
NdbOperation::LockMode lm,
|
|
Uint32 rowNum)
|
|
{
|
|
return checkReadRc(hugoOps, ndb, pTab, lm, rowNum, 0);
|
|
}
|
|
|
|
int runTestUnlockBasic(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Basic tests that we can lock and unlock rows
|
|
* using the unlock mechanism
|
|
* Some minor side-validation that the API rejects
|
|
* readLockInfo for non Exclusive / Shared lock modes
|
|
* and that double-release of the lockhandle is caught
|
|
*/
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
const Uint32 iterations = 200;
|
|
|
|
for (Uint32 iter = 0; iter < iterations; iter++)
|
|
{
|
|
Uint32 rowNum = iter % ctx->getNumRecords();
|
|
|
|
NdbTransaction* trans = GETNDB(step)->startTransaction();
|
|
CHECKN(trans != NULL, GETNDB(step), NDBT_FAILED);
|
|
|
|
ndbout << "First transaction operation using ";
|
|
NdbOperation::LockMode lm = chooseLockMode();
|
|
|
|
NdbOperation* op = defineReadAllColsOp(&hugoOps,
|
|
trans,
|
|
pTab,
|
|
lm,
|
|
rowNum);
|
|
CHECKN(op != NULL, trans, NDBT_FAILED);
|
|
|
|
if (op->getLockHandle() == NULL)
|
|
{
|
|
if ((lm == NdbOperation::LM_CommittedRead) ||
|
|
(lm == NdbOperation::LM_SimpleRead))
|
|
{
|
|
if (op->getNdbError().code == 4549)
|
|
{
|
|
/* As expected, go to next iteration */
|
|
ndbout << "Definition error as expected, moving to next" << endl;
|
|
trans->close();
|
|
continue;
|
|
}
|
|
ndbout << "Expected 4549, got :" << endl;
|
|
}
|
|
ndbout << op->getNdbError() << endl;
|
|
ndbout << " at "<<__FILE__ << ":" <<__LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
CHECKN(trans->execute(NoCommit) == 0, trans, NDBT_FAILED);
|
|
|
|
const NdbLockHandle* lh = op->getLockHandle();
|
|
CHECKN(lh != NULL, op, NDBT_FAILED);
|
|
|
|
/* Ok, let's use another transaction to try and get a
|
|
* lock on the row (exclusive or shared)
|
|
*/
|
|
NdbTransaction* trans2 = GETNDB(step)->startTransaction();
|
|
CHECKN(trans2 != NULL, GETNDB(step), NDBT_FAILED);
|
|
|
|
|
|
ndbout << "Second transaction operation using ";
|
|
NdbOperation::LockMode lm2 = chooseLockMode();
|
|
|
|
NdbOperation* op2 = defineReadAllColsOp(&hugoOps,
|
|
trans2,
|
|
pTab,
|
|
lm2,
|
|
rowNum);
|
|
CHECKN(op2 != NULL, trans2, NDBT_FAILED);
|
|
|
|
/* Execute can succeed if both lock modes are LM read
|
|
* otherwise we'll deadlock (266)
|
|
*/
|
|
bool expectOk = ((lm2 == NdbOperation::LM_CommittedRead) ||
|
|
((lm == NdbOperation::LM_Read) &&
|
|
((lm2 == NdbOperation::LM_Read) ||
|
|
(lm2 == NdbOperation::LM_SimpleRead))));
|
|
|
|
/* Exclusive read locks primary only, and SimpleRead locks
|
|
* Primary or Backup, so SimpleRead may or may not succeed
|
|
*/
|
|
bool unknownCase = ((lm == NdbOperation::LM_Exclusive) &&
|
|
(lm2 == NdbOperation::LM_SimpleRead));
|
|
|
|
if (trans2->execute(NoCommit) != 0)
|
|
{
|
|
if (expectOk ||
|
|
(trans2->getNdbError().code != 266))
|
|
{
|
|
ndbout << trans2->getNdbError() << endl;
|
|
ndbout << " at "<<__FILE__ << ":" <<__LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!expectOk && !unknownCase)
|
|
{
|
|
ndbout << "Expected deadlock but had success!" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
trans2->close();
|
|
|
|
/* Now let's try to create an unlockRow operation, and
|
|
* execute it
|
|
*/
|
|
const NdbOperation* unlockOp = trans->unlock(lh);
|
|
|
|
CHECKN(unlockOp != NULL, trans, NDBT_FAILED);
|
|
|
|
CHECKN(trans->execute(NoCommit) == 0, trans, NDBT_FAILED);
|
|
|
|
/* Now let's try to get an exclusive lock on the row from
|
|
* another transaction which can only be possible if the
|
|
* original lock has been removed.
|
|
*/
|
|
CHECK(checkReadSucceeds(&hugoOps,
|
|
GETNDB(step),
|
|
pTab,
|
|
NdbOperation::LM_Exclusive,
|
|
rowNum));
|
|
ndbout << "Third transaction operation using LM_Exclusive succeeded" << endl;
|
|
|
|
Uint32 choice = rand() % 3;
|
|
switch(choice)
|
|
{
|
|
case 0:
|
|
ndbout << "Closing transaction" << endl;
|
|
trans->close();
|
|
break;
|
|
case 1:
|
|
ndbout << "Releasing handle and closing transaction" << endl;
|
|
CHECKN(trans->releaseLockHandle(lh) == 0, trans, NDBT_FAILED);
|
|
trans->close();
|
|
break;
|
|
case 2:
|
|
ndbout << "Attempting to release the handle twice" << endl;
|
|
CHECKN(trans->releaseLockHandle(lh) == 0, trans, NDBT_FAILED);
|
|
|
|
if ((trans->releaseLockHandle(lh) != -1) ||
|
|
(trans->getNdbError().code != 4551))
|
|
{
|
|
ndbout << "Expected 4551, but got no error " << endl;
|
|
ndbout << " at "<<__FILE__ << ":" <<__LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
trans->close();
|
|
break;
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
} // for (Uint32 iter
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runTestUnlockRepeat(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Transaction A locks 2 rows
|
|
* It repeatedly unlocks and re-locks one row, but leaves
|
|
* the other locked
|
|
* Transaction B verifies that it can only lock the unlocked
|
|
* row when it is unlocked, and can never lock the row which
|
|
* is never unlocked!
|
|
*/
|
|
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
const Uint32 outerLoops = 2;
|
|
const Uint32 iterations = 10;
|
|
|
|
Ndb* ndb = GETNDB(step);
|
|
|
|
/* Transaction A will take a lock on otherRowNum and hold it
|
|
* throughout.
|
|
* RowNum will be locked and unlocked each iteration
|
|
*/
|
|
Uint32 otherRowNum = ctx->getNumRecords() - 1;
|
|
|
|
for (Uint32 outerLoop = 0; outerLoop < outerLoops; outerLoop ++)
|
|
{
|
|
NdbTransaction* transA = ndb->startTransaction();
|
|
CHECKN(transA != NULL, ndb, NDBT_FAILED);
|
|
|
|
NdbOperation::LockMode lockAOtherMode;
|
|
ndbout << "TransA : Try to lock otherRowNum in mode ";
|
|
|
|
switch (outerLoop % 2) {
|
|
case 0:
|
|
ndbout << "LM_Exclusive" << endl;
|
|
lockAOtherMode = NdbOperation::LM_Exclusive;
|
|
break;
|
|
default:
|
|
ndbout << "LM_Read" << endl;
|
|
lockAOtherMode = NdbOperation::LM_Read;
|
|
break;
|
|
}
|
|
|
|
NdbOperation* lockAOtherRowNum = defineReadAllColsOp(&hugoOps,
|
|
transA,
|
|
pTab,
|
|
lockAOtherMode,
|
|
otherRowNum);
|
|
CHECKN(lockAOtherRowNum != NULL, transA, NDBT_FAILED);
|
|
|
|
CHECKN(transA->execute(NoCommit) == 0, transA, NDBT_FAILED);
|
|
|
|
ndbout << "TransA : Got initial lock on otherRowNum" << endl;
|
|
|
|
for (Uint32 iter = 0; iter < iterations; iter++)
|
|
{
|
|
Uint32 rowNum = iter % (ctx->getNumRecords() - 1);
|
|
|
|
ndbout << " TransA : Try to lock rowNum with mode ";
|
|
NdbOperation::LockMode lockAMode = chooseLockMode(true); // Exclusive or LM_Read
|
|
|
|
/* Transaction A takes a lock on rowNum */
|
|
NdbOperation* lockARowNum = defineReadAllColsOp(&hugoOps,
|
|
transA,
|
|
pTab,
|
|
lockAMode,
|
|
rowNum);
|
|
CHECKN(lockARowNum != NULL, transA, NDBT_FAILED);
|
|
|
|
const NdbLockHandle* lockAHandle = lockARowNum->getLockHandle();
|
|
CHECKN(lockAHandle != NULL, lockARowNum, NDBT_FAILED);
|
|
|
|
CHECKN(transA->execute(NoCommit) == 0, transA, NDBT_FAILED);
|
|
|
|
ndbout << " TransA : Got lock on rowNum" << endl;
|
|
|
|
/* Now transaction B checks that it cannot get a conflicting lock
|
|
* on rowNum
|
|
*/
|
|
ndbout << " TransB : Try to lock rowNum by ";
|
|
|
|
CHECK(checkReadDeadlocks(&hugoOps,
|
|
ndb,
|
|
pTab,
|
|
chooseConflictingLockMode(lockAMode),
|
|
rowNum));
|
|
|
|
ndbout << " TransB : Failed to get lock on rowNum as expected" << endl;
|
|
|
|
/* Now transaction A unlocks rowNum */
|
|
const NdbOperation* unlockOpA = transA->unlock(lockAHandle);
|
|
CHECKN(unlockOpA != NULL, transA, NDBT_FAILED);
|
|
|
|
CHECKN(transA->execute(NoCommit) == 0, transA, NDBT_FAILED);
|
|
|
|
ndbout << " TransA : Unlocked rowNum" << endl;
|
|
|
|
/* Now transaction B attempts to gain a lock on RowNum */
|
|
NdbTransaction* transB = ndb->startTransaction();
|
|
CHECKN(transB != NULL, ndb, NDBT_FAILED);
|
|
|
|
ndbout << " TransB : Try to lock rowNum with mode ";
|
|
NdbOperation::LockMode lockBMode = chooseLockMode(true);
|
|
|
|
NdbOperation* tryLockBRowNum2 = defineReadAllColsOp(&hugoOps,
|
|
transB,
|
|
pTab,
|
|
lockBMode,
|
|
rowNum);
|
|
CHECKN(tryLockBRowNum2 != NULL, transB, NDBT_FAILED);
|
|
|
|
CHECKN(transB->execute(NoCommit) == 0, transB, NDBT_FAILED);
|
|
|
|
ndbout << " TransB : Got lock on rowNum" << endl;
|
|
|
|
ndbout << " TransB : Try to lock other row by ";
|
|
NdbOperation::LockMode lockBOtherMode = chooseConflictingLockMode(lockAOtherMode);
|
|
|
|
/* Now transaction B attempts to gain a lock on OtherRowNum
|
|
* which should fail as transaction A still has it locked
|
|
*/
|
|
NdbOperation* tryLockBOtherRowNum = defineReadAllColsOp(&hugoOps,
|
|
transB,
|
|
pTab,
|
|
lockBOtherMode,
|
|
otherRowNum);
|
|
CHECKN(tryLockBOtherRowNum != NULL, transB, NDBT_FAILED);
|
|
|
|
CHECKN(transB->execute(NoCommit) == -1, transB, NDBT_FAILED);
|
|
|
|
if (transB->getNdbError().code != 266)
|
|
{
|
|
ndbout << "Error was expecting 266, but got " << transB->getNdbError() << endl;
|
|
ndbout << "At line " << __LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
ndbout << " TransB : Failed to get lock on otherRowNum as expected" << endl;
|
|
|
|
transB->close();
|
|
}
|
|
|
|
transA->close();
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runTestUnlockMulti(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
/* Verifies that a single transaction (or multiple
|
|
* transactions) taking multiple locks on the same
|
|
* row using multiple operations behaves correctly
|
|
* as the operations unlock their locks.
|
|
*
|
|
* Transaction A will lock the row to depth A
|
|
* Transaction A may use an exclusive lock as its first lock
|
|
* Transaction B will lock the row to depth B
|
|
* iff transaction A did not use exclusive locks
|
|
*
|
|
* Once all locks are in place, the locks placed are
|
|
* removed.
|
|
* The code checks that the row remains locked until
|
|
* all locking operations are unlocked
|
|
* The code checks that the row is unlocked when all
|
|
* locking operations are unlocked.
|
|
*
|
|
* Depth A and B and whether A uses exclusive or not
|
|
* are varied.
|
|
*/
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
const Uint32 MinLocks = 3;
|
|
const Uint32 MaxLocksPerTrans = 20;
|
|
Uint32 rowNum = ctx->getNumRecords() - 1;
|
|
Uint32 numLocksInTransA = rand() % MaxLocksPerTrans;
|
|
numLocksInTransA = (numLocksInTransA > MinLocks) ?
|
|
numLocksInTransA : MinLocks;
|
|
bool useExclusiveInA = ((rand() % 2) == 0);
|
|
|
|
Uint32 numLocksInTransB = useExclusiveInA ? 0 :
|
|
(rand() % MaxLocksPerTrans);
|
|
|
|
Uint32 maxLocks = (numLocksInTransA > numLocksInTransB) ?
|
|
numLocksInTransA : numLocksInTransB;
|
|
|
|
ndbout << "NumLocksInTransA " << numLocksInTransA
|
|
<< " NumLocksInTransB " << numLocksInTransB
|
|
<< " useExclusiveInA " << useExclusiveInA
|
|
<< endl;
|
|
|
|
NdbOperation* transAOps[ MaxLocksPerTrans ];
|
|
NdbOperation* transBOps[ MaxLocksPerTrans ];
|
|
|
|
/* First the lock phase when transA and transB
|
|
* claim locks (with LockHandles)
|
|
* As this occurs, transC attempts to obtain
|
|
* a conflicting lock and fails.
|
|
*/
|
|
Ndb* ndb = GETNDB(step);
|
|
|
|
NdbTransaction* transA = ndb->startTransaction();
|
|
CHECKN(transA != NULL, ndb, NDBT_FAILED);
|
|
|
|
NdbTransaction* transB = ndb->startTransaction();
|
|
CHECKN(transB != NULL, ndb, NDBT_FAILED);
|
|
|
|
ndbout << "Locking phase" << endl << endl;
|
|
for(Uint32 depth=0; depth < maxLocks; depth++)
|
|
{
|
|
ndbout << "Depth " << depth << endl;
|
|
NdbOperation::LockMode lmA;
|
|
/* TransA */
|
|
if (depth < numLocksInTransA)
|
|
{
|
|
ndbout << " TransA : Locking with mode ";
|
|
if ((depth == 0) && useExclusiveInA)
|
|
{
|
|
lmA = NdbOperation::LM_Exclusive;
|
|
ndbout << "LM_Exclusive" << endl;
|
|
}
|
|
else if (!useExclusiveInA)
|
|
{
|
|
lmA = NdbOperation::LM_Read;
|
|
ndbout << "LM_Read" << endl;
|
|
}
|
|
else
|
|
{
|
|
lmA = chooseLockMode(true); // LM_Exclusive or LM_Read;
|
|
}
|
|
|
|
NdbOperation* lockA = defineReadAllColsOp(&hugoOps,
|
|
transA,
|
|
pTab,
|
|
lmA,
|
|
rowNum);
|
|
CHECKN(lockA != NULL, transA, NDBT_FAILED);
|
|
CHECKN(lockA->getLockHandle() != NULL, lockA, NDBT_FAILED);
|
|
|
|
transAOps[ depth ] = lockA;
|
|
|
|
CHECKN(transA->execute(NoCommit) == 0, transA, NDBT_FAILED);
|
|
ndbout << " TransA : Succeeded" << endl;
|
|
}
|
|
|
|
/* TransB */
|
|
if (depth < numLocksInTransB)
|
|
{
|
|
ndbout << " TransB : Locking with mode LM_Read" << endl;
|
|
|
|
NdbOperation* lockB = defineReadAllColsOp(&hugoOps,
|
|
transB,
|
|
pTab,
|
|
NdbOperation::LM_Read,
|
|
rowNum);
|
|
CHECKN(lockB != NULL, transB, NDBT_FAILED);
|
|
CHECKN(lockB->getLockHandle() != NULL, lockB, NDBT_FAILED);
|
|
|
|
transBOps[ depth ] = lockB;
|
|
|
|
CHECKN(transB->execute(NoCommit) == 0, transB, NDBT_FAILED);
|
|
ndbout << " TransB : Succeeded" << endl;
|
|
}
|
|
}
|
|
|
|
ndbout << "Unlocking phase" << endl << endl;
|
|
|
|
for(Uint32 depth = 0; depth < maxLocks; depth++)
|
|
{
|
|
Uint32 level = maxLocks - depth - 1;
|
|
|
|
ndbout << "Depth " << level << endl;
|
|
|
|
ndbout << " TransC : Trying to lock row with lockmode ";
|
|
NdbOperation::LockMode lmC;
|
|
if (useExclusiveInA)
|
|
{
|
|
lmC = chooseLockMode(true); // LM_Exclusive or LM_Read;
|
|
}
|
|
else
|
|
{
|
|
ndbout << "LM_Exclusive" << endl;
|
|
lmC = NdbOperation::LM_Exclusive;
|
|
}
|
|
|
|
CHECK(checkReadDeadlocks(&hugoOps,
|
|
ndb,
|
|
pTab,
|
|
lmC,
|
|
rowNum));
|
|
|
|
ndbout << " TransC failed as expected" << endl;
|
|
|
|
if (level < numLocksInTransB)
|
|
{
|
|
const NdbLockHandle* lockHandleB = transBOps[ level ]->getLockHandle();
|
|
CHECKN(lockHandleB != NULL, transBOps[ level ], NDBT_FAILED);
|
|
|
|
const NdbOperation* unlockB = transB->unlock(lockHandleB);
|
|
CHECKN(unlockB != NULL, transB, NDBT_FAILED);
|
|
|
|
CHECKN(transB->execute(NoCommit) == 0, transB, NDBT_FAILED);
|
|
ndbout << " TransB unlock succeeded" << endl;
|
|
}
|
|
|
|
if (level < numLocksInTransA)
|
|
{
|
|
const NdbLockHandle* lockHandleA = transAOps[ level ]->getLockHandle();
|
|
CHECKN(lockHandleA != NULL, transAOps[ level ], NDBT_FAILED);
|
|
|
|
const NdbOperation* unlockA = transA->unlock(lockHandleA);
|
|
CHECKN(unlockA != NULL, transA, NDBT_FAILED);
|
|
|
|
CHECKN(transA->execute(NoCommit) == 0, transA, NDBT_FAILED);
|
|
ndbout << " TransA unlock succeeded" << endl;
|
|
}
|
|
}
|
|
|
|
|
|
/* Finally, all are unlocked and transC can successfully
|
|
* obtain a conflicting lock
|
|
*/
|
|
CHECK(checkReadSucceeds(&hugoOps,
|
|
ndb,
|
|
pTab,
|
|
NdbOperation::LM_Exclusive,
|
|
rowNum));
|
|
|
|
ndbout << "TransC LM_Exclusive lock succeeded" << endl;
|
|
|
|
transA->close();
|
|
transB->close();
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runTestUnlockScan(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Performs a table scan with LM_Read or LM_Exclusive
|
|
* and lock takeovers for a number of the rows returned
|
|
* Validates that some of the taken-over locks are held
|
|
* before unlocking them and validating that they
|
|
* are released.
|
|
*/
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoCalculator calc(*pTab);
|
|
HugoOperations hugoOps(*pTab);
|
|
|
|
/*
|
|
1) Perform scan of the table with LM_Read / LM_Exclusive
|
|
2) Takeover some of the rows with read and lockinfo
|
|
3) Unlock the rows
|
|
4) Check that they are unlocked
|
|
*/
|
|
Ndb* ndb = GETNDB(step);
|
|
|
|
const int iterations = 2;
|
|
|
|
const int maxNumTakeovers = 15;
|
|
NdbOperation* takeoverOps[ maxNumTakeovers ];
|
|
Uint32 takeoverColIds[ maxNumTakeovers ];
|
|
|
|
int numTakeovers = MIN(maxNumTakeovers, ctx->getNumRecords());
|
|
int takeoverMod = ctx->getNumRecords() / numTakeovers;
|
|
|
|
ndbout << "numTakeovers is " << numTakeovers
|
|
<< " takeoverMod is " << takeoverMod << endl;
|
|
|
|
for (int iter = 0; iter < iterations; iter++)
|
|
{
|
|
ndbout << "Scanning table with lock mode : ";
|
|
NdbOperation::LockMode lmScan = chooseLockMode(true); // LM_Exclusive or LM_Read
|
|
|
|
NdbTransaction* trans = ndb->startTransaction();
|
|
CHECKN(trans != NULL, ndb, NDBT_FAILED);
|
|
|
|
/* Define scan */
|
|
NdbScanOperation* scan = trans->getNdbScanOperation(pTab);
|
|
CHECKN(scan != NULL, trans, NDBT_FAILED);
|
|
|
|
Uint32 scanFlags = NdbScanOperation::SF_KeyInfo;
|
|
|
|
CHECKN(scan->readTuples(lmScan, scanFlags) == 0, scan, NDBT_FAILED);
|
|
|
|
NdbRecAttr* idColRecAttr = NULL;
|
|
|
|
for(int c = 0; c < pTab->getNoOfColumns(); c++)
|
|
{
|
|
NdbRecAttr* ra = scan->getValue(pTab->getColumn(c)->getName());
|
|
CHECKN(ra != NULL, scan, NDBT_FAILED);
|
|
if (calc.isIdCol(c))
|
|
{
|
|
CHECK(idColRecAttr == NULL);
|
|
idColRecAttr = ra;
|
|
}
|
|
}
|
|
CHECK(idColRecAttr != NULL);
|
|
|
|
CHECKN(trans->execute(NoCommit) == 0, trans, NDBT_FAILED);
|
|
|
|
int rowsRead = 0;
|
|
int rowsTakenover = 0;
|
|
while (scan->nextResult(true) == 0)
|
|
{
|
|
if ((rowsTakenover < maxNumTakeovers) &&
|
|
(0 == (rowsRead % takeoverMod)))
|
|
{
|
|
/* We're going to take the lock for this row into
|
|
* a separate operation
|
|
*/
|
|
Uint32 rowId = idColRecAttr->u_32_value();
|
|
ndbout << " Taking over lock on result num " << rowsRead
|
|
<< " row (" << rowId << ")" << endl;
|
|
NdbOperation* readTakeoverOp = scan->lockCurrentTuple();
|
|
CHECKN(readTakeoverOp != NULL, scan, NDBT_FAILED);
|
|
|
|
CHECKN(readTakeoverOp->getLockHandle() != NULL, readTakeoverOp, NDBT_FAILED);
|
|
takeoverOps[ rowsTakenover ] = readTakeoverOp;
|
|
takeoverColIds[ rowsTakenover ] = rowId;
|
|
|
|
CHECKN(trans->execute(NoCommit) == 0, trans, NDBT_FAILED);
|
|
|
|
CHECKN(readTakeoverOp->getNdbError().code == 0, readTakeoverOp, NDBT_FAILED);
|
|
|
|
// // Uncomment to check that takeover keeps lock.
|
|
// if (0 == (rowsTakenover % 7))
|
|
// {
|
|
// ndbout << " Validating taken-over lock holds on rowid "
|
|
// << takeoverColIds[ rowsTakenover ]
|
|
// << " by ";
|
|
// /* Occasionally validate the lock held by the scan */
|
|
// CHECK(checkReadDeadlocks(&hugoOps,
|
|
// ndb,
|
|
// pTab,
|
|
// chooseConflictingLockMode(lmScan),
|
|
// takeoverColIds[ rowsTakenover ]));
|
|
// }
|
|
|
|
rowsTakenover ++;
|
|
|
|
}
|
|
|
|
rowsRead ++;
|
|
}
|
|
|
|
scan->close();
|
|
|
|
ndbout << "Scan complete : rows read : " << rowsRead
|
|
<< " rows locked : " << rowsTakenover << endl;
|
|
|
|
ndbout << "Now unlocking rows individually" << endl;
|
|
for (int lockedRows = 0; lockedRows < rowsTakenover; lockedRows ++)
|
|
{
|
|
if (0 == (lockedRows % 3))
|
|
{
|
|
ndbout << " First validating that lock holds on rowid "
|
|
<< takeoverColIds[ lockedRows ]
|
|
<< " by ";
|
|
/* Occasionally check that the lock held by the scan still holds */
|
|
CHECK(checkReadDeadlocks(&hugoOps,
|
|
ndb,
|
|
pTab,
|
|
chooseConflictingLockMode(lmScan),
|
|
takeoverColIds[ lockedRows ]));
|
|
ndbout << " Lock is held" << endl;
|
|
}
|
|
|
|
/* Unlock the row */
|
|
const NdbLockHandle* lockHandle = takeoverOps[ lockedRows ]->getLockHandle();
|
|
CHECKN(lockHandle != NULL, takeoverOps[ lockedRows ], NDBT_FAILED);
|
|
|
|
const NdbOperation* unlockOp = trans->unlock(lockHandle);
|
|
CHECKN(unlockOp, trans, NDBT_FAILED);
|
|
|
|
CHECKN(trans->execute(NoCommit) == 0, trans, NDBT_FAILED);
|
|
|
|
/* Now check that the row's unlocked */
|
|
CHECK(checkReadSucceeds(&hugoOps,
|
|
ndb,
|
|
pTab,
|
|
NdbOperation::LM_Exclusive,
|
|
takeoverColIds[ lockedRows ]));
|
|
ndbout << " Row " << takeoverColIds[ lockedRows ]
|
|
<< " unlocked successfully" << endl;
|
|
}
|
|
|
|
/* Lastly, verify that scan with LM_Exclusive in separate transaction
|
|
* can scan whole table without locking on anything
|
|
*/
|
|
ndbout << "Validating unlocking code with LM_Exclusive table scan" << endl;
|
|
|
|
NdbTransaction* otherTrans = ndb->startTransaction();
|
|
CHECKN(otherTrans != NULL, ndb, NDBT_FAILED);
|
|
|
|
NdbScanOperation* otherScan = otherTrans->getNdbScanOperation(pTab);
|
|
CHECKN(otherScan != NULL, otherTrans, NDBT_FAILED);
|
|
|
|
CHECKN(otherScan->readTuples(NdbOperation::LM_Exclusive) == 0, otherScan, NDBT_FAILED);
|
|
|
|
for(int c = 0; c < pTab->getNoOfColumns(); c++)
|
|
{
|
|
NdbRecAttr* ra = otherScan->getValue(pTab->getColumn(c)->getName());
|
|
CHECKN(ra != NULL, otherScan, NDBT_FAILED);
|
|
}
|
|
|
|
CHECKN(otherTrans->execute(NoCommit) == 0, trans, NDBT_FAILED);
|
|
|
|
int nextRc = 0;
|
|
while (0 == (nextRc = otherScan->nextResult(true)))
|
|
{};
|
|
|
|
if (nextRc != 1)
|
|
{
|
|
ndbout << "Final scan with lock did not complete successfully" << endl;
|
|
ndbout << otherScan->getNdbError() << endl;
|
|
ndbout << "at line " << __LINE__ << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
otherScan->close();
|
|
otherTrans->close();
|
|
|
|
ndbout << "All locked rows unlocked" << endl;
|
|
|
|
trans->close();
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
#include <NdbMgmd.hpp>
|
|
|
|
class NodeIdReservations {
|
|
bool m_ids[MAX_NODES];
|
|
NdbMutex m_mutex;
|
|
public:
|
|
void lock(unsigned id)
|
|
{
|
|
require(id < NDB_ARRAY_SIZE(m_ids));
|
|
NdbMutex_Lock(&m_mutex);
|
|
//ndbout << "locking nodeid: " << id << endl;
|
|
if (m_ids[id])
|
|
{
|
|
//already locked!
|
|
g_err << "Nodeid " << id << " is already locked! Crashing!" << endl;
|
|
abort();
|
|
}
|
|
m_ids[id] = true;
|
|
NdbMutex_Unlock(&m_mutex);
|
|
}
|
|
|
|
void unlock(unsigned id)
|
|
{
|
|
require(id < NDB_ARRAY_SIZE(m_ids));
|
|
NdbMutex_Lock(&m_mutex);
|
|
//ndbout << "unlocking nodeid: " << id << endl;
|
|
if (!m_ids[id])
|
|
{
|
|
//already unlocked!
|
|
abort();
|
|
}
|
|
m_ids[id] = false;
|
|
NdbMutex_Unlock(&m_mutex);
|
|
}
|
|
|
|
NodeIdReservations() {
|
|
bzero(m_ids, sizeof(m_ids));
|
|
NdbMutex_Init(&m_mutex);
|
|
}
|
|
|
|
class Reserve {
|
|
unsigned m_id;
|
|
NodeIdReservations& m_res;
|
|
|
|
Reserve(); // Not impl.
|
|
Reserve(const Reserve&); // Not impl.
|
|
public:
|
|
Reserve(NodeIdReservations& res, unsigned id) :
|
|
m_id(id), m_res(res) {
|
|
m_res.lock(m_id);
|
|
}
|
|
|
|
void unlock() {
|
|
m_res.unlock(m_id);
|
|
m_id = 0;
|
|
}
|
|
|
|
~Reserve(){
|
|
if (m_id)
|
|
{
|
|
m_res.unlock(m_id);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
NodeIdReservations g_reservations;
|
|
|
|
|
|
int runNdbClusterConnectInit(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
// Find number of unconnected API nodes slot to use for test
|
|
Uint32 api_nodes = 0;
|
|
{
|
|
NdbMgmd mgmd;
|
|
|
|
if (!mgmd.connect())
|
|
return NDBT_FAILED;
|
|
|
|
ndb_mgm_node_type
|
|
node_types[2] = { NDB_MGM_NODE_TYPE_API,
|
|
NDB_MGM_NODE_TYPE_UNKNOWN };
|
|
|
|
ndb_mgm_cluster_state *cs = ndb_mgm_get_status2(mgmd.handle(), node_types);
|
|
if (cs == NULL)
|
|
{
|
|
printf("ndb_mgm_get_status2 failed, error: %d - %s\n",
|
|
ndb_mgm_get_latest_error(mgmd.handle()),
|
|
ndb_mgm_get_latest_error_msg(mgmd.handle()));
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for(int i = 0; i < cs->no_of_nodes; i++ )
|
|
{
|
|
ndb_mgm_node_state *ns = cs->node_states + i;
|
|
require(ns->node_type == NDB_MGM_NODE_TYPE_API);
|
|
if (ns->node_status == NDB_MGM_NODE_STATUS_CONNECTED)
|
|
{
|
|
// Node is already connected, don't use in test
|
|
continue;
|
|
}
|
|
api_nodes++;
|
|
}
|
|
free(cs);
|
|
}
|
|
|
|
if (api_nodes <= 1)
|
|
{
|
|
ndbout << "Too few API node slots available, failing test" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
// Don't try to use nodeid allocated by main cluster connection
|
|
api_nodes--;
|
|
|
|
ndbout << "Found " << api_nodes << " unconnected API nodes" << endl;
|
|
ctx->setProperty("API_NODES", api_nodes);
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runNdbClusterConnect(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
const Uint32 api_nodes = ctx->getProperty("API_NODES");
|
|
const Uint32 step_no = step->getStepNo();
|
|
const Uint32 timeout_after_first_alive = ctx->getProperty("TimeoutAfterFirst",
|
|
30);
|
|
if (step_no > api_nodes)
|
|
{
|
|
// Don't run with more threads than API node slots
|
|
return NDBT_OK;
|
|
}
|
|
|
|
// Get connectstring from main connection
|
|
char constr[256];
|
|
if (!ctx->m_cluster_connection.get_connectstring(constr,
|
|
sizeof(constr)))
|
|
{
|
|
g_err << "Too short buffer for connectstring" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
Uint32 l = 0;
|
|
const Uint32 loops = ctx->getNumLoops();
|
|
while (l < loops && !ctx->isTestStopped())
|
|
{
|
|
g_info << "loop: " << l << endl;
|
|
if (ctx->getProperty("WAIT") > 0)
|
|
{
|
|
ndbout_c("thread %u waiting", step_no);
|
|
ctx->incProperty("WAITING");
|
|
while (ctx->getProperty("WAIT") > 0 && !ctx->isTestStopped())
|
|
NdbSleep_MilliSleep(10);
|
|
ndbout_c("thread %u waiting complete", step_no);
|
|
}
|
|
Ndb_cluster_connection con(constr);
|
|
|
|
const int retries = 12;
|
|
const int retry_delay = 5;
|
|
const int verbose = 1;
|
|
if (con.connect(retries, retry_delay, verbose) != 0)
|
|
{
|
|
g_err << "Ndb_cluster_connection.connect failed" << endl;
|
|
g_err << "Error code: "
|
|
<< con.get_latest_error()
|
|
<< " message: "
|
|
<< con.get_latest_error_msg()
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Check that the connection got a unique nodeid
|
|
NodeIdReservations::Reserve res(g_reservations, con.node_id());
|
|
|
|
const int timeout = 30;
|
|
int ret = con.wait_until_ready(timeout, timeout_after_first_alive);
|
|
if (! (ret == 0 || (timeout_after_first_alive == 0 && ret > 0)))
|
|
{
|
|
g_err << "Cluster connection was not ready, nodeid: "
|
|
<< con.node_id() << endl;
|
|
g_err << "Error code: "
|
|
<< con.get_latest_error()
|
|
<< " message: "
|
|
<< con.get_latest_error_msg()
|
|
<< endl;
|
|
abort();
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Create and init Ndb object
|
|
Ndb ndb(&con, "TEST_DB");
|
|
if (ndb.init() != 0)
|
|
{
|
|
NDB_ERR(ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
const int max_sleep = 25;
|
|
NdbSleep_MilliSleep(10 + rand() % max_sleep);
|
|
|
|
l++;
|
|
res.unlock(); // make sure it's called before ~Ndb_cluster_connection
|
|
}
|
|
|
|
ctx->incProperty("runNdbClusterConnect_FINISHED");
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runRestarts(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
int result = NDBT_OK;
|
|
Uint32 threads = ctx->getProperty("API_NODES", (unsigned)0);
|
|
Uint32 sr = ctx->getProperty("ClusterRestart", (unsigned)0);
|
|
Uint32 master = ctx->getProperty("Master", (unsigned)0);
|
|
Uint32 slow = ctx->getProperty("SlowNR", (unsigned)0);
|
|
Uint32 slowNoStart = ctx->getProperty("SlowNoStart", (unsigned)0);
|
|
NdbRestarter restarter;
|
|
|
|
if (restarter.waitClusterStarted() != 0)
|
|
{
|
|
g_err << "Cluster failed to start" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (sr == 0 && restarter.getNumDbNodes() < 2)
|
|
return NDBT_OK;
|
|
|
|
while (ctx->getProperty("runNdbClusterConnect_FINISHED") < threads
|
|
&& !ctx->isTestStopped())
|
|
{
|
|
ndbout_c("%u %u",
|
|
ctx->getProperty("runNdbClusterConnect_FINISHED"),
|
|
threads);
|
|
if (sr == 0)
|
|
{
|
|
int id = rand() % restarter.getNumDbNodes();
|
|
int nodeId = restarter.getDbNodeId(id);
|
|
if (master == 1)
|
|
{
|
|
nodeId = restarter.getMasterNodeId();
|
|
}
|
|
else if (master == 2)
|
|
{
|
|
nodeId = restarter.getRandomNotMasterNodeId(rand());
|
|
}
|
|
ndbout << "Restart node " << nodeId
|
|
<< "(master: " << restarter.getMasterNodeId() << ")"
|
|
<< endl;
|
|
if (restarter.restartOneDbNode(nodeId, false, true, true) != 0)
|
|
{
|
|
g_err << "Failed to restartNextDbNode" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
if (restarter.waitNodesNoStart(&nodeId, 1))
|
|
{
|
|
g_err << "Failed to waitNodesNoStart" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
if (slowNoStart)
|
|
{
|
|
/**
|
|
* Spend some time in the NOT_STARTED state, as opposed
|
|
* to some substate of STARTING
|
|
*/
|
|
Uint32 blockTime = 3 * 60 * 1000;
|
|
Uint64 end = NdbTick_CurrentMillisecond() + blockTime;
|
|
while (ctx->getProperty("runNdbClusterConnect_FINISHED") < threads
|
|
&& !ctx->isTestStopped() &&
|
|
NdbTick_CurrentMillisecond() < end)
|
|
{
|
|
NdbSleep_MilliSleep(100);
|
|
}
|
|
}
|
|
|
|
if (slow)
|
|
{
|
|
/**
|
|
* Block starting node in sp4
|
|
*/
|
|
int dump[] = { 71, 4 };
|
|
restarter.dumpStateOneNode(nodeId, dump, NDB_ARRAY_SIZE(dump));
|
|
}
|
|
|
|
if (restarter.startNodes(&nodeId, 1))
|
|
{
|
|
g_err << "Failed to start node" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
if (slow)
|
|
{
|
|
Uint32 blockTime = 3 * 60 * 1000;
|
|
Uint64 end = NdbTick_CurrentMillisecond() + blockTime;
|
|
while (ctx->getProperty("runNdbClusterConnect_FINISHED") < threads
|
|
&& !ctx->isTestStopped() &&
|
|
NdbTick_CurrentMillisecond() < end)
|
|
{
|
|
NdbSleep_MilliSleep(100);
|
|
}
|
|
|
|
// unblock
|
|
int dump[] = { 71 };
|
|
restarter.dumpStateOneNode(nodeId, dump, NDB_ARRAY_SIZE(dump));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ndbout << "Blocking threads" << endl;
|
|
ctx->setProperty("WAITING", Uint32(0));
|
|
ctx->setProperty("WAIT", 1);
|
|
while (ctx->getProperty("WAITING") <
|
|
(threads - ctx->getProperty("runNdbClusterConnect_FINISHED")) &&
|
|
!ctx->isTestStopped())
|
|
{
|
|
NdbSleep_MilliSleep(10);
|
|
}
|
|
|
|
ndbout << "Restart cluster" << endl;
|
|
if (restarter.restartAll2(Uint32(NdbRestarter::NRRF_NOSTART |
|
|
NdbRestarter::NRRF_ABORT)) != 0)
|
|
{
|
|
g_err << "Failed to restartAll" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
ctx->setProperty("WAITING", Uint32(0));
|
|
ctx->setProperty("WAIT", Uint32(0));
|
|
|
|
ndbout << "Starting cluster" << endl;
|
|
restarter.startAll();
|
|
}
|
|
|
|
if (restarter.waitClusterStarted() != 0)
|
|
{
|
|
g_err << "Cluster failed to start" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int runCheckAllNodesStarted(NDBT_Context* ctx, NDBT_Step* step){
|
|
NdbRestarter restarter;
|
|
|
|
if (restarter.waitClusterStarted(1) != 0)
|
|
{
|
|
g_err << "All nodes was not started " << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
check_connect_no_such_host()
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
const char* no_such_host = "no_such_host:1186";
|
|
Ndb_cluster_connection con(no_such_host);
|
|
|
|
const int verbose = 1;
|
|
int res = con.connect(i, i, verbose);
|
|
if (res != 1)
|
|
{
|
|
g_err << "Ndb_cluster_connection.connect(" << i << "," << i
|
|
<< ", 1) to '" << no_such_host << "' returned " << res
|
|
<< " instead of expected 1" << endl;
|
|
return false;
|
|
}
|
|
g_info << "Ndb_cluster_connection.connect(" << i << "," << i
|
|
<< ", 1) to '" << no_such_host << "' returned " << res
|
|
<< " and message '" << con.get_latest_error_msg() << "'"<< endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool
|
|
check_connect_until_no_more_nodeid(const char* constr)
|
|
{
|
|
bool result = true;
|
|
Vector<Ndb_cluster_connection*> connections;
|
|
while(true)
|
|
{
|
|
Ndb_cluster_connection* con = new Ndb_cluster_connection(constr);
|
|
if (!con)
|
|
{
|
|
g_err << "Failed to create another Ndb_cluster_connection" << endl;
|
|
result = false;
|
|
break;
|
|
}
|
|
connections.push_back(con);
|
|
g_info << "connections: " << connections.size() << endl;
|
|
|
|
const int verbose = 1;
|
|
int res = con->connect(0, 0, verbose);
|
|
if (res != 0)
|
|
{
|
|
g_info << "Ndb_cluster_connection.connect(0,0,1) returned " << res
|
|
<< " and error message set to : '" << con->get_latest_error_msg()
|
|
<< "'" << endl;
|
|
|
|
if (res != 1)
|
|
{
|
|
// The error returned should be 1
|
|
g_err << "Unexpected return code " << res << " returned" << endl;
|
|
result = false;
|
|
}
|
|
else if (strstr(con->get_latest_error_msg(),
|
|
"No free node id found for mysqld(API)") == NULL)
|
|
{
|
|
// The error message should end with "No free node id
|
|
// found for mysqld(API)" since this host is configured in the config
|
|
g_err << "Unexpected error message " << con->get_latest_error_msg()
|
|
<< " returned" << endl;
|
|
result = false;
|
|
}
|
|
else
|
|
{
|
|
ndbout << "check_connect_until_no_more_nodeid OK!" << endl;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
while(connections.size())
|
|
{
|
|
Ndb_cluster_connection* con = connections[0];
|
|
g_info << "releasing connection, size: " << connections.size() << endl;
|
|
delete con;
|
|
connections.erase(0);
|
|
}
|
|
require(connections.size() == 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int runNdbClusterConnectionConnect(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
// Get connectstring from main connection
|
|
char constr[256];
|
|
if(!ctx->m_cluster_connection.get_connectstring(constr,
|
|
sizeof(constr)))
|
|
{
|
|
g_err << "Too short buffer for connectstring" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (!check_connect_no_such_host() ||
|
|
!check_connect_until_no_more_nodeid(constr))
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/* Testing fragmented signal send/receive */
|
|
|
|
/*
|
|
SectionStore
|
|
|
|
Abstraction of long section storage api.
|
|
Used by FragmentAssembler to assemble received long sections
|
|
*/
|
|
class SectionStore
|
|
{
|
|
public:
|
|
virtual ~SectionStore() {}
|
|
virtual int appendToSection(Uint32 secId, LinearSectionPtr ptr) = 0;
|
|
};
|
|
|
|
/*
|
|
Basic Section Store
|
|
|
|
Naive implementation using malloc. Real usage might use something better.
|
|
*/
|
|
class BasicSectionStore : public SectionStore
|
|
{
|
|
public:
|
|
BasicSectionStore()
|
|
{
|
|
init();
|
|
}
|
|
|
|
~BasicSectionStore()
|
|
{
|
|
freeStorage();
|
|
}
|
|
|
|
void init()
|
|
{
|
|
ptrs[0].p = NULL;
|
|
ptrs[0].sz = 0;
|
|
|
|
ptrs[2] = ptrs[1] = ptrs[0];
|
|
}
|
|
|
|
void freeStorage()
|
|
{
|
|
free(ptrs[0].p);
|
|
free(ptrs[1].p);
|
|
free(ptrs[2].p);
|
|
}
|
|
|
|
virtual int appendToSection(Uint32 secId, LinearSectionPtr ptr)
|
|
{
|
|
/* Potentially expensive re-alloc + copy */
|
|
require(secId < 3);
|
|
|
|
Uint32 existingSz = ptrs[secId].sz;
|
|
Uint32* existingBuff = ptrs[secId].p;
|
|
|
|
Uint32 newSize = existingSz + ptr.sz;
|
|
Uint32* newBuff = (Uint32*) realloc(existingBuff, newSize * 4);
|
|
|
|
if (!newBuff)
|
|
return -1;
|
|
|
|
memcpy(newBuff + existingSz, ptr.p, ptr.sz * 4);
|
|
|
|
ptrs[secId].p = newBuff;
|
|
ptrs[secId].sz = existingSz + ptr.sz;
|
|
|
|
return 0;
|
|
}
|
|
|
|
LinearSectionPtr ptrs[3];
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
FragmentAssembler
|
|
|
|
Used to assemble sections from multiple fragment signals, and
|
|
produce a 'normal' signal.
|
|
|
|
Requires a SectionStore implementation to accumulate the section
|
|
fragments
|
|
|
|
Might be useful generic utility, or not.
|
|
|
|
Usage :
|
|
FragmentAssembler fa(ss);
|
|
while (!fa.isComplete())
|
|
{
|
|
sig = waitSignal();
|
|
ss.handleSignal(sig, sections);
|
|
}
|
|
|
|
fa.getSignalHeader();
|
|
fa.getSignalBody();
|
|
fa.getSectionStore(); ..
|
|
|
|
*/
|
|
class FragmentAssembler
|
|
{
|
|
public:
|
|
enum AssemblyError
|
|
{
|
|
NoError = 0,
|
|
FragmentSequence = 1,
|
|
FragmentSource = 2,
|
|
FragmentIdentity = 3,
|
|
SectionAppend = 4
|
|
};
|
|
|
|
FragmentAssembler(SectionStore* _secStore):
|
|
secsReceived(0),
|
|
secStore(_secStore),
|
|
complete(false),
|
|
fragId(0),
|
|
sourceNode(0),
|
|
error(NoError)
|
|
{}
|
|
|
|
int handleSignal(const SignalHeader* sigHead,
|
|
const Uint32* sigBody,
|
|
LinearSectionPtr* sections)
|
|
{
|
|
Uint32 sigLen = sigHead->theLength;
|
|
|
|
if (fragId == 0)
|
|
{
|
|
switch (sigHead->m_fragmentInfo)
|
|
{
|
|
case 0:
|
|
{
|
|
/* Not fragmented, pass through */
|
|
sh = *sigHead;
|
|
memcpy(signalBody, sigBody, sigLen * 4);
|
|
Uint32 numSecs = sigHead->m_noOfSections;
|
|
for (Uint32 i=0; i<numSecs; i++)
|
|
{
|
|
if (secStore->appendToSection(i, sections[i]) != 0)
|
|
{
|
|
error = SectionAppend;
|
|
return -1;
|
|
}
|
|
}
|
|
complete = true;
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
/* Start of fragmented signal */
|
|
Uint32 incomingFragId;
|
|
Uint32 incomingSourceNode;
|
|
Uint32 numSecsInFragment;
|
|
|
|
if (handleFragmentSections(sigHead, sigBody, sections,
|
|
&incomingFragId, &incomingSourceNode,
|
|
&numSecsInFragment) != 0)
|
|
return -1;
|
|
|
|
require(incomingFragId != 0);
|
|
fragId = incomingFragId;
|
|
sourceNode = incomingSourceNode;
|
|
require(numSecsInFragment > 0);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* Error, out of sequence fragment */
|
|
error = FragmentSequence;
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* FragId != 0 */
|
|
switch (sigHead->m_fragmentInfo)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
{
|
|
/* Error, out of sequence fragment */
|
|
error = FragmentSequence;
|
|
return -1;
|
|
}
|
|
case 2:
|
|
/* Fall through */
|
|
case 3:
|
|
{
|
|
/* Body fragment */
|
|
Uint32 incomingFragId;
|
|
Uint32 incomingSourceNode;
|
|
Uint32 numSecsInFragment;
|
|
|
|
if (handleFragmentSections(sigHead, sigBody, sections,
|
|
&incomingFragId, &incomingSourceNode,
|
|
&numSecsInFragment) != 0)
|
|
return -1;
|
|
|
|
if (incomingSourceNode != sourceNode)
|
|
{
|
|
/* Error in source node */
|
|
error = FragmentSource;
|
|
return -1;
|
|
}
|
|
if (incomingFragId != fragId)
|
|
{
|
|
error = FragmentIdentity;
|
|
return -1;
|
|
}
|
|
|
|
if (sigHead->m_fragmentInfo == 3)
|
|
{
|
|
/* Final fragment, contains actual signal body */
|
|
memcpy(signalBody,
|
|
sigBody,
|
|
sigLen * 4);
|
|
sh = *sigHead;
|
|
sh.theLength = sigLen - (numSecsInFragment + 1);
|
|
sh.m_noOfSections =
|
|
((secsReceived & 4)? 1 : 0) +
|
|
((secsReceived & 2)? 1 : 0) +
|
|
((secsReceived & 1)? 1 : 0);
|
|
sh.m_fragmentInfo = 0;
|
|
|
|
complete=true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* Bad fragmentinfo field */
|
|
error = FragmentSequence;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int handleSignal(NdbApiSignal* signal,
|
|
LinearSectionPtr* sections)
|
|
{
|
|
return handleSignal(signal, signal->getDataPtr(), sections);
|
|
}
|
|
|
|
bool isComplete()
|
|
{
|
|
return complete;
|
|
}
|
|
|
|
/* Valid if isComplete() */
|
|
SignalHeader getSignalHeader()
|
|
{
|
|
return sh;
|
|
}
|
|
|
|
/* Valid if isComplete() */
|
|
Uint32* getSignalBody()
|
|
{
|
|
return signalBody;
|
|
}
|
|
|
|
/* Valid if isComplete() */
|
|
Uint32 getSourceNode()
|
|
{
|
|
return sourceNode;
|
|
}
|
|
|
|
SectionStore* getSectionStore()
|
|
{
|
|
return secStore;
|
|
}
|
|
|
|
AssemblyError getError() const
|
|
{
|
|
return error;
|
|
}
|
|
|
|
private:
|
|
int handleFragmentSections(const SignalHeader* sigHead,
|
|
const Uint32* sigBody,
|
|
LinearSectionPtr* sections,
|
|
Uint32* incomingFragId,
|
|
Uint32* incomingSourceNode,
|
|
Uint32* numSecsInFragment)
|
|
{
|
|
Uint32 sigLen = sigHead->theLength;
|
|
|
|
*numSecsInFragment = sigHead->m_noOfSections;
|
|
require(sigLen >= (1 + *numSecsInFragment));
|
|
|
|
*incomingFragId = sigBody[sigLen - 1];
|
|
*incomingSourceNode = refToNode(sigHead->theSendersBlockRef);
|
|
const Uint32* secIds = &sigBody[sigLen - (*numSecsInFragment) - 1];
|
|
|
|
for (Uint32 i=0; i < *numSecsInFragment; i++)
|
|
{
|
|
secsReceived |= (1 < secIds[i]);
|
|
|
|
if (secStore->appendToSection(secIds[i], sections[i]) != 0)
|
|
{
|
|
error = SectionAppend;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Uint32 secsReceived;
|
|
SectionStore* secStore;
|
|
bool complete;
|
|
Uint32 fragId;
|
|
Uint32 sourceNode;
|
|
SignalHeader sh;
|
|
Uint32 signalBody[NdbApiSignal::MaxSignalWords];
|
|
AssemblyError error;
|
|
};
|
|
|
|
static const Uint32 MAX_SEND_BYTES=32768; /* Align with TransporterDefinitions.hpp */
|
|
static const Uint32 MAX_SEND_WORDS=MAX_SEND_BYTES/4;
|
|
static const Uint32 SEGMENT_WORDS= 60; /* Align with SSPool etc */
|
|
static const Uint32 SEGMENT_BYTES = SEGMENT_WORDS * 4;
|
|
//static const Uint32 MAX_SEGS_PER_SEND=64; /* 6.3 */
|
|
static const Uint32 MAX_SEGS_PER_SEND = (MAX_SEND_BYTES / SEGMENT_BYTES) - 2; /* Align with TransporterFacade.cpp */
|
|
static const Uint32 MAX_WORDS_PER_SEND = MAX_SEGS_PER_SEND * SEGMENT_WORDS;
|
|
static const Uint32 HALF_MAX_WORDS_PER_SEND = MAX_WORDS_PER_SEND / 2;
|
|
static const Uint32 THIRD_MAX_WORDS_PER_SEND = MAX_WORDS_PER_SEND / 3;
|
|
static const Uint32 MEDIUM_SIZE = 5000;
|
|
|
|
/* Most problems occurred with sections lengths around the boundary
|
|
* of the max amount sent - MAX_WORDS_PER_SEND, so we define interesting
|
|
* sizes so that we test behavior around these boundaries
|
|
*/
|
|
static Uint32 interestingSizes[] =
|
|
{
|
|
0,
|
|
1,
|
|
MEDIUM_SIZE,
|
|
THIRD_MAX_WORDS_PER_SEND -1,
|
|
THIRD_MAX_WORDS_PER_SEND,
|
|
THIRD_MAX_WORDS_PER_SEND +1,
|
|
HALF_MAX_WORDS_PER_SEND -1,
|
|
HALF_MAX_WORDS_PER_SEND,
|
|
HALF_MAX_WORDS_PER_SEND + 1,
|
|
MAX_WORDS_PER_SEND -1,
|
|
MAX_WORDS_PER_SEND,
|
|
MAX_WORDS_PER_SEND + 1,
|
|
(2* MAX_SEND_WORDS) + 1,
|
|
1234 /* Random */
|
|
};
|
|
|
|
|
|
/*
|
|
FragSignalChecker
|
|
|
|
Class for testing fragmented signal send + receive
|
|
*/
|
|
class FragSignalChecker
|
|
{
|
|
public:
|
|
|
|
Uint32* buffer;
|
|
|
|
FragSignalChecker()
|
|
{
|
|
buffer= NULL;
|
|
init();
|
|
}
|
|
|
|
~FragSignalChecker()
|
|
{
|
|
free(buffer);
|
|
}
|
|
|
|
void init()
|
|
{
|
|
buffer = (Uint32*) malloc(getBufferSize());
|
|
|
|
if (buffer)
|
|
{
|
|
/* Init to a known pattern */
|
|
for (Uint32 i = 0; i < (getBufferSize()/4); i++)
|
|
{
|
|
buffer[i] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Uint32 getNumInterestingSizes()
|
|
{
|
|
return sizeof(interestingSizes) / sizeof(Uint32);
|
|
}
|
|
|
|
static Uint32 getNumIterationsRequired()
|
|
{
|
|
/* To get combinatorial coverage, need each of 3
|
|
* sections with each of the interesting sizes
|
|
*/
|
|
Uint32 numSizes = getNumInterestingSizes();
|
|
return numSizes * numSizes * numSizes;
|
|
}
|
|
|
|
static Uint32 getSecSz(Uint32 secNum, Uint32 iter)
|
|
{
|
|
require(secNum < 3);
|
|
Uint32 numSizes = getNumInterestingSizes();
|
|
Uint32 divisor = (secNum == 0 ? 1 :
|
|
secNum == 1 ? numSizes :
|
|
numSizes * numSizes);
|
|
/* offset ensures only end sections are 0 length */
|
|
Uint32 index = (iter / divisor) % numSizes;
|
|
if ((index == 0) && (iter >= (divisor * numSizes)))
|
|
index = 1; /* Avoid lower numbered section being empty */
|
|
Uint32 value = interestingSizes[index];
|
|
if(value == 1234)
|
|
{
|
|
value = 1 + (rand() % (2* MAX_WORDS_PER_SEND));
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static Uint32 getBufferSize()
|
|
{
|
|
const Uint32 MaxSectionWords = (2 * MAX_SEND_WORDS) + 1;
|
|
const Uint32 MaxTotalSectionsWords = MaxSectionWords * 3;
|
|
return MaxTotalSectionsWords * 4;
|
|
}
|
|
|
|
int sendRequest(SignalSender* ss,
|
|
Uint32* sizes)
|
|
{
|
|
/*
|
|
* We want to try out various interactions between the
|
|
* 3 sections and the length of the data sent
|
|
* - All fit in one 'chunk'
|
|
* - None fit in one 'chunk'
|
|
* - Each ends on a chunk boundary
|
|
*
|
|
* Max send size is ~ 32kB
|
|
* Segment size is 60 words / 240 bytes
|
|
* -> 136 segments / chunk
|
|
* -> 134 segments / chunk 'normally' sent
|
|
* -> 32160 bytes
|
|
*/
|
|
g_err << "Sending "
|
|
<< sizes[0]
|
|
<< " " << sizes[1]
|
|
<< " " << sizes[2]
|
|
<< endl;
|
|
|
|
const Uint32 numSections =
|
|
(sizes[0] ? 1 : 0) +
|
|
(sizes[1] ? 1 : 0) +
|
|
(sizes[2] ? 1 : 0);
|
|
const Uint32 testType = 40;
|
|
const Uint32 fragmentLength = 1;
|
|
const Uint32 print = 0;
|
|
const Uint32 len = 5 + numSections;
|
|
SimpleSignal request(false);
|
|
|
|
Uint32* signalBody = request.getDataPtrSend();
|
|
signalBody[0] = ss->getOwnRef();
|
|
signalBody[1] = testType;
|
|
signalBody[2] = fragmentLength;
|
|
signalBody[3] = print;
|
|
signalBody[4] = 0; /* Return count */
|
|
signalBody[5] = sizes[0];
|
|
signalBody[6] = sizes[1];
|
|
signalBody[7] = sizes[2];
|
|
|
|
|
|
request.ptr[0].sz = sizes[0];
|
|
request.ptr[0].p = &buffer[0];
|
|
request.ptr[1].sz = sizes[1];
|
|
request.ptr[1].p = &buffer[sizes[0]];
|
|
request.ptr[2].sz = sizes[2];
|
|
request.ptr[2].p = &buffer[sizes[0] + sizes[1]];
|
|
|
|
request.header.m_noOfSections= numSections;
|
|
|
|
int rc = 0;
|
|
ss->lock();
|
|
rc = ss->sendFragmentedSignal(ss->get_an_alive_node(),
|
|
request,
|
|
CMVMI,
|
|
GSN_TESTSIG,
|
|
len);
|
|
ss->unlock();
|
|
|
|
if (rc != 0)
|
|
{
|
|
g_err << "Error sending signal" << endl;
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int waitResponse(SignalSender* ss,
|
|
Uint32* expectedSz)
|
|
{
|
|
/* Here we need to wait for all of the signals which
|
|
* comprise a fragmented send, and check that
|
|
* the data is as expected
|
|
*/
|
|
BasicSectionStore bss;
|
|
FragmentAssembler fa(&bss);
|
|
|
|
while(true)
|
|
{
|
|
ss->lock();
|
|
SimpleSignal* response = ss->waitFor(10000);
|
|
ss->unlock();
|
|
|
|
if (!response)
|
|
{
|
|
g_err << "Timed out waiting for response" << endl;
|
|
return -1;
|
|
}
|
|
|
|
//response->print();
|
|
|
|
if (response->header.theVerId_signalNumber == GSN_TESTSIG)
|
|
{
|
|
if (fa.handleSignal(&response->header,
|
|
response->getDataPtr(),
|
|
response->ptr) != 0)
|
|
{
|
|
g_err << "Error assembling fragmented signal."
|
|
<< " Error is "
|
|
<< (Uint32) fa.getError()
|
|
<< endl;
|
|
return -1;
|
|
}
|
|
|
|
if (fa.isComplete())
|
|
{
|
|
Uint32 expectedWord = 0;
|
|
for (Uint32 i=0; i < 3; i++)
|
|
{
|
|
if (bss.ptrs[i].sz != expectedSz[i])
|
|
{
|
|
g_err << "Wrong size for section : "
|
|
<< i
|
|
<< " expected " << expectedSz[i]
|
|
<< " but received " << bss.ptrs[i].sz
|
|
<< endl;
|
|
return -1;
|
|
}
|
|
|
|
for (Uint32 d=0; d < expectedSz[i]; d++)
|
|
{
|
|
if (bss.ptrs[i].p[d] != expectedWord)
|
|
{
|
|
g_err << "Bad data in section "
|
|
<< i
|
|
<< " at word number "
|
|
<< d
|
|
<< ". Expected "
|
|
<< expectedWord
|
|
<< " but found "
|
|
<< bss.ptrs[i].p[d]
|
|
<< endl;
|
|
return -1;
|
|
}
|
|
expectedWord++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int runTest(SignalSender* ss)
|
|
{
|
|
for (Uint32 iter=0;
|
|
iter < getNumIterationsRequired();
|
|
iter++)
|
|
{
|
|
int rc;
|
|
Uint32 sizes[3];
|
|
sizes[0] = getSecSz(0, iter);
|
|
sizes[1] = getSecSz(1, iter);
|
|
sizes[2] = getSecSz(2, iter);
|
|
|
|
/* Build request, including sections */
|
|
rc = sendRequest(ss, sizes);
|
|
if (rc != 0)
|
|
{
|
|
g_err << "Failed sending request on iteration " << iter
|
|
<< " with rc " << rc << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* Wait for response */
|
|
rc = waitResponse(ss, sizes);
|
|
if (rc != 0)
|
|
{
|
|
g_err << "Failed waiting for response on iteration " << iter
|
|
<< " with rc " << rc << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
};
|
|
|
|
|
|
int testFragmentedSend(NDBT_Context* ctx, NDBT_Step* step){
|
|
Ndb* pNdb= GETNDB(step);
|
|
Ndb_cluster_connection* conn = &pNdb->get_ndb_cluster_connection();
|
|
SignalSender ss(conn);
|
|
FragSignalChecker fsc;
|
|
|
|
return fsc.runTest(&ss);
|
|
}
|
|
|
|
static int
|
|
runReceiveTRANSIDAIAfterRollback(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* const ndb = GETNDB(step);
|
|
NdbRestarter restarter;
|
|
|
|
do {
|
|
// fill table with 10 rows.
|
|
const NdbDictionary::Table * pTab = ctx->getTab();
|
|
HugoTransactions hugoTrans(*pTab);
|
|
if(hugoTrans.loadTable(ndb, 10) != 0) {
|
|
g_err << "Failed to load table" << endl;
|
|
break;
|
|
}
|
|
// do error injection in data nodes
|
|
if (restarter.insertErrorInAllNodes(8107) != 0){
|
|
g_err << "Failed to insert error 8107" << endl;
|
|
break;
|
|
}
|
|
if (restarter.insertErrorInAllNodes(4037) != 0){
|
|
g_err << "Failed to insert error 4037" << endl;
|
|
break;
|
|
}
|
|
|
|
// do error injection in ndbapi
|
|
DBUG_SET_INITIAL("+d,ndb_delay_close_txn,ndb_delay_transid_ai");
|
|
|
|
// start transaction
|
|
NdbTransaction* const trans = ndb->startTransaction();
|
|
if (trans == NULL)
|
|
{
|
|
g_err << "ndb->startTransaction() gave unexpected error : "
|
|
<< ndb->getNdbError() << endl;
|
|
break;
|
|
}
|
|
NdbOperation* const op = trans->getNdbOperation(pTab);
|
|
if (op == NULL)
|
|
{
|
|
g_err << "trans->getNdbOperation() gave unexpected error : "
|
|
<< trans->getNdbError() << endl;
|
|
break;
|
|
}
|
|
|
|
// start primary key read with shared lock
|
|
HugoOperations hugoOps(*ctx->getTab());
|
|
if(hugoOps.startTransaction(ndb)) {
|
|
g_err << "hugoOps.startTransaction() gave unexpected error : "
|
|
<< hugoOps.getTransaction()->getNdbError() << endl;
|
|
break;
|
|
}
|
|
if(hugoOps.pkReadRecord(ndb, 1, 1, NdbOperation::LM_Read)) {
|
|
g_err << "hugoOps.pkReadRecord() gave unexpected error : "
|
|
<< hugoOps.getTransaction()->getNdbError() << endl;
|
|
break;
|
|
}
|
|
if(hugoOps.execute_Commit(ndb) != 0) {
|
|
g_err << "hugoOps.execute_Commit() gave unexpected error : "
|
|
<< hugoOps.getTransaction()->getNdbError() << endl;
|
|
break;
|
|
}
|
|
|
|
// all ok, test passes
|
|
ndb->closeTransaction(trans);
|
|
|
|
// clean up
|
|
DBUG_SET_INITIAL("-d,ndb_delay_close_txn,ndb_delay_transid_ai");
|
|
restarter.insertErrorInAllNodes(0);
|
|
return NDBT_OK;
|
|
} while(0);
|
|
|
|
// clean up for error path
|
|
DBUG_SET_INITIAL("-d,ndb_delay_close_txn,ndb_delay_transid_ai");
|
|
restarter.insertErrorInAllNodes(0);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int
|
|
testNdbRecordSpecificationCompatibility(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Test for checking the compatibility of RecordSpecification
|
|
* when compiling old code with newer header.
|
|
* Create an instance of RecordSpecification_v1 and try to pass
|
|
* it to the NdbApi createRecord.
|
|
*/
|
|
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table* pTab= ctx->getTab();
|
|
int numCols= pTab->getNoOfColumns();
|
|
const NdbRecord* defaultRecord= pTab->getDefaultRecord();
|
|
|
|
NdbDictionary::RecordSpecification_v1 rsArray[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
|
|
|
|
for (int attrId=0; attrId< numCols; attrId++)
|
|
{
|
|
NdbDictionary::RecordSpecification_v1& rs= rsArray[attrId];
|
|
|
|
rs.column= pTab->getColumn(attrId);
|
|
rs.offset= 0;
|
|
rs.nullbit_byte_offset= 0;
|
|
rs.nullbit_bit_in_byte= 0;
|
|
CHECK(NdbDictionary::getOffset(defaultRecord,
|
|
attrId,
|
|
rs.offset));
|
|
CHECK(NdbDictionary::getNullBitOffset(defaultRecord,
|
|
attrId,
|
|
rs.nullbit_byte_offset,
|
|
rs.nullbit_bit_in_byte));
|
|
}
|
|
const NdbRecord* tabRec= pNdb->getDictionary()->createRecord(pTab,
|
|
(NdbDictionary::RecordSpecification*)rsArray,
|
|
numCols,
|
|
sizeof(NdbDictionary::RecordSpecification_v1));
|
|
CHECK(tabRec != 0);
|
|
|
|
char keyRowBuf[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
char attrRowBuf[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
bzero(keyRowBuf, sizeof(keyRowBuf));
|
|
bzero(attrRowBuf, sizeof(attrRowBuf));
|
|
|
|
HugoCalculator calc(*pTab);
|
|
|
|
const int numRecords= 100;
|
|
|
|
for (int record=0; record < numRecords; record++)
|
|
{
|
|
int updates= 0;
|
|
/* calculate the Hugo values for this row */
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
keyRowBuf,
|
|
col);
|
|
CHECK(valPtr != NULL);
|
|
int len= pTab->getColumn(col)->getSizeInBytes();
|
|
Uint32 real_len;
|
|
bool isNull= (calc.calcValue(record, col, updates, valPtr,
|
|
len, &real_len) == NULL);
|
|
if (pTab->getColumn(col)->getNullable())
|
|
{
|
|
NdbDictionary::setNull(tabRec,
|
|
keyRowBuf,
|
|
col,
|
|
isNull);
|
|
}
|
|
}
|
|
|
|
/* insert the row */
|
|
NdbTransaction* trans=pNdb->startTransaction();
|
|
CHECK(trans != 0);
|
|
CHECK(trans->getNdbError().code == 0);
|
|
|
|
const NdbOperation* op= NULL;
|
|
op= trans->insertTuple(tabRec,
|
|
keyRowBuf);
|
|
CHECK(op != 0);
|
|
|
|
CHECK(trans->execute(Commit) == 0);
|
|
trans->close();
|
|
|
|
/* Now read back */
|
|
Uint32 pkVal= 0;
|
|
memcpy(&pkVal, NdbDictionary::getValuePtr(tabRec,
|
|
keyRowBuf,
|
|
0),
|
|
sizeof(pkVal));
|
|
|
|
trans= pNdb->startTransaction();
|
|
op= trans->readTuple(tabRec,
|
|
keyRowBuf,
|
|
tabRec,
|
|
attrRowBuf);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
CHECK(trans->getNdbError().code == 0);
|
|
trans->close();
|
|
|
|
/* Verify the values read back */
|
|
for (int col=0; col<pTab->getNoOfColumns(); col++)
|
|
{
|
|
const char* valPtr= NdbDictionary::getValuePtr(tabRec,
|
|
attrRowBuf,
|
|
col);
|
|
CHECK(valPtr != NULL);
|
|
|
|
char calcBuff[ NDB_MAX_TUPLE_SIZE_IN_WORDS << 2 ];
|
|
int len= pTab->getColumn(col)->getSizeInBytes();
|
|
Uint32 real_len;
|
|
bool isNull= (calc.calcValue(record, col, updates, calcBuff,
|
|
len, &real_len) == NULL);
|
|
bool colIsNullable= pTab->getColumn(col)->getNullable();
|
|
if (isNull)
|
|
{
|
|
CHECK(colIsNullable);
|
|
if (!NdbDictionary::isNull(tabRec,
|
|
attrRowBuf,
|
|
col))
|
|
{
|
|
ndbout << "Error, col " << col
|
|
<< " (pk=" << pTab->getColumn(col)->getPrimaryKey()
|
|
<< ") should be Null, but is not" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (colIsNullable)
|
|
{
|
|
if (NdbDictionary::isNull(tabRec,
|
|
attrRowBuf,
|
|
col))
|
|
{
|
|
ndbout << "Error, col " << col
|
|
<< " (pk=" << pTab->getColumn(col)->getPrimaryKey()
|
|
<< ") should be non-Null but is null" << endl;
|
|
return NDBT_FAILED;
|
|
};
|
|
}
|
|
|
|
/* Compare actual data read back */
|
|
if( memcmp(calcBuff, valPtr, real_len) != 0 )
|
|
{
|
|
ndbout << "Error, col " << col
|
|
<< " (pk=" << pTab->getColumn(col)->getPrimaryKey()
|
|
<< ") should be equal, but isn't for record "
|
|
<< record << endl;
|
|
ndbout << "Expected :";
|
|
for (Uint32 i=0; i < real_len; i++)
|
|
{
|
|
ndbout_c("%x ", calcBuff[i]);
|
|
}
|
|
ndbout << endl << "Received :";
|
|
for (Uint32 i=0; i < real_len; i++)
|
|
{
|
|
ndbout_c("%x ", valPtr[i]);
|
|
}
|
|
ndbout << endl;
|
|
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now delete the tuple */
|
|
trans= pNdb->startTransaction();
|
|
op= trans->deleteTuple(tabRec,
|
|
keyRowBuf,
|
|
tabRec);
|
|
CHECK(op != 0);
|
|
CHECK(trans->execute(Commit) == 0);
|
|
|
|
trans->close();
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int testSchemaObjectOwnerCheck(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* const ndb = GETNDB(step);
|
|
Ndb *otherNdb = NULL;
|
|
NdbDictionary::Dictionary* const dict = ndb->getDictionary();
|
|
NdbTransaction *trans = ndb->startTransaction();
|
|
NdbRestarter restarter;
|
|
int result = NDBT_OK;
|
|
|
|
do
|
|
{
|
|
ndbout << "Creating table with index" << endl;
|
|
NdbDictionary::Table tab;
|
|
NdbDictionary::Index idx;
|
|
tab.setName("SchemaObjOwnerCheck_tab");
|
|
tab.setLogging(true);
|
|
|
|
// create column
|
|
NdbDictionary::Column col("col1");
|
|
col.setType(NdbDictionary::Column::Unsigned);
|
|
col.setPrimaryKey(true);
|
|
tab.addColumn(col);
|
|
|
|
// create index on column
|
|
idx.setTable("SchemaObjOwnerCheck_tab");
|
|
idx.setName("SchemaObjOwnerCheck_idx");
|
|
idx.setType(NdbDictionary::Index::UniqueHashIndex);
|
|
idx.setLogging(false);
|
|
idx.addColumnName("col1");
|
|
|
|
NdbError error;
|
|
if(tab.validate(error) == -1)
|
|
{
|
|
ndbout << "Failed to create table" << endl;
|
|
break;
|
|
}
|
|
|
|
if (dict->createTable(tab) == -1) {
|
|
g_err << "Failed to create SchemaObjOwnerCheck_tab table." << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
if (dict->createIndex(idx) == -1) {
|
|
g_err << "Failed to create index, error: " << dict->getNdbError() << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
ndbout << "Setting up other connection to acquire schema objects." << endl;
|
|
char connectString[256];
|
|
ctx->m_cluster_connection.get_connectstring(connectString,
|
|
sizeof(connectString));
|
|
otherConnection= new Ndb_cluster_connection(connectString);
|
|
if (otherConnection == NULL)
|
|
{
|
|
ndbout << "otherConnection is null" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
int rc= otherConnection->connect();
|
|
if (rc != 0)
|
|
{
|
|
ndbout << "Connect of otherConnection failed with rc " << rc << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
if (otherConnection->wait_until_ready(10,10) != 0)
|
|
{
|
|
ndbout << "Cluster connection otherConnection was not ready" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
otherNdb = new Ndb(otherConnection, "TEST_DB");
|
|
if(!otherNdb)
|
|
{
|
|
ndbout << "Failed to acquire Ndb object from otherConnection" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
otherNdb->init();
|
|
if(otherNdb->waitUntilReady(10) != 0)
|
|
{
|
|
ndbout << "Failed to init Ndb object from otherConnection" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
const NdbDictionary::Table *otherTable = otherNdb->getDictionary()->getTable("SchemaObjOwnerCheck_tab");
|
|
if(!otherTable)
|
|
{
|
|
ndbout << "Failed to get Ndb table from otherConnection" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
const NdbDictionary::Index *otherIndex = otherNdb->getDictionary()->getIndex("SchemaObjOwnerCheck_idx", "SchemaObjOwnerCheck_tab");
|
|
if(!otherIndex)
|
|
{
|
|
ndbout << "Failed to get Ndb index from otherConnection" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
ndbout << "Enabling schema object ownership check on ctx connection" << endl;
|
|
trans->setSchemaObjOwnerChecks(true);
|
|
|
|
ndbout << "Attempting to acquire Ndb*Operations on schema objects ";
|
|
ndbout << "which belong to other connection" << endl;
|
|
NdbOperation *op = trans->getNdbOperation(otherTable);
|
|
const NdbError err1 = trans->getNdbError();
|
|
if(err1.code != 1231)
|
|
{
|
|
ndbout << "Failed to detect Table with wrong owner for NdbOperation" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
NdbScanOperation *scanop = trans->getNdbScanOperation(otherTable);
|
|
const NdbError err2 = trans->getNdbError();
|
|
if(err2.code != 1231)
|
|
{
|
|
ndbout << "Failed to detect Table with wrong owner for NdbScanOperation" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
NdbIndexScanOperation *idxscanop = trans->getNdbIndexScanOperation(otherIndex, otherTable);
|
|
const NdbError err3 = trans->getNdbError();
|
|
if(err3.code != 1231)
|
|
{
|
|
ndbout << "Failed to detect Table/Index with wrong owner for NdbIndexScanOperation" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
NdbIndexOperation *idxop = trans->getNdbIndexOperation(otherIndex);
|
|
const NdbError err4 = trans->getNdbError();
|
|
if(err4.code != 1231)
|
|
{
|
|
ndbout << "Failed to detect Index with wrong owner for NdbIndexOperation" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
ndbout << "Success: ownership check detected wrong owner" << endl;
|
|
|
|
ndbout << "Disabling schema object ownership check on valid connection" << endl;
|
|
trans->setSchemaObjOwnerChecks(false);
|
|
|
|
ndbout << "Attempting to acquire Ndb*Operations ";
|
|
ndbout << "on valid schema objects from other connection" << endl;
|
|
op = trans->getNdbOperation(otherTable);
|
|
scanop = trans->getNdbScanOperation(otherTable);
|
|
idxscanop = trans->getNdbIndexScanOperation(otherIndex, otherTable);
|
|
idxop = trans->getNdbIndexOperation(otherIndex);
|
|
|
|
if(!op || !scanop || !idxscanop || !idxop) // failure to acquire at least one op
|
|
{
|
|
ndbout << "Failed to acquire ";
|
|
if(!op) ndbout << "NdbOperation, ";
|
|
if(!scanop) ndbout << "NdbScanOperation, ";
|
|
if(!idxscanop) ndbout << "NdbIndexScanOperation, ";
|
|
if(!idxop) ndbout << "NdbIndexOperation, ";
|
|
ndbout << "error: " << trans->getNdbError().message << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
ndbout << "Success: ownership check skipped, wrong owner not detected" << endl;
|
|
|
|
ndbout << "Enabling schema object ownership check on valid connection" << endl;
|
|
trans->setSchemaObjOwnerChecks(true);
|
|
|
|
ndbout << "Acquiring schema objects from current connection" << endl;
|
|
const NdbDictionary::Table *table = ndb->getDictionary()->getTable("SchemaObjOwnerCheck_tab");
|
|
if(!table)
|
|
{
|
|
ndbout << "Failed to get Ndb table from connection" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
const NdbDictionary::Index *index = ndb->getDictionary()->getIndex("SchemaObjOwnerCheck_idx", "SchemaObjOwnerCheck_tab");
|
|
if(!index)
|
|
{
|
|
ndbout << "Failed to get Ndb index from connection" << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
ndbout << "Attempting to acquire Ndb*Operations ";
|
|
ndbout << "on owned schema objects with different db" << endl;
|
|
ndb->setDatabaseName("notexist");
|
|
NdbOperation *op2 = trans->getNdbOperation(table);
|
|
NdbScanOperation *scanop2 = trans->getNdbScanOperation(table);
|
|
NdbIndexScanOperation *idxscanop2 = trans->getNdbIndexScanOperation(index, table);
|
|
NdbIndexOperation *idxop2 = trans->getNdbIndexOperation(index, table);
|
|
|
|
if(!op2 || !scanop2 || !idxscanop2 || !idxop2) // failure to acquire at least one op
|
|
{
|
|
ndbout << "Failed to acquire ";
|
|
if(!op) ndbout << "NdbOperation, ";
|
|
if(!scanop) ndbout << "NdbScanOperation, ";
|
|
if(!idxscanop) ndbout << "NdbIndexScanOperation, ";
|
|
if(!idxop) ndbout << "NdbIndexOperation, ";
|
|
ndbout << "error: " << trans->getNdbError().message << endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
ndbout << "Success: acquired Ndb*Operations on owned schema objects" << endl;
|
|
} while(false);
|
|
|
|
ndbout << "Cleanup" << endl;
|
|
ndb->setDatabaseName("TEST_DB");
|
|
if (dict->dropIndex("SchemaObjOwnerCheck_idx", "SchemaObjOwnerCheck_tab") == -1)
|
|
{
|
|
g_err << "Failed to drop SchemaObjOwnerCheck_idx index." << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
if (dict->dropTable("SchemaObjOwnerCheck_tab") == -1)
|
|
{
|
|
g_err << "Failed to drop SchemaObjOwnerCheck_tab table." << endl;
|
|
result = NDBT_FAILED;
|
|
}
|
|
|
|
trans->setSchemaObjOwnerChecks(false);
|
|
ndb->closeTransaction(trans);
|
|
|
|
if(otherNdb)
|
|
{
|
|
delete otherNdb;
|
|
otherNdb = NULL;
|
|
}
|
|
if(otherConnection)
|
|
{
|
|
delete otherConnection;
|
|
otherConnection = NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int
|
|
testMgmdSendBufferExhaust(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* 1 : Get MGMD node id
|
|
* 2 : Get a data node node id
|
|
* 3 : Consume most SB in MGMD
|
|
* 4 : Block sending from MGMD -> data node
|
|
* 5 : Observe whether MGMD is alive + well
|
|
* 6 : Unblock sending
|
|
* 7 : Release SB
|
|
* 8 : Completed
|
|
*/
|
|
NdbRestarter restarter;
|
|
int result = NDBT_OK;
|
|
|
|
int dataNodeId = restarter.getNode(NdbRestarter::NS_RANDOM);
|
|
int mgmdNodeId = ndb_mgm_get_mgmd_nodeid(restarter.handle);
|
|
|
|
ndbout << "MGMD node id : " << mgmdNodeId << endl;
|
|
ndbout << "Data node id : " << dataNodeId << endl;
|
|
|
|
ndbout << "Reducing MGMD SB memory + blocking send to data node" << endl;
|
|
const int leftSbBytes = 96 * 1024;
|
|
const int dumpCodeConsumeSb [] = {9996, leftSbBytes};
|
|
const int dumpCodeBlockSend [] = {9994, dataNodeId};
|
|
CHECK(restarter.dumpStateOneNode(mgmdNodeId, dumpCodeConsumeSb, 2) == 0);
|
|
CHECK(restarter.dumpStateOneNode(mgmdNodeId, dumpCodeBlockSend, 2) == 0);
|
|
|
|
ndbout << "Checking ability of MGMD to respond to requests" << endl;
|
|
|
|
Uint32 count = 30;
|
|
|
|
while (count--)
|
|
{
|
|
ndbout << " - Getting node status " << count;
|
|
ndb_mgm_cluster_state* state = ndb_mgm_get_status(restarter.handle);
|
|
if (state == NULL)
|
|
{
|
|
ndbout << "ndb_mgm_get_status failed"
|
|
<< ", error: " << ndb_mgm_get_latest_error(restarter.handle)
|
|
<< " - " << ndb_mgm_get_latest_error_msg(restarter.handle)
|
|
<< endl;
|
|
result = NDBT_FAILED;
|
|
break;
|
|
}
|
|
|
|
ndbout << " - ok." << endl;
|
|
free(state);
|
|
NdbSleep_MilliSleep(1000);
|
|
}
|
|
|
|
ndbout << "Cleaning up" << endl;
|
|
const int dumpCodeUnblockSend [] = {9995, dataNodeId};
|
|
const int dumpCodeReleaseSb [] = {9997};
|
|
CHECK(restarter.dumpStateOneNode(mgmdNodeId, dumpCodeUnblockSend, 2) == 0);
|
|
CHECK(restarter.dumpStateOneNode(mgmdNodeId, dumpCodeReleaseSb, 1) == 0);
|
|
CHECK(ndb_mgm_get_latest_error(restarter.handle) == 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Create Unique Index in the given table using ndbapi
|
|
* Returns
|
|
* NDBT_OK if index creation was successful
|
|
* NDBT_FAILED if the index creation failed
|
|
* */
|
|
int
|
|
createUniqueIndex(NdbDictionary::Dictionary* pDict,
|
|
const char* tableName,
|
|
const char* indexName,
|
|
const char* columnName)
|
|
{
|
|
/* create a new index on the table */
|
|
NdbDictionary::Index tmpIndex;
|
|
tmpIndex.setName(indexName);
|
|
tmpIndex.setTable(tableName);
|
|
tmpIndex.setType(NdbDictionary::Index::UniqueHashIndex);
|
|
tmpIndex.setLogging(false);
|
|
tmpIndex.addIndexColumn(columnName);
|
|
|
|
/* create an index on the table */
|
|
ndbout << "Creating index " << indexName
|
|
<< " on " << tableName << endl;
|
|
CHECKN(pDict->createIndex(tmpIndex) == 0, pDict, NDBT_FAILED);
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/**
|
|
* Runs a transaction using the passed index data.
|
|
* Returns the errorCode on failure. 0 on success.
|
|
*/
|
|
int
|
|
runTransactionUsingNdbIndexOperation(Ndb* pNdb,
|
|
Vector<const NdbDictionary::Index*> pIndexes,
|
|
const NdbDictionary::Table *tab)
|
|
{
|
|
/*
|
|
* 1. Start a transaction and fetch NdbIndexOperations using the
|
|
* sent indexes.
|
|
* 2. Execute the transaction.
|
|
* 3. Return the error-code or 0
|
|
*/
|
|
/* start a transaction */
|
|
NdbTransaction *pTransaction= pNdb->startTransaction();
|
|
CHECKN(pTransaction != NULL, pNdb, pNdb->getNdbError().code);
|
|
|
|
for(uint i = 0; i < pIndexes.size(); i++){
|
|
/* use the obsolete index to fetch a NdbIndexOperation */
|
|
NdbIndexOperation *pIndexOperation=
|
|
pTransaction->getNdbIndexOperation(pIndexes[i], tab);
|
|
CHECKN(pIndexOperation != NULL, pTransaction,
|
|
pTransaction->getNdbError().code);
|
|
|
|
/* add where field */
|
|
pIndexOperation->readTuple(NdbOperation::LM_Read);
|
|
pIndexOperation->equal(pIndexes[i]->getColumn(0)->getName(), 10);
|
|
|
|
/* add select field */
|
|
NdbRecAttr *pRecAttr= pIndexOperation->getValue(1, NULL);
|
|
CHECKN(pRecAttr != NULL, pTransaction, pTransaction->getNdbError().code);
|
|
}
|
|
|
|
/* execute the transaction */
|
|
ndbout << "Executing the transaction." << endl;
|
|
if(pTransaction->execute( NdbTransaction::Commit,
|
|
NdbOperation::AbortOnError) == -1)
|
|
{
|
|
/* Transaction failed. */
|
|
NdbError ndbError = pTransaction->getNdbError();
|
|
/* Ignore - Tuple did not exist errors */
|
|
if(ndbError.code != 626)
|
|
{
|
|
pNdb->closeTransaction(pTransaction);
|
|
NDB_ERR(ndbError);
|
|
return ndbError.code;
|
|
}
|
|
}
|
|
pNdb->closeTransaction(pTransaction);
|
|
ndbout << "Transaction ran successfully." << endl;
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runGetNdbIndexOperationTest(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/**
|
|
* 1. Obtain the index using getIndex()
|
|
* 2. Drop that index from that table.
|
|
* 3. Execute transaction using that index
|
|
* 5. Verify that the transaction returns error code 284.
|
|
* 6. Create another index - this will take the same index id as the
|
|
* one previously dropped.
|
|
* 7. Repeat 3 the previously dropped index object.
|
|
* 8. Verify that the transaction returns error code 241.
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const char* tableName = "I3";
|
|
const char* indexName = "I3$NDBT_IDX0";
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
|
|
|
|
/* load the index */
|
|
const NdbDictionary::Index *pIndex;
|
|
Vector<const NdbDictionary::Index*> pIndexes;
|
|
CHECKN((pIndex = pDict->getIndex(indexName,tableName)) != NULL,
|
|
pDict, NDBT_FAILED);
|
|
pIndexes.push_back(pIndex);
|
|
/* drop the index from the table */
|
|
ndbout << "Dropping index " << indexName << " from " << tableName << endl;
|
|
CHECKN(pDict->dropIndexGlobal(*pIndex) == 0, pDict, NDBT_FAILED);
|
|
/*
|
|
perform a transaction using the dropped index
|
|
Expected Error : 284 - Table not defined in transaction coordinator
|
|
*/
|
|
if(runTransactionUsingNdbIndexOperation(pNdb, pIndexes, tab) != 284)
|
|
{
|
|
ndberr << "Transaction was supposed to fail with error 284 but didn't."
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* create a new index on the table */
|
|
CHECK(createUniqueIndex(pDict, tableName, indexName,
|
|
pIndex->getColumn(0)->getName()) != NDBT_FAILED);
|
|
|
|
/*
|
|
perform a transaction using the dropped index
|
|
Expected Error : 241 - Invalid schema object version
|
|
*/
|
|
if(runTransactionUsingNdbIndexOperation(pNdb, pIndexes, tab) != 241)
|
|
{
|
|
ndberr << "Transaction was supposed to fail with error 241 but didn't."
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runCreateIndexesOnI3(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Create indexes on table I3 */
|
|
Ndb* pNdb = GETNDB(step);
|
|
const char* tableName = "I3";
|
|
const uint numOfIndexes = 4;
|
|
const char* columnNames[] = {"PORT", "MAC", "HOSTNAME", "GW"};
|
|
ctx->setProperty("numOfIndexes", numOfIndexes);
|
|
NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
|
|
|
|
/* create the indexes */
|
|
Vector<const NdbDictionary::Index*> pIndexes;
|
|
for(uint i = 0; i < numOfIndexes; i++)
|
|
{
|
|
BaseString name;
|
|
name.assfmt("I3$NDBT_UIDX%d", i);
|
|
CHECK(createUniqueIndex(pDict, tableName, name.c_str(),
|
|
columnNames[i]) != NDBT_FAILED);
|
|
}
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runGetNdbIndexOperationBatchTest(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/**
|
|
* 1. In a loop, use all the indexes to perform batch transactions
|
|
* but, drop an index at every turn at different positions.
|
|
* 2. Verify that the transactions fail with expected error.
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const char* tableName = "I3";
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
|
|
const uint numOfIndexes = ctx->getProperty("numOfIndexes");
|
|
|
|
/* load the indexes */
|
|
Vector<const NdbDictionary::Index*> pIndexes;
|
|
for(uint i = 0; i < numOfIndexes; i++)
|
|
{
|
|
BaseString name;
|
|
const NdbDictionary::Index *pIndex;
|
|
name.assfmt("I3$NDBT_UIDX%d", i);
|
|
CHECKN((pIndex = pDict->getIndex(name.c_str(),tableName)) != NULL,
|
|
pDict, NDBT_FAILED);
|
|
pIndexes.push_back(pIndex);
|
|
}
|
|
|
|
/* start batch operations */
|
|
ndbout << "Starting batch transactions." << endl;
|
|
for(uint i=0; i < numOfIndexes; i++)
|
|
{
|
|
/* drop ith index */
|
|
ndbout << "Dropping index " << pIndexes[i]->getName()
|
|
<< " from " << tableName << endl;
|
|
CHECKN(pDict->dropIndexGlobal(*pIndexes[i]) == 0, pDict, NDBT_FAILED);
|
|
|
|
/* run batch operations in a loop,
|
|
* changing the position of dropped indexes every time */
|
|
for(uint loops = 0; loops < numOfIndexes; loops++)
|
|
{
|
|
/*
|
|
perform a transaction using the dropped index
|
|
Expected Error : 284 - Table not defined in transaction coordinator
|
|
*/
|
|
if(runTransactionUsingNdbIndexOperation(pNdb, pIndexes, tab) != 284)
|
|
{
|
|
ndberr << "Transaction was supposed to fail with error 284 but didn't."
|
|
<< endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/* rotate positions of obsolete indexes */
|
|
pIndexes.push_back(pIndexes[0]);
|
|
pIndexes.erase(0);
|
|
}
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runGetNdbIndexOperationTransactions(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/**
|
|
* 1. In a loop, use all the indexes to perform batch transactions
|
|
* 2. Verify that the transactions fail with one of the expected error.
|
|
*/
|
|
Ndb* pNdb = GETNDB(step);
|
|
const char* tableName = "I3";
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
|
|
const uint numOfIndexes = ctx->getProperty("numOfIndexes");
|
|
|
|
/* start batch operations */
|
|
ndbout << "Starting batch transactions." << endl;
|
|
uint l = 0;
|
|
while(ctx->getProperty("StopTransactions") == 0)
|
|
{
|
|
Vector<const NdbDictionary::Index*> pIndexes;
|
|
if(l++ % 50 == 0)
|
|
{
|
|
/* load the indexes every 50th loop */
|
|
pIndexes.clear();
|
|
for(uint i = 0; i < numOfIndexes; i++)
|
|
{
|
|
BaseString name;
|
|
const NdbDictionary::Index *pIndex;
|
|
name.assfmt("I3$NDBT_UIDX%d", i);
|
|
pIndex = pDict->getIndex(name.c_str(),tableName);
|
|
if(pIndex != NULL)
|
|
pIndexes.push_back(pIndex);
|
|
}
|
|
}
|
|
|
|
/*
|
|
perform a transaction
|
|
Expected Errors : 284 - Table not defined in transaction coordinator (or)
|
|
241 - Invalid schema object version (or)
|
|
283/1226 - Table is being dropped
|
|
*/
|
|
int result = runTransactionUsingNdbIndexOperation(pNdb, pIndexes, tab);
|
|
if(result != NDBT_OK && result != 241 && result != 284 &&
|
|
result != 283 && result != 1226)
|
|
{
|
|
/* Transaction failed with an unexpected error */
|
|
ndberr << "Transaction failed with an unexpected error : " << result << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runDropIndexesOnI3(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* pNdb = GETNDB(step);
|
|
const char* tableName = "I3";
|
|
NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
|
|
const uint numOfIndexes = ctx->getProperty("numOfIndexes");
|
|
uint loops = ctx->getNumLoops();
|
|
|
|
while(loops-- > 0)
|
|
{
|
|
for(uint i =0; i < numOfIndexes; i++)
|
|
{
|
|
BaseString name;
|
|
name.assfmt("I3$NDBT_UIDX%d", i);
|
|
/* drop the index. */
|
|
ndbout << "Dropping index " << name.c_str()
|
|
<< " from " << tableName << endl;
|
|
CHECKN(pDict->dropIndex(name.c_str(), tableName) == 0,
|
|
pDict, NDBT_FAILED);
|
|
|
|
/* sleep for a random ms */
|
|
const int max_sleep = 100;
|
|
NdbSleep_MilliSleep(rand() % max_sleep);
|
|
}
|
|
|
|
/* recreate the indexes and start again */
|
|
runCreateIndexesOnI3(ctx, step);
|
|
}
|
|
ctx->setProperty("StopTransactions", 1);
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
static void unusedCallback(int, NdbTransaction*, void*)
|
|
{}
|
|
|
|
/**
|
|
* Test that Ndb::closeTransaction() and/or Ndb-d'tor is
|
|
* able to do propper cleanup of NdbTransactions which
|
|
* are in some 'incomplete' states:
|
|
* - Transactions being closed before executed.
|
|
* - Transactions being closed without, or only partially
|
|
* defined operations.
|
|
* - Transactions being closed with prepared async operations
|
|
* not yet executed.
|
|
* - Ndb instance destructed with NdbTransactions still open
|
|
* or in 'incomplete' states as described above.
|
|
*
|
|
* Pass verification is no unexpected errors being returned,
|
|
* no asserts hit (Normally found in Ndb::free_list's), and
|
|
* no datanode crashed. (All of these used to be a problem!)
|
|
*/
|
|
int runTestNoExecute(NDBT_Context* ctx, NDBT_Step* step){
|
|
int result = NDBT_OK;
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
{
|
|
Ndb ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
}
|
|
{
|
|
Ndb ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (ndb.init()){
|
|
NDB_ERR(ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
Ndb* pNdb = NULL;
|
|
NdbConnection* pCon = NULL;
|
|
for (int i = 0; i < 1000; i++)
|
|
{
|
|
if (pNdb == NULL)
|
|
{
|
|
pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
|
|
if (pNdb == NULL){
|
|
ndbout << "pNdb == NULL" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (pNdb->init()){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
pCon = pNdb->startTransaction();
|
|
if (pCon == NULL){
|
|
NDB_ERR(pNdb->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
const int testcase = ((i >> 2) % 10);
|
|
switch (testcase)
|
|
{
|
|
case 0: //Do nothing
|
|
break;
|
|
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
{
|
|
NdbOperation *pOp = pCon->getNdbOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (testcase == 1)
|
|
break;
|
|
|
|
if (pOp->readTuple() != 0){
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (testcase == 2)
|
|
break;
|
|
|
|
if (pOp->getLockHandle() == NULL){
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (testcase == 3)
|
|
break;
|
|
|
|
pCon->executeAsynchPrepare(NdbTransaction::Commit, &unusedCallback, NULL);
|
|
if (testcase == 4)
|
|
break;
|
|
|
|
pNdb->sendPollNdb(0, 0);
|
|
break;
|
|
}
|
|
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
{
|
|
NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName());
|
|
if (pOp == NULL){
|
|
NDB_ERR(pCon->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (testcase == 6)
|
|
break;
|
|
|
|
if (pOp->readTuples() != 0){
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (testcase == 7)
|
|
break;
|
|
|
|
if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL){
|
|
NDB_ERR(pOp->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
if (testcase == 8)
|
|
break;
|
|
|
|
if (pCon->execute(Commit) != 0){
|
|
NDB_ERR(pCon->getNdbError());
|
|
delete pNdb;
|
|
return NDBT_FAILED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((i >> 0) & 0x01)
|
|
{
|
|
pNdb->closeTransaction(pCon);
|
|
pCon = NULL;
|
|
}
|
|
if ((i >> 1) & 0x01)
|
|
{
|
|
delete pNdb;
|
|
pNdb = NULL;
|
|
pCon = NULL;
|
|
}
|
|
}
|
|
delete pNdb;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int
|
|
runCheckTransId(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* stepNdb = GETNDB(step);
|
|
Ndb_cluster_connection* ncc = &stepNdb->get_ndb_cluster_connection();
|
|
|
|
/**
|
|
* Coverage of problem in bug#23709232
|
|
*
|
|
* Shared 'max transid' concept assumes that when a block
|
|
* reference is reused, the old Ndb's 'max transid' is passed
|
|
* to the new Ndb.
|
|
* However this had a bug, exposed by interleaving of
|
|
* Ndb(), Ndb->init(), and ~Ndb(), which might be expected
|
|
* to occur in any multithreaded environment.
|
|
*/
|
|
|
|
Ndb* ndb1 = new Ndb(ncc); // Init transid from connection
|
|
|
|
ndb1->init(); // Determine block-ref
|
|
|
|
NdbTransaction* trans1 = ndb1->startTransaction();
|
|
Uint64 transId1 = trans1->getTransactionId();
|
|
trans1->close();
|
|
|
|
ndbout << "Transid1 : " << transId1 << endl;
|
|
|
|
Ndb* ndb2 = new Ndb(ncc); // Init transid from connection
|
|
|
|
delete ndb1; // Free block-ref
|
|
|
|
ndb2->init(); // Determine block-ref
|
|
|
|
NdbTransaction* trans2 = ndb2->startTransaction();
|
|
Uint64 transId2 = trans2->getTransactionId();
|
|
trans2->close();
|
|
|
|
ndbout << "Transid2 : " << transId2 << endl;
|
|
|
|
delete ndb2;
|
|
|
|
if (transId1 == transId2)
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
/* CheckTransIdMt
|
|
* Can control threading + iterations here
|
|
*/
|
|
const int CheckTransIdSteps = 8;
|
|
const Uint32 CheckTransIdIterations = 10000;
|
|
const Uint32 CheckTransIdEntries = CheckTransIdSteps * CheckTransIdIterations;
|
|
|
|
static Uint64* g_checkTransIdArrays;
|
|
|
|
int
|
|
runInitCheckTransIdMt(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
g_checkTransIdArrays = new Uint64[CheckTransIdEntries];
|
|
|
|
ndbout << "Running" << endl;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runCheckTransIdMt(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* stepNdb = GETNDB(step);
|
|
Ndb_cluster_connection* ncc = &stepNdb->get_ndb_cluster_connection();
|
|
|
|
Uint32 stepIdx = step->getStepNo() - 1;
|
|
Uint64* myIds = g_checkTransIdArrays + (stepIdx * CheckTransIdIterations);
|
|
|
|
for (Uint32 i=0; i<CheckTransIdIterations; i++)
|
|
{
|
|
/* New Ndb, create a transaction, get id, close it, delete Ndb */
|
|
Ndb newNdb(ncc);
|
|
newNdb.init();
|
|
|
|
NdbTransaction* newTrans = newNdb.startTransaction();
|
|
myIds[i] = newTrans->getTransactionId();
|
|
newTrans->close();
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int cmpUint64(const void* a, const void* b)
|
|
{
|
|
Uint64 va = *((const Uint64*)a);
|
|
Uint64 vb = *((const Uint64*)b);
|
|
|
|
return ((va > vb)? 1 :
|
|
(vb > va)? -1 :
|
|
0);
|
|
}
|
|
|
|
int
|
|
runVerifyCheckTransIdMt(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Look for duplicates */
|
|
ndbout << "Checking" << endl;
|
|
|
|
/* First sort */
|
|
qsort(g_checkTransIdArrays, CheckTransIdEntries, sizeof(Uint64), cmpUint64);
|
|
|
|
int result = NDBT_OK;
|
|
Uint32 contigCount = 0;
|
|
Uint32 errorCount = 0;
|
|
Uint32 maxContigError = 0;
|
|
Uint32 contigErrorCount = 0;
|
|
|
|
/* Then check */
|
|
for (Uint32 i=1; i<CheckTransIdEntries; i++)
|
|
{
|
|
//ndbout << g_checkTransIdArrays[i-1] << endl;
|
|
if (g_checkTransIdArrays[i] == g_checkTransIdArrays[i-1])
|
|
{
|
|
ndbout << "Error : Duplicate transid found "
|
|
<< " (" << g_checkTransIdArrays[i]
|
|
<< ")" << endl;
|
|
errorCount ++;
|
|
contigErrorCount++;
|
|
|
|
result = NDBT_FAILED;
|
|
}
|
|
else
|
|
{
|
|
if (contigErrorCount > 0)
|
|
{
|
|
if (contigErrorCount > maxContigError)
|
|
{
|
|
maxContigError = contigErrorCount;
|
|
}
|
|
contigErrorCount = 0;
|
|
}
|
|
if (g_checkTransIdArrays[i] == g_checkTransIdArrays[i-1] + 1)
|
|
{
|
|
contigCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
ndbout << CheckTransIdEntries << " transaction ids of which "
|
|
<< contigCount << " are contiguous, giving "
|
|
<< CheckTransIdEntries - contigCount << " gaps." << endl;
|
|
|
|
ndbout << errorCount << " duplicates found, with max of "
|
|
<< maxContigError + 1 << " uses of the same transaction id" << endl;
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
runFinaliseCheckTransIdMt(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/* Free the storage */
|
|
delete g_checkTransIdArrays;
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runTestColumnNameLookupPerf(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
|
|
ndbout_c("Table lookups on columns in table %s",
|
|
tab->getName());
|
|
|
|
const char* colNames[512];
|
|
for (int c=0; c<tab->getNoOfColumns(); c++)
|
|
{
|
|
colNames[c] = tab->getColumn(c)->getName();
|
|
ndbout_c(" %d %s",
|
|
c, colNames[c]);
|
|
}
|
|
|
|
const Uint32 iterations=10000000;
|
|
for (int c=0; c < tab->getNoOfColumns(); c++)
|
|
{
|
|
const NDB_TICKS start = NdbTick_getCurrentTicks();
|
|
const char* name = colNames[c];
|
|
for (Uint32 i=0; i < iterations; i++)
|
|
{
|
|
const NdbDictionary::Column* col = tab->getColumn(colNames[c]);
|
|
(void) col;
|
|
};
|
|
const Uint64 time = NdbTick_Elapsed(start, NdbTick_getCurrentTicks()).milliSec();
|
|
ndbout_c("Col %u %s : %u iterations in %llu millis",
|
|
c, name, iterations, time);
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runMaybeRestartMaster(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
/**
|
|
* Psuedo-randomly restart the current master node
|
|
* Often in test runs the Master node is the lowest
|
|
* numbered node id due to nodes being iterated.
|
|
*
|
|
* Randomly restarting the Master prior to running a
|
|
* test is one way to avoid tests with do [not] restart
|
|
* the master for always [never] restarting the
|
|
* lowest node id
|
|
*/
|
|
NdbRestarter restarter;
|
|
int masterNodeId = restarter.getMasterNodeId();
|
|
const bool restartMaster = ((rand() % 2) == 0);
|
|
|
|
if (restartMaster)
|
|
{
|
|
ndbout << "Restarting Master node "
|
|
<< masterNodeId
|
|
<< endl;
|
|
|
|
if (restarter.restartOneDbNode(masterNodeId,
|
|
false, // Initial
|
|
true) != 0) // NOSTART
|
|
{
|
|
g_err << "Failed to restart node" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (restarter.waitNodesNoStart(&masterNodeId, 1) != 0)
|
|
{
|
|
g_err << "Failed to wait for NoStart" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (restarter.startNodes(&masterNodeId, 1) != 0)
|
|
{
|
|
g_err << "Failed to start node" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (restarter.waitClusterStarted() != 0)
|
|
{
|
|
g_err << "Failed waiting for node to start" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
ndbout << "Master node restarted" << endl;
|
|
}
|
|
else
|
|
{
|
|
ndbout << "Not restarting Master node "
|
|
<< masterNodeId
|
|
<< endl;
|
|
}
|
|
return NDBT_OK;
|
|
}
|
|
|
|
void
|
|
asyncCallback(int res, NdbTransaction* trans, void* obj)
|
|
{
|
|
|
|
}
|
|
|
|
int
|
|
runTestOldApiScanFinalise(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* pNdb = GETNDB(step);
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
|
|
/**
|
|
* Test behaviour of 'old api' scan prepare + send
|
|
* without subsequent execAsynchPrepare()
|
|
* Note that use of async API with scans is not
|
|
* currently documented, but it is possible.
|
|
*/
|
|
{
|
|
NdbTransaction * trans = pNdb->startTransaction();
|
|
CHECK(trans != NULL);
|
|
|
|
/**
|
|
* Prepare transaction, so that it is considered for
|
|
* sending
|
|
*/
|
|
trans->executeAsynchPrepare(NdbTransaction::NoCommit,
|
|
asyncCallback,
|
|
NULL);
|
|
|
|
/**
|
|
* Now define a scan, which is not prepared
|
|
*/
|
|
|
|
NdbScanOperation* scanOp = trans->getNdbScanOperation(tab);
|
|
CHECK(scanOp != NULL);
|
|
|
|
CHECK(scanOp->readTuples(NdbScanOperation::LM_CommittedRead,
|
|
0,
|
|
16) == 0);
|
|
|
|
for(int a = 0; a<tab->getNoOfColumns(); a++)
|
|
{
|
|
CHECK(scanOp->getValue(tab->getColumn(a)) != 0);
|
|
}
|
|
|
|
/**
|
|
* Now call send and check behaviour
|
|
* Expect :
|
|
* send will finalise + send the scan
|
|
* scan will proceed as expected (no rows in resultset)
|
|
*/
|
|
|
|
CHECK(pNdb->sendPollNdb() != 0);
|
|
|
|
ndbout_c("Trans error : %u %s\n"
|
|
"Scan error : %u %s\n",
|
|
trans->getNdbError().code,
|
|
trans->getNdbError().message,
|
|
scanOp->getNdbError().code,
|
|
scanOp->getNdbError().message);
|
|
|
|
/* Specific error for this case now */
|
|
CHECK(trans->getNdbError().code == 4342);
|
|
CHECK(scanOp->getNdbError().code == 4342);
|
|
|
|
/**
|
|
* Now attempt nextResult
|
|
*/
|
|
int nextRes = scanOp->nextResult();
|
|
|
|
ndbout_c("Next result : %d\n"
|
|
"ScanError : %u %s",
|
|
nextRes,
|
|
scanOp->getNdbError().code,
|
|
scanOp->getNdbError().message);
|
|
CHECK(nextRes == -1);
|
|
CHECK(scanOp->getNdbError().code == 4342); /* Scan defined but not prepared */
|
|
|
|
trans->close();
|
|
}
|
|
|
|
/* Test requires DBUG error injection */
|
|
#ifndef DBUG_OFF
|
|
/**
|
|
* Test behaviour of 'old api' scan finalisation
|
|
* failure
|
|
*/
|
|
{
|
|
NdbTransaction * trans = pNdb->startTransaction();
|
|
CHECK(trans != NULL);
|
|
|
|
NdbScanOperation* scanOp = trans->getNdbScanOperation(tab);
|
|
CHECK(scanOp != NULL);
|
|
|
|
CHECK(scanOp->readTuples(NdbScanOperation::LM_CommittedRead,
|
|
0,
|
|
16) == 0);
|
|
|
|
for(int a = 0; a<tab->getNoOfColumns(); a++)
|
|
{
|
|
CHECK(scanOp->getValue(tab->getColumn(a)) != 0);
|
|
}
|
|
|
|
/* Force failure in finalisation via error-insert */
|
|
DBUG_SET_INITIAL("+d,ndb_scanbuff_oom");
|
|
|
|
int execRes = trans->execute(NdbTransaction::NoCommit,
|
|
NdbOperation::AbortOnError);
|
|
|
|
DBUG_SET_INITIAL("-d,ndb_scanbuff_oom");
|
|
|
|
NdbError transError = trans->getNdbError();
|
|
NdbError scanError1 = scanOp->getNdbError();
|
|
|
|
int nextRes = scanOp->nextResult();
|
|
|
|
NdbError scanError2 = scanOp->getNdbError();
|
|
|
|
ndbout_c("execRes : %d\n"
|
|
"transError : %u %s\n"
|
|
"scanError : %u %s\n"
|
|
"nextRes + scanError : %d %u %s",
|
|
execRes,
|
|
transError.code, transError.message,
|
|
scanError1.code, scanError1.message,
|
|
nextRes,
|
|
scanError2.code, scanError2.message);
|
|
|
|
CHECK(execRes == 0);
|
|
CHECK(transError.code == 4000);
|
|
CHECK(scanError1.code == 4000);
|
|
CHECK(nextRes == -1);
|
|
CHECK(scanError2.code == 4000);
|
|
|
|
trans->close();
|
|
}
|
|
#endif
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int reCreateTableHook(Ndb* ndb,
|
|
NdbDictionary::Table & table,
|
|
int when,
|
|
void* arg)
|
|
{
|
|
if (when == 0)
|
|
{
|
|
NDBT_Context* ctx = (NDBT_Context*) arg;
|
|
|
|
bool readBackup = (ctx->getProperty("CreateRB", Uint32(0)) != 0);
|
|
bool fullyReplicated = (ctx->getProperty("CreateFR", Uint32(0)) != 0);
|
|
|
|
/* Add others as necessary... */
|
|
|
|
if (readBackup)
|
|
{
|
|
ndbout << "rCTH : Setting ReadBackup property" << endl;
|
|
}
|
|
table.setReadBackupFlag(readBackup);
|
|
|
|
if (fullyReplicated)
|
|
{
|
|
ndbout << "rCTH : Setting Fully Replicated property" << endl;
|
|
}
|
|
table.setFullyReplicated(fullyReplicated);
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int
|
|
runReCreateTable(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
/* Drop table by name if it exists */
|
|
NdbDictionary::Table tab = * ctx->getTab();
|
|
NdbDictionary::Dictionary* pDict = GETNDB(step)->getDictionary();
|
|
|
|
BaseString tabName(tab.getName());
|
|
|
|
ndbout << "Dropping table " << tabName << endl;
|
|
|
|
pDict->dropTable(tabName.c_str());
|
|
|
|
ndbout << "Recreating table " << tabName << endl;
|
|
|
|
/* Now re-create, perhaps with different options */
|
|
if (NDBT_Tables::createTable(pNdb,
|
|
tabName.c_str(),
|
|
false,
|
|
false,
|
|
reCreateTableHook,
|
|
ctx) != 0)
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
const NdbDictionary::Table* newTab = pDict->getTable(tabName.c_str());
|
|
|
|
if (newTab == NULL)
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
ctx->setTab(newTab);
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runDropTable(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
Ndb* pNdb = GETNDB(step);
|
|
NdbDictionary::Table tab = * ctx->getTab();
|
|
NdbDictionary::Dictionary* pDict = pNdb->getDictionary();
|
|
|
|
ndbout << "Dropping table " << tab.getName() << endl;
|
|
|
|
pDict->dropTable(tab.getName());
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int runCheckLateDisconnect(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
const NdbDictionary::Table* tab = ctx->getTab();
|
|
HugoTransactions hugoTrans(*tab);
|
|
NdbRestarter restarter;
|
|
//Ndb* pNdb = GETNDB(step);
|
|
|
|
Ndb otherNdb(otherConnection, "TEST_DB");
|
|
otherNdb.init();
|
|
int rc = otherNdb.waitUntilReady(10);
|
|
|
|
if (rc != 0)
|
|
{
|
|
ndbout << "Ndb was not ready" << endl;
|
|
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
ndbout << "Loading data" << endl;
|
|
/* Put some data into the table */
|
|
if (hugoTrans.loadTable(&otherNdb,
|
|
1024) != NDBT_OK)
|
|
{
|
|
ndbout << "Data load failed " << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
const Uint32 code = ctx->getProperty("ErrorCode", Uint32(0));
|
|
|
|
ndbout << "Setting error insert : " << code << endl;
|
|
|
|
/* TC error insert causing API disconnection
|
|
* at some point
|
|
*/
|
|
|
|
if (restarter.insertErrorInAllNodes(code) != 0)
|
|
{
|
|
ndbout << "Failed to insert error" << endl;
|
|
}
|
|
|
|
ndbout << "Updating data, expect disconnection" << endl;
|
|
/* Perform a bulk update */
|
|
/* We expect to be disconnected at the end of this... */
|
|
rc = hugoTrans.pkUpdateRecords(&otherNdb,
|
|
1024);
|
|
|
|
|
|
restarter.insertErrorInAllNodes(0);
|
|
|
|
/* We rely on the test framework to detect a problem
|
|
* if the data nodes failed here
|
|
*/
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int
|
|
runCheckWriteTransaction(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
const NdbDictionary::Table* pTab = ctx->getTab();
|
|
|
|
HugoOperations hugoOps(*pTab);
|
|
Ndb* pNdb = GETNDB(step);
|
|
|
|
CHECKE((hugoOps.startTransaction(pNdb) == NDBT_OK),
|
|
hugoOps);
|
|
|
|
CHECKE((hugoOps.pkWriteRecord(pNdb,
|
|
0) == NDBT_OK),
|
|
hugoOps);
|
|
CHECKE((hugoOps.execute_Commit(pNdb) == NDBT_OK),
|
|
hugoOps);
|
|
CHECKE((hugoOps.closeTransaction(pNdb) == NDBT_OK),
|
|
hugoOps);
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runCheckSlowCommit(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
NdbRestarter restarter;
|
|
/* Want to test the 'slow' commit protocol behaves
|
|
* correctly for various table types
|
|
*/
|
|
for (int table_type = 0; table_type < 3; table_type++)
|
|
{
|
|
switch (table_type)
|
|
{
|
|
case 0:
|
|
{
|
|
ndbout << "Normal table" << endl;
|
|
ctx->setProperty("CreateRB", Uint32(0));
|
|
ctx->setProperty("CreateFR", Uint32(0));
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
ndbout << "ReadBackup table" << endl;
|
|
ctx->setProperty("CreateRB", Uint32(1));
|
|
ctx->setProperty("CreateFR", Uint32(0));
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
ndbout << "FullyReplicated" << endl;
|
|
/* Need RB set, as can create !RB FR table... */
|
|
ctx->setProperty("CreateRB", Uint32(1));
|
|
ctx->setProperty("CreateFR", Uint32(1));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (runReCreateTable(ctx, step) != NDBT_OK)
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
for (int test_type=0; test_type < 3; test_type++)
|
|
{
|
|
Uint32 errorCode = 0;
|
|
switch (test_type)
|
|
{
|
|
case 0:
|
|
/* As normal */
|
|
break;
|
|
case 1:
|
|
/* Timeout during commit phase */
|
|
errorCode = 8113;
|
|
break;
|
|
case 2:
|
|
/* Timeout during complete phase */
|
|
errorCode = 8114;
|
|
break;
|
|
}
|
|
ndbout << "Inserting error " << errorCode
|
|
<< " in all nodes." << endl;
|
|
|
|
restarter.insertErrorInAllNodes(errorCode);
|
|
|
|
int ret = runCheckWriteTransaction(ctx,step);
|
|
|
|
restarter.insertErrorInAllNodes(0);
|
|
if (ret != NDBT_OK)
|
|
{
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runWriteRecord(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
// Write a record so we can run parallel exclusive reads
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
HugoOperations hugoOps(*tab);
|
|
Ndb *ndb = GETNDB(step);
|
|
CHECK(hugoOps.startTransaction(ndb) == 0);
|
|
CHECK(hugoOps.pkWriteRecord(ndb, 0, 1) == 0);
|
|
CHECK(hugoOps.execute_Commit(ndb) == 0);
|
|
CHECK(hugoOps.closeTransaction(ndb) == 0);
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runPkRead1(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
HugoOperations hugoOps(*tab);
|
|
Ndb *ndb = GETNDB(step);
|
|
CHECK(hugoOps.startTransaction(ndb) == 0);
|
|
CHECK(hugoOps.pkReadRecord(ndb, 0, 1, NdbOperation::LM_Exclusive) == 0);
|
|
CHECK(hugoOps.execute_NoCommit(ndb) == 0);
|
|
// Signal the other thread that the row is locked by this transaction
|
|
ctx->setProperty("ReadExecuted", 1);
|
|
ctx->getPropertyWait("ReadExecuted", 2);
|
|
// The other thread has completed its (unsuccessful) attempts to lock this
|
|
// row. Go ahead and complete the operation thus releasing the lock
|
|
CHECK(hugoOps.execute_Commit(ndb) == 0);
|
|
CHECK(hugoOps.closeTransaction(ndb) == 0);
|
|
// Signal the other thread that the transaction has completed and that the
|
|
// row has been unlocked
|
|
ctx->setProperty("ReadExecuted", 3);
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int runPkRead2(NDBT_Context* ctx, NDBT_Step* step)
|
|
{
|
|
// Wait until the transaction in the other thread has locked the row
|
|
ctx->getPropertyWait("ReadExecuted", 1);
|
|
const NdbDictionary::Table *tab = ctx->getTab();
|
|
HugoOperations hugoOps(*tab);
|
|
Ndb *ndb = GETNDB(step);
|
|
CHECK(hugoOps.startTransaction(ndb) == 0);
|
|
CHECK(hugoOps.pkReadRecord(ndb, 0, 1, NdbOperation::LM_Exclusive) == 0);
|
|
// Try and read the locked row without the NoWait option set. This results in
|
|
// Error 266: Time-out in NDB, probably caused by deadlock
|
|
CHECK(hugoOps.execute_NoCommit(ndb) == 266);
|
|
CHECK(hugoOps.closeTransaction(ndb) == 0);
|
|
CHECK(hugoOps.startTransaction(ndb) == 0);
|
|
// Try and read the locked row with the NoWait option set. This results in
|
|
// Error 635: Lock already taken, not waiting
|
|
CHECK(hugoOps.pkReadRecord(ndb, 0, 1, NdbOperation::LM_Exclusive, 0,
|
|
true) == 0);
|
|
CHECK(hugoOps.execute_NoCommit(ndb) == 635);
|
|
CHECK(hugoOps.closeTransaction(ndb) == 0);
|
|
// Signal the other thread that this transaction has completed its attempts
|
|
// to lock the row
|
|
ctx->setProperty("ReadExecuted", 2);
|
|
// Wait until the transaction in the other thread has completed
|
|
ctx->getPropertyWait("ReadExecuted", 3);
|
|
// Finally now that the other transaction has completed its read, attempt
|
|
// another locking read with NoWait option set which should be successful
|
|
// this time around
|
|
CHECK(hugoOps.startTransaction(ndb) == 0);
|
|
CHECK(hugoOps.pkReadRecord(ndb, 0, 1, NdbOperation::LM_Exclusive, 0,
|
|
true) == 0);
|
|
CHECK(hugoOps.execute_NoCommit(ndb) == 0);
|
|
CHECK(hugoOps.closeTransaction(ndb) == 0);
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
NDBT_TESTSUITE(testNdbApi);
|
|
TESTCASE("MaxNdb",
|
|
"Create Ndb objects until no more can be created\n"){
|
|
INITIALIZER(runTestMaxNdb);
|
|
}
|
|
TESTCASE("MaxTransactions",
|
|
"Start transactions until no more can be created\n"){
|
|
INITIALIZER(runTestMaxTransaction);
|
|
}
|
|
TESTCASE("MaxOperations",
|
|
"Get operations until no more can be created\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runTestMaxOperations);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("MaxGetValue",
|
|
"Call getValue loads of time\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runTestGetValue);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("MaxEqual",
|
|
"Call equal loads of time\n"){
|
|
INITIALIZER(runTestEqual);
|
|
}
|
|
TESTCASE("DeleteNdb",
|
|
"Make sure that a deleted Ndb object is properly deleted\n"
|
|
"and removed from transporter\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runTestDeleteNdb);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("WaitUntilReady",
|
|
"Make sure you get an error message when calling waitUntilReady\n"
|
|
"without an init'ed Ndb\n"){
|
|
INITIALIZER(runTestWaitUntilReady);
|
|
}
|
|
TESTCASE("GetOperationNoTab",
|
|
"Call getNdbOperation on a table that does not exist\n"){
|
|
INITIALIZER(runGetNdbOperationNoTab);
|
|
}
|
|
TESTCASE("BadColNameHandling",
|
|
"Call methods with an invalid column name and check error handling\n"){
|
|
INITIALIZER(runBadColNameHandling);
|
|
}
|
|
TESTCASE("MissingOperation",
|
|
"Missing operation request(insertTuple) should give an error code\n"){
|
|
INITIALIZER(runMissingOperation);
|
|
}
|
|
TESTCASE("GetValueInUpdate",
|
|
"Test that it's not possible to perform getValue in an update\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runGetValueInUpdate);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("UpdateWithoutKeys",
|
|
"Test that it's not possible to perform update without setting\n"
|
|
"PKs"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runUpdateWithoutKeys);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("UpdateWithoutValues",
|
|
"Test that it's not possible to perform update without setValues\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runUpdateWithoutValues);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("NdbErrorOperation",
|
|
"Test that NdbErrorOperation is properly set"){
|
|
INITIALIZER(runCheckGetNdbErrorOperation);
|
|
}
|
|
TESTCASE("ReadWithoutGetValue",
|
|
"Test that it's possible to perform read wo/ getvalue's\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runReadWithoutGetValue);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("Bug_11133",
|
|
"Test ReadEx-Delete-Write\n"){
|
|
INITIALIZER(runBug_11133);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("Bug_WritePartialIgnoreError",
|
|
"Test WritePartialIgnoreError\n"){
|
|
INITIALIZER(runBug_WritePartialIgnoreError);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("Scan_4006",
|
|
"Check that getNdbScanOperation does not get 4006\n"){
|
|
INITIALIZER(runLoadTable);
|
|
INITIALIZER(runScan_4006);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("IgnoreError", ""){
|
|
INITIALIZER(createPkIndex);
|
|
STEP(runTestIgnoreError);
|
|
FINALIZER(runClearTable);
|
|
FINALIZER(createPkIndex_Drop);
|
|
}
|
|
TESTCASE("CheckNdbObjectList",
|
|
""){
|
|
INITIALIZER(runCheckNdbObjectList);
|
|
}
|
|
TESTCASE("DeleteClusterConnectionWhileUsed",
|
|
"Make sure that deleting of Ndb_cluster_connection will"
|
|
"not return until all it's Ndb objects has been deleted."){
|
|
STEP(runNdbClusterConnectionDelete_connection_owner)
|
|
STEP(runNdbClusterConnectionDelete_connection_user);
|
|
}
|
|
TESTCASE("ExecuteAsynch",
|
|
"Check that executeAsync() works (BUG#27495)\n"){
|
|
INITIALIZER(runTestExecuteAsynch);
|
|
}
|
|
TESTCASE("Bug28443",
|
|
""){
|
|
INITIALIZER(runBug28443);
|
|
}
|
|
TESTCASE("Bug37158",
|
|
""){
|
|
INITIALIZER(runBug37158);
|
|
}
|
|
TESTCASE("SimpleReadAbortOnError",
|
|
"Test behaviour of Simple reads with Abort On Error"){
|
|
INITIALIZER(simpleReadAbortOnError);
|
|
}
|
|
TESTCASE("NdbRecordPKAmbiguity",
|
|
"Test behaviour of NdbRecord insert with ambig. pk values"){
|
|
INITIALIZER(testNdbRecordPkAmbiguity);
|
|
}
|
|
TESTCASE("NdbRecordPKUpdate",
|
|
"Verify that primary key columns can be updated"){
|
|
INITIALIZER(testNdbRecordPKUpdate);
|
|
}
|
|
TESTCASE("NdbRecordCICharPKUpdate",
|
|
"Verify that a case-insensitive char pk column can be updated"){
|
|
INITIALIZER(testNdbRecordCICharPKUpdate);
|
|
}
|
|
TESTCASE("NdbRecordRowLength",
|
|
"Verify that the record row length calculation is correct") {
|
|
INITIALIZER(testNdbRecordRowLength);
|
|
}
|
|
TESTCASE("Bug44015",
|
|
"Rollback insert followed by delete to get corruption") {
|
|
STEP(runBug44015);
|
|
STEPS(runScanReadUntilStopped, 10);
|
|
}
|
|
TESTCASE("Bug44065_org",
|
|
"Rollback no-change update on top of existing data") {
|
|
INITIALIZER(runBug44065_org);
|
|
}
|
|
TESTCASE("Bug44065",
|
|
"Rollback no-change update on top of existing data") {
|
|
INITIALIZER(runBug44065);
|
|
}
|
|
TESTCASE("ApiFailReqBehaviour",
|
|
"Check ApiFailReq cleanly marks Api disconnect") {
|
|
// Some flags to enable the various threads to cooperate
|
|
TC_PROPERTY(ApiFailTestRun, (Uint32)0);
|
|
TC_PROPERTY(ApiFailTestComplete, (Uint32)0);
|
|
TC_PROPERTY(ApiFailTestsRunning, (Uint32)0);
|
|
TC_PROPERTY(ApiFailNumberPkSteps, (Uint32)5); // Num threads below
|
|
INITIALIZER(runLoadTable);
|
|
// 5 threads to increase probability of pending
|
|
// TCKEYREQ after API_FAILREQ
|
|
STEP(runBulkPkReads);
|
|
STEP(runBulkPkReads);
|
|
STEP(runBulkPkReads);
|
|
STEP(runBulkPkReads);
|
|
STEP(runBulkPkReads);
|
|
STEP(testApiFailReq);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("ReadColumnDuplicates",
|
|
"Check NdbApi behaves ok when reading same column multiple times") {
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runReadColumnDuplicates);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("Bug51775", "")
|
|
{
|
|
INITIALIZER(runBug51775);
|
|
}
|
|
TESTCASE("FragmentedApiFailure",
|
|
"Test in-assembly fragment cleanup code for API failure") {
|
|
// We reuse some of the infrastructure from ApiFailReqBehaviour here
|
|
TC_PROPERTY(ApiFailTestRun, (Uint32)0);
|
|
TC_PROPERTY(ApiFailTestComplete, (Uint32)0);
|
|
TC_PROPERTY(ApiFailTestsRunning, (Uint32)0);
|
|
TC_PROPERTY(ApiFailNumberPkSteps, (Uint32)5); // Num threads below
|
|
// 5 threads to increase probability of fragmented signal being
|
|
// in-assembly when disconnect occurs
|
|
STEP(runFragmentedScanOtherApi);
|
|
STEP(runFragmentedScanOtherApi);
|
|
STEP(runFragmentedScanOtherApi);
|
|
STEP(runFragmentedScanOtherApi);
|
|
STEP(runFragmentedScanOtherApi);
|
|
STEP(testFragmentedApiFail);
|
|
};
|
|
TESTCASE("UnlockBasic",
|
|
"Check basic op unlock behaviour") {
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runTestUnlockBasic);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("UnlockRepeat",
|
|
"Check repeated lock/unlock behaviour") {
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runTestUnlockRepeat);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("UnlockMulti",
|
|
"Check unlock behaviour with multiple operations") {
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runTestUnlockMulti);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("UnlockScan",
|
|
"Check unlock behaviour with scan lock-takeover") {
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runTestUnlockScan);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("NdbClusterConnect",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
}
|
|
TESTCASE("NdbClusterConnectionConnect",
|
|
"Test Ndb_cluster_connection::connect()")
|
|
{
|
|
INITIALIZER(runNdbClusterConnectionConnect);
|
|
}
|
|
TESTCASE("NdbClusterConnectNR",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
TC_PROPERTY("TimeoutAfterFirst", (Uint32)0);
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
STEP(runRestarts); // Note after runNdbClusterConnect or else counting wrong
|
|
}
|
|
TESTCASE("NdbClusterConnectNR_master",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
TC_PROPERTY("Master", 1);
|
|
TC_PROPERTY("TimeoutAfterFirst", (Uint32)0);
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
STEP(runRestarts); // Note after runNdbClusterConnect or else counting wrong
|
|
}
|
|
TESTCASE("NdbClusterConnectNR_non_master",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
TC_PROPERTY("Master", 2);
|
|
TC_PROPERTY("TimeoutAfterFirst", (Uint32)0);
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
STEP(runRestarts); // Note after runNdbClusterConnect or else counting wrong
|
|
}
|
|
TESTCASE("NdbClusterConnectNR_slow",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
TC_PROPERTY("Master", 2);
|
|
TC_PROPERTY("TimeoutAfterFirst", (Uint32)0);
|
|
TC_PROPERTY("SlowNR", 1);
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
STEP(runRestarts); // Note after runNdbClusterConnect or else counting wrong
|
|
}
|
|
TESTCASE("NdbClusterConnectSR",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
TC_PROPERTY("ClusterRestart", (Uint32)1);
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
STEP(runRestarts); // Note after runNdbClusterConnect or else counting wrong
|
|
}
|
|
TESTCASE("NdbClusterConnectNR_slow_nostart",
|
|
"Make sure that every Ndb_cluster_connection get a unique nodeid")
|
|
{
|
|
// Test ability for APIs to connect while some node in NOT_STARTED state
|
|
// Limit to non-master nodes due to uniqueness failing when master
|
|
// restarted
|
|
// (Bug #27484475 NDB : NODEID ALLOCATION UNIQUENESS NOT GUARANTEED
|
|
// OVER MASTER NODE FAILURE)
|
|
// Use randomised initial master restart to avoid always testing
|
|
// the same node id restart behaviour
|
|
TC_PROPERTY("Master", 2);
|
|
TC_PROPERTY("TimeoutAfterFirst", (Uint32)0);
|
|
TC_PROPERTY("SlowNoStart", 1);
|
|
INITIALIZER(runMaybeRestartMaster);
|
|
INITIALIZER(runNdbClusterConnectInit);
|
|
STEPS(runNdbClusterConnect, MAX_NODES);
|
|
STEP(runRestarts); // Note after runNdbClusterConnect or else counting wrong
|
|
}
|
|
TESTCASE("TestFragmentedSend",
|
|
"Test fragmented send behaviour"){
|
|
INITIALIZER(testFragmentedSend);
|
|
}
|
|
TESTCASE("ReceiveTRANSIDAIAfterRollback",
|
|
"Delay the delivery of TRANSID_AI results from the data node." \
|
|
"Abort a transaction with a timeout so that the "\
|
|
"transaction closing and TRANSID_AI processing are interleaved." \
|
|
"Confirm that this interleaving does not result in a core."
|
|
){
|
|
STEP(runReceiveTRANSIDAIAfterRollback);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
TESTCASE("RecordSpecificationBackwardCompatibility",
|
|
"Test RecordSpecification struct's backward compatibility"){
|
|
STEP(testNdbRecordSpecificationCompatibility);
|
|
}
|
|
TESTCASE("SchemaObjectOwnerCheck",
|
|
"Test use of schema objects with non-owning connections"){
|
|
STEP(testSchemaObjectOwnerCheck);
|
|
}
|
|
TESTCASE("MgmdSendbufferExhaust",
|
|
"")
|
|
{
|
|
INITIALIZER(testMgmdSendBufferExhaust);
|
|
}
|
|
TESTCASE("GetNdbIndexOperationTest",
|
|
"Send an obsolete index into getNdbIndexOperation and execute." \
|
|
"Confirm that this doesn't crash the ndbd.")
|
|
{
|
|
//To be run only on Table I3
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runGetNdbIndexOperationTest);
|
|
VERIFIER(runCheckAllNodesStarted);
|
|
FINALIZER(runClearTable)
|
|
}
|
|
TESTCASE("GetNdbIndexOperationBatchTest",
|
|
"Send an obsolete index into getNdbIndexOperation in a batch" \
|
|
"and execute. Confirm that this doesn't crash the ndbd.")
|
|
{
|
|
//To be run only on Table I3
|
|
INITIALIZER(runCreateIndexesOnI3);
|
|
INITIALIZER(runLoadTable);
|
|
STEP(runGetNdbIndexOperationBatchTest);
|
|
VERIFIER(runCheckAllNodesStarted);
|
|
FINALIZER(runClearTable)
|
|
}
|
|
TESTCASE("GetNdbIndexOperationParallelDroppingTest",
|
|
"1. Start transactions batch/normal in a step" \
|
|
"2. Start dropping/creating indexes in a parallel thread " \
|
|
"Confirm that this doesn't crash the ndbd.")
|
|
{
|
|
//To be run only on Table I3
|
|
INITIALIZER(runCreateIndexesOnI3);
|
|
INITIALIZER(runLoadTable);
|
|
STEPS(runGetNdbIndexOperationTransactions, 100);
|
|
STEP(runDropIndexesOnI3);
|
|
VERIFIER(runCheckAllNodesStarted);
|
|
FINALIZER(runClearTable)
|
|
}
|
|
TESTCASE("CloseBeforeExecute",
|
|
"Check that objects allocated within a Ndb/NdbTransaction " \
|
|
"is released even if Txn is not executed"){
|
|
INITIALIZER(runTestNoExecute);
|
|
}
|
|
TESTCASE("CheckTransId",
|
|
"Check transid uniqueness across multiple Ndb instances")
|
|
{
|
|
INITIALIZER(runCheckTransId);
|
|
}
|
|
TESTCASE("CheckTransIdMt",
|
|
"Check transid uniqueness across multiple threads")
|
|
{
|
|
INITIALIZER(runInitCheckTransIdMt);
|
|
STEPS(runCheckTransIdMt, CheckTransIdSteps);
|
|
VERIFIER(runVerifyCheckTransIdMt);
|
|
FINALIZER(runFinaliseCheckTransIdMt);
|
|
}
|
|
TESTCASE("OldApiScanFinalise",
|
|
"Test error during finalise behaviour")
|
|
{
|
|
VERIFIER(runTestOldApiScanFinalise);
|
|
}
|
|
TESTCASE("TestColumnNameLookupPerf",
|
|
"")
|
|
{
|
|
INITIALIZER(runTestColumnNameLookupPerf);
|
|
}
|
|
TESTCASE("CheckDisconnectCommit",
|
|
"Check commit post API disconnect")
|
|
{
|
|
TC_PROPERTY("CreateRB", Uint32(1)); // ReadBackup
|
|
TC_PROPERTY("ErrorCode", Uint32(8110)); // API disconnect during COMMIT
|
|
INITIALIZER(runReCreateTable);
|
|
INITIALIZER(setupOtherConnection);
|
|
STEP(runCheckLateDisconnect);
|
|
FINALIZER(runDropTable);
|
|
FINALIZER(tearDownOtherConnection);
|
|
}
|
|
TESTCASE("CheckDisconnectComplete",
|
|
"Check complete post API disconnect")
|
|
{
|
|
TC_PROPERTY("CreateRB", Uint32(1)); // ReadBackup
|
|
TC_PROPERTY("ErrorCode", Uint32(8111)); // API disconnect during COMPLETE
|
|
INITIALIZER(runReCreateTable);
|
|
INITIALIZER(setupOtherConnection);
|
|
STEP(runCheckLateDisconnect);
|
|
FINALIZER(runDropTable);
|
|
FINALIZER(tearDownOtherConnection);
|
|
}
|
|
TESTCASE("CheckSlowCommit",
|
|
"Check slow commit protocol + table types")
|
|
{
|
|
STEP(runCheckSlowCommit);
|
|
FINALIZER(runDropTable);
|
|
}
|
|
TESTCASE("PkLockingReadNoWait",
|
|
"Check if PK locking read op with NoWait option set works as expected")
|
|
{
|
|
TC_PROPERTY("ReadExecuted", Uint32(0));
|
|
INITIALIZER(runWriteRecord);
|
|
STEP(runPkRead1);
|
|
STEP(runPkRead2);
|
|
FINALIZER(runClearTable);
|
|
}
|
|
|
|
|
|
NDBT_TESTSUITE_END(testNdbApi)
|
|
|
|
int main(int argc, const char** argv){
|
|
ndb_init();
|
|
NDBT_TESTSUITE_INSTANCE(testNdbApi);
|
|
// TABLE("T1");
|
|
return testNdbApi.execute(argc, argv);
|
|
}
|
|
|
|
template class Vector<Ndb*>;
|
|
template class Vector<NdbConnection*>;
|
|
template class Vector<Ndb_cluster_connection*>;
|