21 #include <mysqld_error.h> 
   23 #include <ndb_global.h> 
   27 #include "../../src/ndbapi/NdbQueryBuilder.hpp" 
   28 #include "../../src/ndbapi/NdbQueryOperation.hpp" 
   39 #define ASSERT_ALWAYS(cond) if(!(cond)){abort();} 
   41 #define ASSERT_ALWAYS assert 
   44 #define QRY_REQ_ARG_IS_NULL 4800 
   45 #define QRY_TOO_FEW_KEY_VALUES 4801 
   46 #define QRY_TOO_MANY_KEY_VALUES 4802 
   47 #define QRY_OPERAND_HAS_WRONG_TYPE 4803 
   48 #define QRY_CHAR_OPERAND_TRUNCATED 4804 
   49 #define QRY_NUM_OPERAND_RANGE 4805 
   50 #define QRY_MULTIPLE_PARENTS 4806 
   51 #define QRY_UNKONWN_PARENT 4807 
   52 #define QRY_UNKNOWN_COLUMN 4808 
   53 #define QRY_UNRELATED_INDEX 4809 
   54 #define QRY_WRONG_INDEX_TYPE 4810 
   55 #define QRY_OPERAND_ALREADY_BOUND 4811 
   56 #define QRY_DEFINITION_TOO_LARGE 4812 
   57 #define QRY_SEQUENTIAL_SCAN_SORTED 4813 
   58 #define QRY_RESULT_ROW_ALREADY_DEFINED 4814 
   59 #define QRY_HAS_ZERO_OPERATIONS 4815 
   60 #define QRY_IN_ERROR_STATE 4816 
   61 #define QRY_ILLEGAL_STATE 4817 
   62 #define QRY_WRONG_OPERATION_TYPE 4820 
   63 #define QRY_SCAN_ORDER_ALREADY_SET 4821 
   64 #define QRY_PARAMETER_HAS_WRONG_TYPE 4822 
   65 #define QRY_CHAR_PARAMETER_TRUNCATED 4823 
   66 #define QRY_MULTIPLE_SCAN_BRANCHES 4824 
   67 #define QRY_MULTIPLE_SCAN_SORTED 4825 
   70 namespace SPJSanityTest{
 
   72   static void resetError(
const NdbError& err)
 
   74     new (&
const_cast<NdbError&
>(err)) NdbError;
 
   79     static const char* getType(){
 
   87     const char* toStr(
char* buff)
 const {
 
   88       sprintf(buff, 
"%d", m_val);
 
   92     int compare(
const IntField& other)
 const{
 
   93       if (m_val > other.m_val)
 
   95       else if (m_val == other.m_val)
 
  101     Uint64 getValue()
 const{
 
  105     Uint32 getSize()
 const{
 
  115     static const char* getType(){
 
  116       return "VARCHAR(10)";
 
  122       sprintf(m_val, 
"c%5d", 
i);
 
  125     const char* toStr(
char* buff)
 const {
 
  126       sprintf(buff, 
"'%s'", getValue());
 
  130     int compare(
const StrField& other)
 const{
 
  131       return strcmp(getValue(), other.getValue());
 
  134     const char* getValue()
 const{
 
  139     Uint32 getSize()
 const{
 
  141       return strlen(m_val);
 
  146     mutable char m_val[10];
 
  151   template <
typename FieldType>
 
  154     static const int size = 2;
 
  155     FieldType m_values[size];
 
  159       ASSERT_ALWAYS(fieldNo<size);
 
  161       return builder.constValue(m_values[fieldNo].getValue());
 
  166   template <
typename FieldType>
 
  169     static const int size = 4;
 
  171     FieldType m_values[size];
 
  177       for(
int i = 0; 
i<size; 
i++){
 
  178         m_values[
i] = FieldType(
i+rowNo);
 
  182     static const char *getType(
int colNo){
 
  184       return FieldType::getType();
 
  187     static void makeSQLValues(
char* buffer, 
int rowNo){
 
  189       sprintf(buffer, 
"values(");
 
  190       char* tail = buffer+strlen(buffer);
 
  191       for(
int i = 0; 
i<size; 
i++){
 
  195           sprintf(tail, 
"%s,", row.m_values[
i].toStr(tmp));
 
  197           sprintf(tail, 
"%s)", row.m_values[
i].toStr(tmp));
 
  199         tail = buffer+strlen(buffer);
 
  209     void makeLessThanCond(NdbScanFilter& scanFilter){
 
  223   template <
typename FieldType>
 
  227       key.m_values[
i] = m_values[
i];    
 
  232   template <
typename FieldType>
 
  234     return getForeignKey(1);
 
  237   template <
typename FieldType>
 
  239     ASSERT_ALWAYS(keyNo<=1);
 
  242       key.m_values[
i] = m_values[getForeignKeyColNo(keyNo,
i)];    
 
  247   template <
typename FieldType>
 
  249     return getForeignKeyColNo(1, indexCol);
 
  252   template <
typename FieldType>
 
  259   template <
typename FieldType>
 
  262       if(a.m_values[
i].compare(b.m_values[
i]) != 0){
 
  269   template <
typename FieldType>
 
  272       if(a.m_values[
i].compare(b.m_values[
i]) != 0){
 
  280   template <
typename FieldType>
 
  283       if(a.m_values[
i].compare(b.m_values[
i]) == 1){
 
  290   template <
typename FieldType>
 
  291   static NdbOut& operator<<(NdbOut& out, const GenericRow<FieldType>& row){
 
  295       out << row.m_values[
i].toStr(buff);
 
  310   static const char* colName(
int colNo){
 
  311     static const char* names[] = {
 
  312       "c1", 
"c2", 
"c3", 
"c4", 
"c5", 
"c6", 
"c7", 
"c8", 
"c9", 
"c10" 
  314     ASSERT_ALWAYS(static_cast<unsigned int>(colNo)< 
sizeof names/
sizeof names[0]);
 
  318   static void printMySQLError(
MYSQL& mysql, 
const char* before=NULL){
 
  322     ndbout << mysql_error(&mysql) << endl;
 
  326   static void mySQLExec(
MYSQL& mysql, 
const char* stmt){
 
  327     ndbout << stmt << 
";" << endl;
 
  328     if(mysql_query(&mysql, stmt) != 0){
 
  329       ndbout << 
"Error executing '" << stmt << 
"' : ";
 
  330       printMySQLError(mysql);
 
  357       const Row* m_resultPtr;
 
  359       const char* m_resultCharPtr;
 
  362     Uint32 m_operationId;
 
  364     const Uint32 m_childNo;
 
  382     void compareRows(
const char* text, 
 
  384                      const Row* actual) 
const;
 
  390     explicit Query(Ndb& ndb);
 
  394       if (m_queryDef != NULL)
 
  396         m_queryDef->destroy();
 
  404     void submit(NdbTransaction& transaction);
 
  406     void submitOperation(
Operation& operation) 
const;
 
  408     void setRoot(
Operation& root){ m_root = &root;}
 
  419     Uint32 allocOperationId(){ 
 
  420       return m_operationCount++;
 
  424       return m_query->getQueryOperation(ident);
 
  427     int getTableSize()
 const { 
 
  436       return m_ndb.getDictionary();
 
  439     void close(
bool forceSend = 
false){
 
  440       m_query->
close(forceSend);
 
  448     Uint32 m_operationCount;
 
  470                                   const char* indexName,
 
  480     const char* 
const m_indexName;
 
  509                                 const char* indexName,
 
  527     const char* 
const m_indexName;
 
  529     const int m_lowerBoundRowNo;
 
  531     const int m_upperBoundRowNo;
 
  539     bool m_hasPreviousRow;
 
  545   Query::Query(Ndb& ndb):
 
  555     ASSERT_ALWAYS(m_builder != NULL);
 
  559     m_tableSize = tableSize;
 
  560     m_root->
build(*m_builder, tab);
 
  561     m_queryDef = m_builder->prepare();
 
  566     m_query = transaction.createQuery(m_queryDef);
 
  567     ASSERT_ALWAYS(m_query!=NULL);
 
  568     submitOperation(*m_root);
 
  571   void Query::submitOperation(
Operation& operation)
 const{
 
  574     for(Uint32 
i = 0; 
i<operation.m_children.size(); 
i++){
 
  575       submitOperation(*operation.m_children[
i]);
 
  584     m_operationDef(NULL),
 
  586     m_childNo(parent == NULL ? 0 : parent->m_children.
size())
 
  589       query.setRoot(*
this);
 
  591       parent->m_children.push_back(
this);
 
  597     m_operationId = 
m_query.allocOperationId();
 
  600     for(Uint32 
i = 0; 
i<m_children.size(); 
i++){
 
  601       m_children[
i]->build(builder, tab);
 
  607     for(Uint32 
i = 0; 
i<m_children.size(); 
i++){
 
  608       m_children[
i]->verifyRow();
 
  612   typedef const char* constCharPtr;
 
  614   void Operation::compareRows(
const char* text, 
 
  616                                    const Row* actual)
 const{
 
  619         ndbout << text << 
" operationId=" << m_operationId
 
  620                << 
" expected NULL and got it." << endl;
 
  622         ndbout << text << 
" operationId=" << m_operationId
 
  623                << 
" expected NULL but got." << *actual
 
  625         ASSERT_ALWAYS(
false);
 
  629         ndbout << text << 
" operationId=" << m_operationId
 
  630                << 
" expected: " << *expected
 
  631                << 
" but got NULL." << endl;
 
  632         ASSERT_ALWAYS(
false);
 
  634         ndbout << text << 
" operationId=" << m_operationId
 
  635                << 
" expected: " << *expected;
 
  636         if(*expected == *actual){
 
  637           ndbout << 
" and got it." << endl;
 
  639           ndbout << 
" but got: " << *actual;
 
  640           ASSERT_ALWAYS(
false);
 
  649   ::LookupOperation(
Query& query, 
 
  658       const Key key = 
Row(0).getPrimaryKey();
 
  660         keyOperands[
i] = key.makeConstOperand(builder, 
i);
 
  664       ASSERT_ALWAYS(builder.linkedValue(
m_parent->m_operationDef, 
 
  665                                         "unknown_col") == NULL);
 
  666       ASSERT_ALWAYS(builder.
getNdbError().code == QRY_UNKNOWN_COLUMN);
 
  670           builder.linkedValue(
m_parent->m_operationDef, 
 
  673         ASSERT_ALWAYS(keyOperands[
i]!=NULL);
 
  681     keyOperands[Key::size+1] = NULL;
 
  682     ASSERT_ALWAYS(builder.readTuple(&tab, keyOperands)== NULL);
 
  683     ASSERT_ALWAYS(builder.
getNdbError().code == QRY_TOO_MANY_KEY_VALUES);
 
  687     m_operationDef = builder.readTuple(&tab, keyOperands);
 
  688     ASSERT_ALWAYS(m_operationDef != NULL);
 
  691     keyOperands[Key::size-1] = builder.constValue(0x1fff1fff);
 
  692     ASSERT_ALWAYS(keyOperands[Key::size-1] != NULL);
 
  693     ASSERT_ALWAYS(builder.readTuple(&tab, keyOperands) == NULL);
 
  694     ASSERT_ALWAYS(builder.
getNdbError().code == QRY_OPERAND_HAS_WRONG_TYPE);
 
  697     keyOperands[Key::size-1] = NULL;
 
  698     ASSERT_ALWAYS(builder.readTuple(&tab, keyOperands) == NULL);
 
  699     ASSERT_ALWAYS(builder.
getNdbError().code == QRY_TOO_FEW_KEY_VALUES);
 
  705       = 
m_query.getOperation(m_operationId);
 
  707     ASSERT_ALWAYS(queryOp->setResultRowRef(NULL, 
 
  710     ASSERT_ALWAYS(queryOp->getQuery().
getNdbError().code == 
 
  711                   QRY_REQ_ARG_IS_NULL);
 
  712     ASSERT_ALWAYS(queryOp->
setOrdering(NdbQueryOptions::ScanOrdering_ascending)
 
  714     ASSERT_ALWAYS(queryOp->getQuery().
getNdbError().code == 
 
  715                   QRY_WRONG_OPERATION_TYPE);
 
  717     ASSERT_ALWAYS(queryOp->setResultRowRef(
m_query.getNdbRecord(), 
 
  721     ASSERT_ALWAYS(queryOp->setResultRowRef(
m_query.getNdbRecord(), 
 
  724     ASSERT_ALWAYS(queryOp->getQuery().
getNdbError().code == 
 
  725                   QRY_RESULT_ROW_ALREADY_DEFINED);
 
  730       const Row expected(0);
 
  731       compareRows(
"lookup root operation",
 
  737         .getOperation(m_operationId);
 
  738       if(!queryOp->getParentOperation(0)->isRowNULL()){
 
  741           ->getForeignKey(m_childNo);
 
  743         for(
int i = 0; 
i<
m_query.getTableSize(); 
i++){
 
  745           if(row.getPrimaryKey() == key){
 
  747             compareRows(
"lookup child operation",
 
  752         if(!found && !queryOp->isRowNULL()){
 
  753           compareRows(
"lookup child operation",
 
  764   ::IndexLookupOperation(
Query& query, 
 
  765                          const char* indexName, 
 
  768     m_indexName(indexName){
 
  775       = m_query.getDictionary();
 
  777     sprintf(fullName, 
"%s$unique", m_indexName);
 
  780     ASSERT_ALWAYS(index!=NULL);
 
  784       const Key key = 
Row(0).getIndexKey();
 
  785       for(
int i = 0; 
i<Key::size; 
i++){
 
  786         keyOperands[
i] = key.makeConstOperand(builder, 
i);
 
  789       for(
int i = 0; 
i<Key::size; 
i++){
 
  791           builder.linkedValue(m_parent->m_operationDef,
 
  794         ASSERT_ALWAYS(keyOperands[
i]!=NULL);
 
  800     keyOperands[Key::size] = NULL;
 
  801     m_operationDef = builder.readTuple(index, &tab, keyOperands);
 
  806     ASSERT_ALWAYS(orderedIndex != NULL);
 
  807     ASSERT_ALWAYS(builder.readTuple(orderedIndex, &tab, keyOperands) == NULL);
 
  808     ASSERT_ALWAYS(builder.
getNdbError().code == QRY_WRONG_INDEX_TYPE);
 
  814       = 
m_query.getOperation(m_operationId);
 
  815     queryOp->setResultRowRef(
m_query.getNdbRecord(), 
 
  822       const Row expected(0);
 
  823       compareRows(
"index lookup root operation",
 
  829         .getOperation(m_operationId);
 
  830       if(!queryOp->getParentOperation(0)->isRowNULL()){
 
  833           ->getForeignKey(m_childNo);
 
  835         for(
int i = 0; 
i<
m_query.getTableSize(); 
i++){
 
  837           if(row.getIndexKey() == key){
 
  839             compareRows(
"index lookup child operation",
 
  844         if(!found && !queryOp->isRowNULL()){
 
  845           compareRows(
"index lookup child operation",
 
  856   ::TableScanOperation(
Query& query, 
int lessThanRow):
 
  859     m_lessThanRow(lessThanRow){
 
  864     m_operationDef = builder.scanTable(&tab);
 
  865     m_rowFound = 
new bool[
m_query.getTableSize()];
 
  866     for(
int i = 0; 
i<
m_query.getTableSize(); 
i++){
 
  867       m_rowFound[
i] = 
false;
 
  873       = 
m_query.getOperation(m_operationId);
 
  874     queryOp->setResultRowRef(
m_query.getNdbRecord(), 
 
  877     if(m_lessThanRow!=-1){
 
  879       NdbScanFilter filter(&
code);
 
  880       ASSERT_ALWAYS(filter.begin()==0);
 
  881       Row(m_lessThanRow).makeLessThanCond(filter);
 
  882       ASSERT_ALWAYS(filter.end()==0);
 
  889     const int upperBound = 
 
  893     for(
int i = 0; 
i<upperBound; 
i++){
 
  895       if(
Row(
i) == *m_resultPtr){
 
  898           ndbout << 
"Root table scan operation: "  
  900                  << 
"appeared twice." << endl;
 
  901           ASSERT_ALWAYS(
false);
 
  903         m_rowFound[
i] = 
true;
 
  907       ndbout << 
"Root table scan operation. Unexpected row: "  
  908              << *m_resultPtr << endl;
 
  909       ASSERT_ALWAYS(
false);
 
  911       ndbout << 
"Root table scan operation. Got row: "  
  913              << 
" as expected." << endl;
 
  920   ::IndexScanOperation(
Query& query, 
 
  921                        const char* indexName,
 
  926     m_indexName(indexName),
 
  927     m_lowerBoundRowNo(lowerBoundRowNo),
 
  928     m_upperBoundRowNo(upperBoundRowNo),
 
  929     m_ordering(ordering),
 
  931     m_hasPreviousRow(false){
 
  940     ASSERT_ALWAYS(index!=NULL);
 
  945     ASSERT_ALWAYS(strcmp(m_indexName, 
"PRIMARY")==0);
 
  948     const Key& lowKey = *
new Key(
Row(m_lowerBoundRowNo).getPrimaryKey());
 
  949     const Key& highKey = *
new Key(
Row(m_upperBoundRowNo).getPrimaryKey());
 
  952       low[
i] = lowKey.makeConstOperand(builder, 
i);
 
  953       high[
i] = highKey.makeConstOperand(builder, 
i);
 
  962       = builder.scanIndex(index, &tab, &bound, &options);
 
  963     m_operationDef = opDef;
 
  964     ASSERT_ALWAYS(m_operationDef!=NULL);
 
  965     m_rowFound = 
new bool[
m_query.getTableSize()];
 
  966     for(
int i = 0; 
i<
m_query.getTableSize(); 
i++){
 
  967       m_rowFound[
i] = 
false;
 
  973       = 
m_query.getOperation(m_operationId);
 
  974     queryOp->setResultRowRef(
m_query.getNdbRecord(), 
 
  980       ASSERT_ALWAYS(queryOp
 
  981                     ->setOrdering(NdbQueryOptions::ScanOrdering_ascending) != 0);
 
  982       ASSERT_ALWAYS(queryOp->getQuery().
getNdbError().code == 
 
  983                     QRY_SCAN_ORDER_ALREADY_SET);
 
  986       ASSERT_ALWAYS(queryOp->getQuery().
getNdbError().code == 
 
  987                     QRY_SEQUENTIAL_SCAN_SORTED);
 
  993     for(
int i = m_lowerBoundRowNo; 
i<=m_upperBoundRowNo; 
i++){
 
  995       if(row == *m_resultPtr){
 
  998           ndbout << 
"Root index scan operation: "  
 1000                  << 
"appeared twice." << endl;
 
 1001           ASSERT_ALWAYS(
false);
 
 1003         m_rowFound[
i] = 
true;
 
 1007       ndbout << 
"Root index scan operation. Unexpected row: "  
 1008              << *m_resultPtr << endl;
 
 1009       ASSERT_ALWAYS(
false);
 
 1011       if(m_hasPreviousRow){
 
 1013         case NdbQueryOptions::ScanOrdering_ascending:
 
 1014           if(!lessOrEqual(m_previousRow, *m_resultPtr)){
 
 1015             ndbout << 
"Error in result ordering. Did not expect row " 
 1018             ASSERT_ALWAYS(
false);
 
 1021         case NdbQueryOptions::ScanOrdering_descending:
 
 1022           if(lessOrEqual(m_previousRow, *m_resultPtr)){
 
 1023             ndbout << 
"Error in result ordering. Did not expect row " 
 1026             ASSERT_ALWAYS(
false);
 
 1032           ASSERT_ALWAYS(
false);
 
 1035       m_hasPreviousRow = 
true;
 
 1036       m_previousRow = *m_resultPtr;
 
 1037       ndbout << 
"Root index scan operation. Got row: "  
 1039              << 
" as expected." << endl;
 
 1046   void makeTable(
MYSQL& mysql, 
const char* 
name, 
int rowCount){
 
 1049     sprintf(cmd, 
"drop table if exists %s", name);
 
 1050     mySQLExec(mysql, cmd);
 
 1051     sprintf(cmd, 
"create table %s (\n", name);
 
 1052     for(
int i = 0; 
i<Row::size; 
i++){
 
 1053       sprintf(piece, 
"   %s %s NOT NULL,\n", colName(
i), Row::getType(
i));
 
 1056     strcat(cmd, 
"   PRIMARY KEY(");
 
 1057     for(
int i = 0; 
i<Key::size; 
i++){
 
 1058       strcat(cmd, colName(
i));
 
 1059       if(
i<Key::size - 1){
 
 1062         strcat(cmd, 
"),\n");
 
 1065     strcat(cmd, 
"   UNIQUE KEY UIX (");
 
 1066     for(
int i = 0; 
i<Key::size; 
i++){
 
 1068       if(
i<Key::size - 1){
 
 1071         strcat(cmd, 
"))\n");
 
 1074     strcat(cmd, 
"ENGINE=NDB");
 
 1075     mySQLExec(mysql, cmd);
 
 1076     for(
int i = 0; 
i<rowCount; 
i++){
 
 1077       Row::makeSQLValues(piece, 
i);
 
 1078       sprintf(cmd, 
"insert into %s %s", name, piece);
 
 1079       mySQLExec(mysql, cmd);
 
 1085   void runCase(
MYSQL& mysql, 
 
 1088                const char* tabName, 
 
 1092     makeTable(mysql, tabName, tabSize);
 
 1095     ASSERT_ALWAYS(tab!=NULL);
 
 1097     query.
build(*tab, tabSize);
 
 1098     NdbTransaction* trans = ndb.startTransaction();
 
 1099     ASSERT_ALWAYS(trans!=NULL);
 
 1102     ASSERT_ALWAYS(trans->execute(NoCommit)==0);
 
 1104     for(
int i = 0; 
i<rowCount; 
i++){
 
 1105       ASSERT_ALWAYS(query.nextResult() ==  NdbQuery::NextResult_gotRow);
 
 1110         ndb.closeTransaction(trans);
 
 1114     ASSERT_ALWAYS(query.nextResult() ==  NdbQuery::NextResult_scanComplete);
 
 1115     ndb.closeTransaction(trans);
 
 1119   void runTestSuite(
MYSQL& mysql, Ndb& ndb){
 
 1120     for(
int caseNo = 0; caseNo<7; caseNo++){
 
 1121       ndbout << endl << 
"Running test case " << caseNo << endl;
 
 1124       sprintf(tabName, 
"t%d", caseNo);
 
 1133           runCase(mysql, ndb, query, tabName, 1, 1);
 
 1140           runCase(mysql, ndb, query, tabName, 5, 1);
 
 1150           runCase(mysql, ndb, query, tabName, 5, 3);
 
 1157           runCase(mysql, ndb, query, tabName, 5, 5);
 
 1168           runCase(mysql, ndb, query, tabName, 10, 10);
 
 1174                                  NdbQueryOptions::ScanOrdering_descending);
 
 1176           runCase(mysql, ndb, query, tabName, 10, 10);
 
 1183           runCase(mysql, ndb, query, tabName, 5, 3);
 
 1191                                  NdbQueryOptions::ScanOrdering_descending);
 
 1193           runCase(mysql, ndb, query, tabName, 10*(caseNo-6), 10*(caseNo-6));
 
 1204 using namespace SPJSanityTest;
 
 1206 int main(
int argc, 
char* argv[]){
 
 1208     ndbout << 
"Usage: " << argv[0] 
 
 1209            << 
" <mysql IP address> <mysql port> <cluster connect string>"  
 1211     return NDBT_ProgramExit(NDBT_FAILED);
 
 1213   const char* 
const host=argv[1];
 
 1214   const int port = atoi(argv[2]);
 
 1215   const char* 
const connectString = argv[3];
 
 1219   ASSERT_ALWAYS(mysql_init(&mysql));
 
 1220   if(!mysql_real_connect(&mysql, host, 
"root", 
"", 
"",
 
 1222     printMySQLError(mysql, 
"mysql_real_connect() failed:");
 
 1223     return NDBT_ProgramExit(NDBT_FAILED);
 
 1225   mySQLExec(mysql, 
"create database if not exists CK_DB");
 
 1226   mySQLExec(mysql, 
"use CK_DB");
 
 1229     if(con.
connect(12, 5, 1) != 0){
 
 1230       ndbout << 
"Unable to connect to management server." << endl;
 
 1231       return NDBT_ProgramExit(NDBT_FAILED);
 
 1236       ndbout << 
"Cluster nodes not ready in 30 seconds." << endl;
 
 1237       return NDBT_ProgramExit(NDBT_FAILED);
 
 1239     Ndb ndb(&con, 
"CK_DB");
 
 1240     if(ndb.init() != 0){
 
 1241       ERR(ndb.getNdbError());
 
 1242       return NDBT_ProgramExit(NDBT_FAILED);
 
 1244     runTestSuite(mysql, ndb);