/* Copyright (c) 2011, 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 as published by the Free Software Foundation; version 2 of the License. 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 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 #include #include #include #include #include #include #include "../../src/ndbapi/NdbQueryBuilder.hpp" #include "../../src/ndbapi/NdbQueryOperation.hpp" /* TODO: - RecAttr and setResultRowBuff result retrieval. - Parameter operands. - Add another table type (e.g. with CHAR() fields.) */ /* Query-related error codes. Used for negative testing. */ #define QRY_REQ_ARG_IS_NULL 4800 #define QRY_TOO_FEW_KEY_VALUES 4801 #define QRY_TOO_MANY_KEY_VALUES 4802 #define QRY_OPERAND_HAS_WRONG_TYPE 4803 #define QRY_CHAR_OPERAND_TRUNCATED 4804 #define QRY_NUM_OPERAND_RANGE 4805 #define QRY_MULTIPLE_PARENTS 4806 #define QRY_UNKONWN_PARENT 4807 #define QRY_UNKNOWN_COLUMN 4808 #define QRY_UNRELATED_INDEX 4809 #define QRY_WRONG_INDEX_TYPE 4810 #define QRY_OPERAND_ALREADY_BOUND 4811 #define QRY_DEFINITION_TOO_LARGE 4812 #define QRY_SEQUENTIAL_SCAN_SORTED 4813 #define QRY_RESULT_ROW_ALREADY_DEFINED 4814 #define QRY_HAS_ZERO_OPERATIONS 4815 #define QRY_IN_ERROR_STATE 4816 #define QRY_ILLEGAL_STATE 4817 #define QRY_WRONG_OPERATION_TYPE 4820 #define QRY_SCAN_ORDER_ALREADY_SET 4821 #define QRY_PARAMETER_HAS_WRONG_TYPE 4822 #define QRY_CHAR_PARAMETER_TRUNCATED 4823 #define QRY_MULTIPLE_SCAN_SORTED 4824 #define QRY_BATCH_SIZE_TOO_SMALL 4825 namespace SPJSanityTest{ static void resetError(const NdbError& err) { new (&const_cast(err)) NdbError; } class IntField{ public: static const char* getType(){ return "INT"; } IntField(int i=0): m_val(i) {} const char* toStr(char* buff) const { sprintf(buff, "%d", m_val); return buff; } int compare(const IntField& other) const{ if (m_val > other.m_val) return 1; else if (m_val == other.m_val) return 0; else return -1; } Uint64 getValue() const{ return m_val; } Uint32 getSize() const{ return sizeof m_val; } private: uint m_val; }; class StrField{ public: static const char* getType(){ return "VARCHAR(10)"; } StrField(int i=0): m_len(6){ // bzero(m_val, sizeof m_val); sprintf(m_val, "c%5d", i); } const char* toStr(char* buff) const { sprintf(buff, "'%s'", getValue()); return buff; } int compare(const StrField& other) const{ return strcmp(getValue(), other.getValue()); } const char* getValue() const{ m_val[m_len] = '\0'; return m_val; } Uint32 getSize() const{ m_val[m_len] = '\0'; return strlen(m_val); } private: Uint8 m_len; mutable char m_val[10]; }; /* Key class.*/ template class GenericKey{ public: static const int size = 2; FieldType m_values[size]; NdbConstOperand* makeConstOperand(NdbQueryBuilder& builder, int fieldNo) const { require(fieldNo class GenericRow{ public: static const int size = 4; FieldType m_values[size]; explicit GenericRow(int rowNo){ /* Attribute values are chosen such that rows are sorted on * all attribtes, and that any pair of consecutive columns can be * used as a foreign key to the table itself.*/ for(int i = 0; i row(rowNo); sprintf(buffer, "values("); char* tail = buffer+strlen(buffer); for(int i = 0; i getPrimaryKey() const; GenericKey getIndexKey() const; GenericKey getForeignKey(int keyNo) const; void makeLessThanCond(NdbScanFilter& scanFilter){ //require(scanFilter.lt(0, m_values[0].getValue())==0); require(scanFilter.cmp(NdbScanFilter::COND_LT, 0, m_values, m_values[0].getSize())==0); } /** Get the row column number that corresponds to the n'th column * of the index.*/ static int getIndexKeyColNo(int indexCol); /** Get the row column number that corresponds to the n'th column * of the m'th foreign key..*/ static int getForeignKeyColNo(int keyNo, int keyCol); }; template GenericKey GenericRow::getPrimaryKey() const { GenericKey key; for(int i = 0; i::size; i++){ key.m_values[i] = m_values[i]; } return key; } template GenericKey GenericRow::getIndexKey() const { return getForeignKey(1); } template GenericKey GenericRow::getForeignKey(int keyNo) const { require(keyNo<=1); GenericKey key; for(int i = 0; i::size; i++){ key.m_values[i] = m_values[getForeignKeyColNo(keyNo,i)]; } return key; } template int GenericRow::getIndexKeyColNo(int indexCol){ return getForeignKeyColNo(1, indexCol); } template int GenericRow::getForeignKeyColNo(int keyNo, int keyCol){ require(keyNo::size-GenericKey::size); require(keyCol::size); return size-GenericKey::size-keyNo+keyCol; } template static bool operator==(const GenericRow& a, const GenericRow& b){ for(int i = 0; i::size; i++){ if(a.m_values[i].compare(b.m_values[i]) != 0){ return false; } } return true; } template static bool operator==(const GenericKey& a, const GenericKey& b){ for(int i = 0; i::size; i++){ if(a.m_values[i].compare(b.m_values[i]) != 0){ return false; } } return true; } /** Returns true if key of a <= key of b.*/ template static bool lessOrEqual(const GenericRow& a, const GenericRow& b){ for(int i = 0; i::size; i++){ if(a.m_values[i].compare(b.m_values[i]) == 1){ return false; } } return true; } template static NdbOut& operator<<(NdbOut& out, const GenericRow& row){ char buff[11]; out << "{"; for(int i = 0; i::size; i++){ out << row.m_values[i].toStr(buff); if(i::size-1){ out << ", "; } } out << "}"; return out; } //typedef GenericRow Row; //typedef GenericKey Key; typedef GenericRow Row; typedef GenericKey Key; static const char* colName(int colNo){ static const char* names[] = { "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10" }; require(static_cast(colNo)< sizeof names/sizeof names[0]); return names[colNo]; }; static void printMySQLError(MYSQL& mysql, const char* before=NULL){ if(before!=NULL){ ndbout << before; } ndbout << mysql_error(&mysql) << endl; exit(-1); } static void mySQLExec(MYSQL& mysql, const char* stmt){ ndbout << stmt << ";" << endl; if(mysql_query(&mysql, stmt) != 0){ ndbout << "Error executing '" << stmt << "' : "; printMySQLError(mysql); } } class Query; /** Class representing a single NdbQueryOperation. 'Row' * is a template argument, to allow different table defintions.*/ class Operation{ public: explicit Operation(class Query& query, Operation* parent); virtual ~Operation(){} //protected: FIXME public: friend class Query; /** Enclosing NdbQuery.*/ Query& m_query; /** Optional parent operation.*/ const Operation* m_parent; Vector m_children; const NdbQueryOperationDef* m_operationDef; // For now, only setResultRowRef() style result retrieval is tested. union { const Row* m_resultPtr; // Use union to avoid strict-aliasing problems. const char* m_resultCharPtr; }; // Corresponds to NdbQueryOperationDef operation numbering Uint32 m_operationId; // Number among siblings. const Uint32 m_childNo; /** Check that result of this op and descendans is ok.*/ void verifyRow(); /** Check that result of this op is ok.*/ virtual void verifyOwnRow() = 0; /** Build operation definition.*/ virtual void buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab) = 0; /** Build this and descendants.*/ void build(NdbQueryBuilder& builder, const NdbDictionary::Table& tab); /** Set up result retrieval before execution.*/ virtual void submit()=0; void compareRows(const char* text, const Row* expected, const Row* actual) const; }; class Query{ public: explicit Query(Ndb& ndb); ~Query(){ m_builder->destroy(); if (m_queryDef != NULL) { m_queryDef->destroy(); } } /** Build query definition.*/ void build(const NdbDictionary::Table& tab, int tableSize); /** Execute within transaction.*/ void submit(NdbTransaction& transaction); void submitOperation(Operation& operation) const; void setRoot(Operation& root){ m_root = &root;} NdbQuery::NextResultOutcome nextResult(){ return m_query->nextResult(true, false); } /** Verify current row for all operations.*/ void verifyRow() const { m_root->verifyRow(); } Uint32 allocOperationId(){ return m_operationCount++; } NdbQueryOperation* getOperation(Uint32 ident) const { return m_query->getQueryOperation(ident); } int getTableSize() const { return m_tableSize; } const NdbRecord* getNdbRecord() const { return m_ndbRecord; } const NdbDictionary::Dictionary* getDictionary() const { return m_ndb.getDictionary(); } void close(bool forceSend = false){ m_query->close(forceSend); } private: Ndb& m_ndb; NdbQueryBuilder* const m_builder; Operation* m_root; const NdbQueryDef* m_queryDef; NdbQuery* m_query; Uint32 m_operationCount; int m_tableSize; const NdbRecord* m_ndbRecord; }; class LookupOperation: public Operation{ public: explicit LookupOperation(Query& query, Operation* parent = NULL); virtual void verifyOwnRow(); /** Set up result retrieval before execution.*/ virtual void submit(); protected: virtual void buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab); }; class IndexLookupOperation: public Operation{ public: explicit IndexLookupOperation(Query& query, const char* indexName, Operation* parent = NULL); virtual void verifyOwnRow(); /** Set up result retrieval before execution.*/ virtual void submit(); protected: virtual void buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab); private: const char* const m_indexName; }; class TableScanOperation: public Operation{ public: explicit TableScanOperation(Query& query, int lessThanRow=-1); virtual ~TableScanOperation() { delete[] m_rowFound; } virtual void verifyOwnRow(); /** Set up result retrieval before execution.*/ virtual void submit(); protected: virtual void buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab); private: bool* m_rowFound; int m_lessThanRow; }; class IndexScanOperation: public Operation{ public: explicit IndexScanOperation(Query& query, const char* indexName, int lowerBoundRowNo, int upperBoundRowNo, NdbQueryOptions::ScanOrdering ordering); virtual ~IndexScanOperation() { delete[] m_rowFound; } virtual void verifyOwnRow(); /** Set up result retrieval before execution.*/ virtual void submit(); protected: virtual void buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab); private: const char* const m_indexName; /** Number of table row from which get key to use as lower bound.*/ const int m_lowerBoundRowNo; /** Number of table row from which get key to use as upper bound.*/ const int m_upperBoundRowNo; /** An entry per row. True if row has been seen in the result stream.*/ bool* m_rowFound; /** Ordering of results.*/ NdbQueryOptions::ScanOrdering m_ordering; /** Previous row, for verifying ordering.*/ Row m_previousRow; /** True from the second row and onwards.*/ bool m_hasPreviousRow; }; // Query methods. Query::Query(Ndb& ndb): m_ndb(ndb), m_builder(NdbQueryBuilder::create()), m_root(NULL), m_queryDef(NULL), m_query(NULL), m_operationCount(0), m_tableSize(-1), m_ndbRecord(NULL) { require(m_builder != NULL); } void Query::build(const NdbDictionary::Table& tab, int tableSize){ m_tableSize = tableSize; m_root->build(*m_builder, tab); m_queryDef = m_builder->prepare(); m_ndbRecord = tab.getDefaultRecord(); } void Query::submit(NdbTransaction& transaction){ m_query = transaction.createQuery(m_queryDef); require(m_query!=NULL); submitOperation(*m_root); } void Query::submitOperation(Operation& operation) const{ // Do a depth first traversal of the operations graph. operation.submit(); for(Uint32 i = 0; im_children.size()) { if(parent==NULL){ query.setRoot(*this); }else{ parent->m_children.push_back(this); } } void Operation::build(NdbQueryBuilder& builder, const NdbDictionary::Table& tab){ m_operationId = m_query.allocOperationId(); buildThis(builder, tab); require(builder.getNdbError().code==0); for(Uint32 i = 0; ibuild(builder, tab); } } void Operation::verifyRow(){ verifyOwnRow(); for(Uint32 i = 0; iverifyRow(); } } typedef const char* constCharPtr; void Operation::compareRows(const char* text, const Row* expected, const Row* actual) const{ if(expected==NULL){ if(actual==NULL){ ndbout << text << " operationId=" << m_operationId << " expected NULL and got it." << endl; }else{ ndbout << text << " operationId=" << m_operationId << " expected NULL but got." << *actual << endl; require(false); } }else{ if(actual==NULL){ ndbout << text << " operationId=" << m_operationId << " expected: " << *expected << " but got NULL." << endl; require(false); }else{ ndbout << text << " operationId=" << m_operationId << " expected: " << *expected; if(*expected == *actual){ ndbout << " and got it." << endl; }else{ ndbout << " but got: " << *actual; require(false); } } } }; // LookupOperation methods. LookupOperation ::LookupOperation(Query& query, Operation* parent): Operation(query, parent){ } void LookupOperation::buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab){ NdbQueryOperand* keyOperands[Key::size+2]; if(m_parent==NULL){ const Key key = Row(0).getPrimaryKey(); for(int i = 0; im_operationDef, "unknown_col") == NULL); require(builder.getNdbError().code == QRY_UNKNOWN_COLUMN); for(int i = 0; im_operationDef, colName(Row::getForeignKeyColNo( m_childNo, i))); require(keyOperands[i]!=NULL); /*Row::makeLinkedKey(builder, keyOperands, Operation::m_parent->m_operationDef, Operation::m_childNo);*/ } } // Negative testing keyOperands[Key::size] = keyOperands[0]; keyOperands[Key::size+1] = NULL; require(builder.readTuple(&tab, keyOperands)== NULL); require(builder.getNdbError().code == QRY_TOO_MANY_KEY_VALUES); resetError(builder.getNdbError()); keyOperands[Key::size] = NULL; m_operationDef = builder.readTuple(&tab, keyOperands); require(m_operationDef != NULL); // Negative testing keyOperands[Key::size-1] = builder.constValue(0x1fff1fff); require(keyOperands[Key::size-1] != NULL); require(builder.readTuple(&tab, keyOperands) == NULL); require(builder.getNdbError().code == QRY_OPERAND_HAS_WRONG_TYPE); // Negative testing keyOperands[Key::size-1] = NULL; require(builder.readTuple(&tab, keyOperands) == NULL); require(builder.getNdbError().code == QRY_TOO_FEW_KEY_VALUES); resetError(builder.getNdbError()); } void LookupOperation::submit(){ NdbQueryOperation* queryOp = m_query.getOperation(m_operationId); // Negative testing require(queryOp->setResultRowRef(NULL, m_resultCharPtr, NULL) == -1); require(queryOp->getQuery().getNdbError().code == QRY_REQ_ARG_IS_NULL); require( queryOp->setOrdering(NdbQueryOptions::ScanOrdering_ascending) == -1); require( queryOp->getQuery().getNdbError().code == QRY_WRONG_OPERATION_TYPE); require(queryOp->setResultRowRef(m_query.getNdbRecord(), m_resultCharPtr, NULL) == 0); // Negative testing require(queryOp->setResultRowRef(m_query.getNdbRecord(), m_resultCharPtr, NULL) == -1); require(queryOp->getQuery().getNdbError().code == QRY_RESULT_ROW_ALREADY_DEFINED); } void LookupOperation::verifyOwnRow(){ if(m_parent==NULL){ const Row expected(0); compareRows("lookup root operation", &expected, m_resultPtr); }else{ NdbQueryOperation* queryOp = m_query .getOperation(m_operationId); if(!queryOp->getParentOperation(0)->isRowNULL()){ const Key key = m_parent->m_resultPtr ->getForeignKey(m_childNo); bool found = false; for(int i = 0; iisRowNULL()){ compareRows("lookup child operation", NULL, m_resultPtr); } } } } // IndexLookupOperation methods. IndexLookupOperation ::IndexLookupOperation(Query& query, const char* indexName, Operation* parent): Operation(query, parent), m_indexName(indexName){ } void IndexLookupOperation ::buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab){ const NdbDictionary::Dictionary* const dict = m_query.getDictionary(); char fullName[200]; sprintf(fullName, "%s$unique", m_indexName); const NdbDictionary::Index* const index = dict->getIndex(fullName, tab.getName()); require(index!=NULL); NdbQueryOperand* keyOperands[Key::size+1]; if(m_parent==NULL){ const Key key = Row(0).getIndexKey(); for(int i = 0; im_operationDef, colName(Row::getForeignKeyColNo( m_childNo, i))); require(keyOperands[i]!=NULL); } /*Row::makeLinkedKey(builder, keyOperands, Operation::m_parent->m_operationDef, Operation::m_childNo);*/ } keyOperands[Key::size] = NULL; m_operationDef = builder.readTuple(index, &tab, keyOperands); // Negative testing const NdbDictionary::Index* const orderedIndex = dict->getIndex(m_indexName, tab.getName()); require(orderedIndex != NULL); require(builder.readTuple(orderedIndex, &tab, keyOperands) == NULL); require(builder.getNdbError().code == QRY_WRONG_INDEX_TYPE); resetError(builder.getNdbError()); } void IndexLookupOperation::submit(){ NdbQueryOperation* queryOp = m_query.getOperation(m_operationId); queryOp->setResultRowRef(m_query.getNdbRecord(), m_resultCharPtr, NULL); } void IndexLookupOperation::verifyOwnRow(){ if(m_parent==NULL){ const Row expected(0); compareRows("index lookup root operation", &expected, m_resultPtr); }else{ NdbQueryOperation* queryOp = m_query .getOperation(m_operationId); if(!queryOp->getParentOperation(0)->isRowNULL()){ const Key key = m_parent->m_resultPtr ->getForeignKey(m_childNo); bool found = false; for(int i = 0; iisRowNULL()){ compareRows("index lookup child operation", NULL, m_resultPtr); } } } } // TableScanOperation methods. TableScanOperation ::TableScanOperation(Query& query, int lessThanRow): Operation(query, NULL), m_rowFound(NULL), m_lessThanRow(lessThanRow){ } void TableScanOperation::buildThis(NdbQueryBuilder& builder, const NdbDictionary::Table& tab){ m_operationDef = builder.scanTable(&tab); m_rowFound = new bool[m_query.getTableSize()]; for(int i = 0; isetResultRowRef(m_query.getNdbRecord(), m_resultCharPtr, NULL); if(m_lessThanRow!=-1){ NdbInterpretedCode code(queryOp->getQueryOperationDef().getTable()); NdbScanFilter filter(&code); require(filter.begin()==0); Row(m_lessThanRow).makeLessThanCond(filter); require(filter.end()==0); require(queryOp->setInterpretedCode(code)==0); } } void TableScanOperation::verifyOwnRow(){ bool found = false; const int upperBound = m_lessThanRow==-1 ? m_query.getTableSize() : m_lessThanRow; for(int i = 0; igetIndex(m_indexName, tab.getName()); require(index!=NULL); const NdbQueryOperand* low[Key::size+1]; const NdbQueryOperand* high[Key::size+1]; // Code below assume that we use primary key index. require(strcmp(m_indexName, "PRIMARY")==0); /* Tables are alway sorted on all columns. Using these bounds, we therefore get m_upperBoundRowNo - m_lowerBoundRowNo +1 rows.*/ const Key& lowKey = *new Key(Row(m_lowerBoundRowNo).getPrimaryKey()); const Key& highKey = *new Key(Row(m_upperBoundRowNo).getPrimaryKey()); for(int i = 0; isetResultRowRef(m_query.getNdbRecord(), m_resultCharPtr, NULL); // Negative testing. if (m_ordering != NdbQueryOptions::ScanOrdering_unordered){ require( queryOp->setOrdering(NdbQueryOptions::ScanOrdering_ascending) != 0); require( queryOp->getQuery().getNdbError().code == QRY_SCAN_ORDER_ALREADY_SET); require(queryOp->setParallelism(1) != 0); require( queryOp->getQuery().getNdbError().code == QRY_SEQUENTIAL_SCAN_SORTED); } } void IndexScanOperation::verifyOwnRow(){ bool found = false; for(int i = m_lowerBoundRowNo; i<=m_upperBoundRowNo; i++){ const Row row(i); if(row == *m_resultPtr){ found = true; if(m_rowFound[i]){ ndbout << "Root index scan operation: " << *m_resultPtr << "appeared twice." << endl; require(false); } m_rowFound[i] = true; } } if(!found){ ndbout << "Root index scan operation. Unexpected row: " << *m_resultPtr << endl; require(false); }else{ if(m_hasPreviousRow){ switch(m_ordering){ case NdbQueryOptions::ScanOrdering_ascending: if(!lessOrEqual(m_previousRow, *m_resultPtr)){ ndbout << "Error in result ordering. Did not expect row " << *m_resultPtr << " now." << endl; require(false); } break; case NdbQueryOptions::ScanOrdering_descending: if(lessOrEqual(m_previousRow, *m_resultPtr)){ ndbout << "Error in result ordering. Did not expect row " << *m_resultPtr << " now." << endl; require(false); } break; case NdbQueryOptions::ScanOrdering_unordered: break; default: require(false); } } m_hasPreviousRow = true; m_previousRow = *m_resultPtr; ndbout << "Root index scan operation. Got row: " << *m_resultPtr << " as expected." << endl; } } // Misc. functions. /** Make and populate SQL table.*/ void makeTable(MYSQL& mysql, const char* name, int rowCount){ char cmd[500]; char piece[500]; sprintf(cmd, "drop table if exists %s", name); mySQLExec(mysql, cmd); sprintf(cmd, "create table %s (\n", name); for(int i = 0; igetTable(tabName); require(tab!=NULL); // Build generic query definition. query.build(*tab, tabSize); NdbTransaction* trans = ndb.startTransaction(); require(trans!=NULL); // instantiate query within transaction. query.submit(*trans); require(trans->execute(NoCommit)==0); // Verify each row and total number of rows. for(int i = 0; i3){ // Enable to test close of incomplete scan. query.close(); ndb.closeTransaction(trans); return; } } require(query.nextResult() == NdbQuery::NextResult_scanComplete); ndb.closeTransaction(trans); } /** Run a set of test cases.*/ void runTestSuite(MYSQL& mysql, Ndb& ndb){ for(int caseNo = 0; caseNo<7; caseNo++){ ndbout << endl << "Running test case " << caseNo << endl; char tabName[20]; sprintf(tabName, "t%d", caseNo); Query query(ndb); switch(caseNo){ case 0: { LookupOperation root(query); LookupOperation child(query, &root); LookupOperation child2(query, &root); runCase(mysql, ndb, query, tabName, 1, 1); } break; case 1: { IndexLookupOperation root(query, "UIX"); IndexLookupOperation child(query, "UIX", &root); runCase(mysql, ndb, query, tabName, 5, 1); } break; case 2: { IndexScanOperation root(query, "PRIMARY", 2, 4, NdbQueryOptions::ScanOrdering_unordered); LookupOperation child(query, &root); IndexLookupOperation child2(query, "UIX", &child); LookupOperation child3(query, &child); runCase(mysql, ndb, query, tabName, 5, 3); } break; case 3: { TableScanOperation root(query); LookupOperation child(query, &root); runCase(mysql, ndb, query, tabName, 5, 5); } break; case 4: { TableScanOperation root(query); IndexLookupOperation child1(query, "UIX", &root); LookupOperation child2(query, &child1); IndexLookupOperation child3(query, "UIX", &child2); LookupOperation child1_2(query, &root); LookupOperation child2_2(query, &child1_2); runCase(mysql, ndb, query, tabName, 10, 10); } break; case 5: { IndexScanOperation root(query, "PRIMARY", 0, 10, NdbQueryOptions::ScanOrdering_descending); LookupOperation child(query, &root); runCase(mysql, ndb, query, tabName, 10, 10); } break; case 6: { TableScanOperation root(query, 3); LookupOperation child(query, &root); runCase(mysql, ndb, query, tabName, 5, 3); } break; #if 0 default: //case 6: { IndexScanOperation root(query, "PRIMARY", 0, 1000, NdbQueryOptions::ScanOrdering_descending); LookupOperation child(query, &root); runCase(mysql, ndb, query, tabName, 10*(caseNo-6), 10*(caseNo-6)); } break; #endif } } } }; using namespace SPJSanityTest; int main(int argc, char* argv[]){ if(argc!=4){ ndbout << "Usage: " << argv[0] << " " << endl; return NDBT_ProgramExit(NDBT_FAILED); } const char* const host=argv[1]; const int port = atoi(argv[2]); const char* const connectString = argv[3]; NDB_INIT(argv[0]); MYSQL mysql; require(mysql_init(&mysql)); if(!mysql_real_connect(&mysql, host, "root", "", "", port, NULL, 0)){ printMySQLError(mysql, "mysql_real_connect() failed:"); return NDBT_ProgramExit(NDBT_FAILED); } mySQLExec(mysql, "create database if not exists CK_DB"); mySQLExec(mysql, "use CK_DB"); { Ndb_cluster_connection con(connectString); if(con.connect(12, 5, 1) != 0){ ndbout << "Unable to connect to management server." << endl; return NDBT_ProgramExit(NDBT_FAILED); } int res = con.wait_until_ready(30,30); if (res != 0){ ndbout << "Cluster nodes not ready in 30 seconds." << endl; return NDBT_ProgramExit(NDBT_FAILED); } Ndb ndb(&con, "CK_DB"); if(ndb.init() != 0){ ERR(ndb.getNdbError()); return NDBT_ProgramExit(NDBT_FAILED); } runTestSuite(mysql, ndb); } // Must call ~Ndb_cluster_connection() before ndb_end(). ndb_end(0); return 0; } // Explicit template instantiations. template class Vector; template class GenericRow; template class GenericKey; template class GenericRow; template class GenericKey;