/* Copyright (C) 2003 MySQL AB 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */ /* * testOdbcDriver * * Test of ODBC and SQL using a fixed set of tables. */ #include #undef test #include #include #include #include #ifdef ndbODBC #include #endif #include #undef BOOL #include #include #include #include #include #include #include #ifdef ndbODBC #include #else #define NDBT_OK 0 #define NDBT_FAILED 1 #define NDBT_WRONGARGS 2 static int NDBT_ProgramExit(int rcode) { const char* rtext = "Unknown"; switch (rcode) { case NDBT_OK: rtext = "OK"; break; case NDBT_FAILED: rtext = "Failed"; break; case NDBT_WRONGARGS: rtext = "Wrong arguments"; break; }; ndbout_c("\nNDBT_ProgramExit: %d - %s\n", rcode, rtext); return rcode; } #endif #ifdef DMALLOC #include #endif #define arraySize(x) (sizeof(x)/sizeof(x[0])) #define SQL_ATTR_NDB_TUPLES_FETCHED 66601 // options #define MAX_THR 128 // max threads struct Opt { const char* m_name[100]; unsigned m_namecnt; bool m_core; unsigned m_depth; const char* m_dsn; unsigned m_errs; const char* m_fragtype; unsigned m_frob; const char* m_home; unsigned m_loop; bool m_nogetd; bool m_noputd; bool m_nosort; unsigned m_scale; bool m_serial; const char* m_skip[100]; unsigned m_skipcnt; unsigned m_subloop; const char* m_table; unsigned m_threads; unsigned m_trace; unsigned m_v; Opt() : m_namecnt(0), m_core(false), m_depth(5), m_dsn("NDB"), m_errs(0), m_fragtype(0), m_frob(0), m_home(0), m_loop(1), m_nogetd(false), m_noputd(false), m_nosort(false), m_scale(100), m_serial(false), m_skipcnt(0), m_subloop(1), m_table(0), m_threads(1), m_trace(0), m_v(1) { for (unsigned i = 0; i < arraySize(m_name); i++) m_name[i] = 0; for (unsigned i = 0; i < arraySize(m_skip); i++) m_skip[i] = 0; } }; static Opt opt; static void listCases(); static void listTables(); static void printusage() { Opt d; ndbout << "usage: testOdbcDriver [options]" << endl << "-case name run only named tests (substring match - can be repeated)" << endl << "-core dump core on failure" << endl << "-depth N join depth - default " << d.m_depth << endl << "-dsn string data source name - default " << d.m_dsn << endl << "-errs N allow N errors before quitting - default " << d.m_errs << endl << "-fragtype t fragment type single/small/medium/large" << d.m_errs << endl << "-frob X case-dependent tweak (number)" << endl << "-home dir set NDB_HOME (contains Ndb.cfg)" << endl << "-loop N loop N times (0 = forever) - default " << d.m_loop << endl << "-nogetd do not use SQLGetData - default " << d.m_nogetd << endl << "-noputd do not use SQLPutData - default " << d.m_noputd << endl << "-nosort no order-by in verify scan (checks non-Pk values only)" << endl << "-scale N row count etc - default " << d.m_scale << endl << "-serial run multi-threaded test cases one at a time" << endl << "-skip name skip named tests (substring match - can be repeated)" << endl << "-subloop N loop count per case (same threads) - default " << d.m_subloop << endl << "-table T do only table T (table name on built-in list)" << endl << "-threads N number of threads (max " << MAX_THR << ") - default " << d.m_threads << endl << "-trace N trace in NDB ODBC driver - default " << d.m_trace << endl << "-v N verbosity - default " << d.m_v << endl ; listCases(); listTables(); } static void fatal(const char* fmt, ...) { va_list ap; char buf[200]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); ndbout << buf << endl; if (opt.m_errs != 0) { opt.m_errs--; return; } if (opt.m_core) abort(); NDBT_ProgramExit(NDBT_FAILED); exit(1); } static void cleanprint(const char* s, unsigned n) { for (unsigned i = 0; i < n; i++) { char b[10]; if (0x20 < s[i] && s[i] <= 0x7e) sprintf(b, "%c", s[i]); else sprintf(b, "\\%02x", (unsigned)s[i]); ndbout << b; } } // global mutex static NdbMutex my_mutex = NDB_MUTEX_INITIALIZER; static void lock_mutex() { NdbMutex_Lock(&my_mutex); } static void unlock_mutex() { NdbMutex_Unlock(&my_mutex); } // semaphore zeroed before each call to a test routine static unsigned my_sema = 0; // print mutex static NdbMutex out_mutex = NDB_MUTEX_INITIALIZER; static NdbOut& lock(NdbOut& out) { NdbMutex_Lock(&out_mutex); return out; } static NdbOut& unlock(NdbOut& out) { NdbMutex_Unlock(&out_mutex); return out; } static unsigned urandom(unsigned n) { assert(n != 0); unsigned i = random(); return i % n; } // test cases struct Test; struct Case { enum Mode { Single = 1, // single thread Serial = 2, // all threads but one at a time Thread = 3 // all threads in parallel }; const char* m_name; void (*m_func)(Test& test); Mode m_mode; unsigned m_stuff; const char* m_desc; Case(const char* name, void (*func)(Test& test), Mode mode, unsigned stuff, const char* desc) : m_name(name), m_func(func), m_mode(mode), m_stuff(stuff), m_desc(desc) { } const char* modename() const { const char* s = "?"; if (m_mode == Case::Single) return "Single"; if (m_mode == Case::Serial) return "Serial"; if (m_mode == Case::Thread) return "Thread"; return "?"; } bool matchcase() const { if (opt.m_namecnt == 0) return ! skipcase(); for (unsigned i = 0; i < opt.m_namecnt; i++) { if (strstr(m_name, opt.m_name[i]) != 0) return ! skipcase(); } return false; } private: bool skipcase() const { for (unsigned i = 0; i < opt.m_skipcnt; i++) { if (strstr(m_name, opt.m_skip[i]) != 0) return true; } return false; } }; // calculate values struct Calc { enum { m_mul = 1000000 }; unsigned m_no; unsigned m_base; unsigned m_salt; // modifies non-PK values bool m_const; // base non-PK values on PK of row 0 Calc(unsigned no) : m_no(no), m_salt(0), m_const(false) { m_base = m_no * m_mul; } void calcPk(unsigned rownum, char* v, unsigned n) const { char b[10]; sprintf(b, "%08x", m_base + rownum); for (unsigned i = 0; i < n; i++) { char c = i < n - 1 ? b[i % 8] : 0; v[i] = c; } } void calcPk(unsigned rownum, long* v) const { *v = m_base + rownum; } void hashPk(unsigned* hash, const char* v, unsigned n) const { for (unsigned i = 0; i < n; i++) { *hash ^= (v[i] << i); } } void hashPk(unsigned* hash, long v) const { *hash ^= v; } void calcNk(unsigned hash, char* v, unsigned n, SQLINTEGER* ind, bool null) const { unsigned m = hash % n; for (unsigned i = 0; i < n; i++) { char c = i < m ? 'a' + (hash + i) % ('z' - 'a' + 1) : i < n - 1 ? ' ' : 0; v[i] = c; } *ind = null && hash % 9 == 0 ? SQL_NULL_DATA : SQL_NTS; } void calcNk(unsigned hash, long* v, SQLINTEGER* ind, bool null) const { *v = long(hash); *ind = null && hash % 7 == 0 ? SQL_NULL_DATA : 0; } void calcNk(unsigned hash, double* v, SQLINTEGER* ind, bool null) const { *v = long(hash) / 1000.0; *ind = null && hash % 5 == 0 ? SQL_NULL_DATA : 0; } bool verify(const char* v1, SQLINTEGER ind1, const char* v2, SQLINTEGER ind2, unsigned n) { if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA) return true; if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA) if (memcmp(v1, v2, n) == 0) return true; if (ind1 == SQL_NULL_DATA) v1 = "NULL"; if (ind2 == SQL_NULL_DATA) v2 = "NULL"; ndbout << "verify failed: got "; if (ind1 == SQL_NULL_DATA) ndbout << "NULL"; else cleanprint(v1, n); ndbout << " != "; if (ind2 == SQL_NULL_DATA) ndbout << "NULL"; else cleanprint(v2, n); ndbout << endl; return false; } bool verify(long v1, SQLINTEGER ind1, long v2, SQLINTEGER ind2) { char buf1[40], buf2[40]; if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA) return true; if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA) if (v1 == v2) return true; if (ind1 == SQL_NULL_DATA) strcpy(buf1, "NULL"); else sprintf(buf1, "%ld", v1); if (ind2 == SQL_NULL_DATA) strcpy(buf2, "NULL"); else sprintf(buf2, "%ld", v2); ndbout << "verify failed: got " << buf1 << " != " << buf2 << endl; return false; } bool verify(double v1, SQLINTEGER ind1, double v2, SQLINTEGER ind2) { char buf1[40], buf2[40]; if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA) return true; if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA) if (fabs(v1 - v2) < 1) // XXX return true; if (ind1 == SQL_NULL_DATA) strcpy(buf1, "NULL"); else sprintf(buf1, "%.10f", v1); if (ind2 == SQL_NULL_DATA) strcpy(buf2, "NULL"); else sprintf(buf2, "%.10f", v2); ndbout << "verify failed: got " << buf1 << " != " << buf2 << endl; return false; } }; #if defined(NDB_SOLARIS) || defined(NDB_LINUX) || defined(NDB_MACOSX) #define HAVE_SBRK #else #undef HAVE_SBRK #endif struct Timer { Timer() : m_cnt(0), m_calls(0), m_on(0), m_msec(0) #ifndef NDB_WIN32 , m_brk(0), m_incr(0) #endif { } void timerOn() { m_cnt = 0; m_calls = 0; m_on = NdbTick_CurrentMillisecond(); #ifdef HAVE_SBRK m_brk = (int)sbrk(0); #endif } void timerOff() { m_msec = NdbTick_CurrentMillisecond() - m_on; if (m_msec <= 0) m_msec = 1; #ifdef HAVE_SBRK m_incr = (int)sbrk(0) - m_brk; if (m_incr < 0) m_incr = 0; #endif } void timerCnt(unsigned cnt) { m_cnt += cnt; } void timerCnt(const Timer& timer) { m_cnt += timer.m_cnt; m_calls += timer.m_calls; } friend NdbOut& operator<<(NdbOut& out, const Timer& timer) { out << timer.m_cnt << " ( " << 1000 * timer.m_cnt / timer.m_msec << "/sec )"; #ifdef HAVE_SBRK out << " - " << timer.m_incr << " sbrk"; if (opt.m_namecnt != 0) { // per case meaningless if many cases if (timer.m_cnt > 0) out << " ( " << timer.m_incr / timer.m_cnt << "/cnt )"; } #endif out << " - " << timer.m_calls << " calls"; return out; } protected: unsigned m_cnt; // count rows or whatever unsigned m_calls; // count ODBC function calls NDB_TICKS m_on; unsigned m_msec; #ifdef HAVE_SBRK int m_brk; int m_incr; #endif }; #define MAX_MESSAGE 500 #define MAX_DIAG 20 struct Diag { char m_state[5+1]; SQLINTEGER m_native; char m_message[MAX_MESSAGE]; unsigned m_flag; // temp use Diag() { strcpy(m_state, "00000"); m_native = 0; memset(m_message, 0, sizeof(m_message)); m_flag = 0; } const char* text() { snprintf(m_buf, sizeof(m_buf), "%s %d '%s'", m_state, (int)m_native, m_message); return m_buf; } void getDiag(SQLSMALLINT type, SQLHANDLE handle, unsigned k, unsigned count) { int ret; SQLSMALLINT length = -1; memset(m_message, 0, MAX_MESSAGE); ret = SQLGetDiagRec(type, handle, k, (SQLCHAR*)m_state, &m_native, (SQLCHAR*)m_message, MAX_MESSAGE, &length); if (k <= count && ret != SQL_SUCCESS) fatal("SQLGetDiagRec %d of %d: return %d != SQL_SUCCESS", k, count, (int)ret); if (k <= count && strlen(m_message) != length) fatal("SQLGetDiagRec %d of %d: message length %d != %d", k, count, strlen(m_message), length); if (k > count && ret != SQL_NO_DATA) fatal("SQLGetDiagRec %d of %d: return %d != SQL_NO_DATA", k, count, (int)ret); m_flag = 0; } private: char m_buf[MAX_MESSAGE]; }; struct Diags { Diag m_diag[MAX_DIAG]; SQLINTEGER m_diagCount; SQLINTEGER m_rowCount; SQLINTEGER m_functionCode; void getDiags(SQLSMALLINT type, SQLHANDLE handle) { int ret; m_diagCount = -1; ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_NUMBER, &m_diagCount, SQL_IS_INTEGER, 0); if (ret == SQL_INVALID_HANDLE) return; if (ret != SQL_SUCCESS) fatal("SQLGetDiagField: return %d != SQL_SUCCESS", (int)ret); if (m_diagCount < 0 || m_diagCount > MAX_DIAG) fatal("SQLGetDiagField: count %d", (int)m_diagCount); for (unsigned k = 0; k < MAX_DIAG; k++) { m_diag[k].getDiag(type, handle, k + 1, m_diagCount); if (k == m_diagCount + 1) break; } m_rowCount = -1; m_functionCode = SQL_DIAG_UNKNOWN_STATEMENT; if (type == SQL_HANDLE_STMT) { ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_ROW_COUNT, &m_rowCount, SQL_IS_INTEGER, 0); #ifndef iODBC if (ret != SQL_SUCCESS) fatal("SQLGetDiagField: return %d != SQL_SUCCESS", (int)ret); #endif ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_DYNAMIC_FUNCTION_CODE, &m_functionCode, SQL_IS_INTEGER, 0); } } void showDiags() { for (unsigned k = 0; 0 <= m_diagCount && k < m_diagCount; k++) { Diag& diag = m_diag[k]; ndbout << "diag " << k + 1; ndbout << (diag.m_flag ? " [*]" : " [ ]"); ndbout << " " << diag.text() << endl; if (k > 10) abort(); } } }; struct Exp { int m_ret; const char* m_state; SQLINTEGER m_native; Exp() : m_ret(SQL_SUCCESS), m_state(""), m_native(0) {} Exp(int ret, const char* state) : m_ret(ret), m_state(state) {} }; struct Test : Calc, Timer, Diags { Test(unsigned no, unsigned loop) : Calc(no), m_loop(loop), m_stuff(0), m_perf(false), ccp(0) { exp(SQL_SUCCESS, 0, 0, true); } unsigned m_loop; // current loop Exp m_expList[20]; // expected results unsigned m_expCount; int m_ret; // actual return code int m_stuff; // the stuff of abuse bool m_perf; // check no diags on success const Case* ccp; // current case void exp(int ret, const char* state, SQLINTEGER native, bool reset) { if (reset) m_expCount = 0; unsigned i = m_expCount++; assert(i < arraySize(m_expList) - 1); m_expList[i].m_ret = ret; m_expList[i].m_state = state == 0 ? "" : state; m_expList[i].m_native = native; } void runCase(const Case& cc) { ccp = &cc; if (opt.m_v >= 3) ndbout << cc.m_name << ": start" << endl; m_rowCount = -1; NDB_TICKS m_ms1 = NdbTick_CurrentMillisecond(); m_salt = m_loop | (16 << cc.m_stuff); m_const = cc.m_stuff == 0; m_stuff = cc.m_stuff; (*cc.m_func)(*this); NDB_TICKS m_ms2 = NdbTick_CurrentMillisecond(); } void run(SQLSMALLINT type, SQLHANDLE handle, int line, int ret) { m_calls++; m_ret = ret; if (m_perf && (m_ret == SQL_SUCCESS)) return; m_diagCount = 0; if (handle != SQL_NULL_HANDLE) getDiags(type, handle); if (m_diagCount <= 0 && (ret != SQL_SUCCESS && ret != SQL_INVALID_HANDLE && ret != SQL_NEED_DATA && ret != SQL_NO_DATA)) { fatal("%s: thr %d line %d: ret=%d but no diag records", ccp->m_name, m_no, line, ret); } for (unsigned k = 0; 0 <= m_diagCount && k < m_diagCount; k++) { Diag& diag = m_diag[k]; bool match = false; for (unsigned i = 0; i < m_expCount; i++) { if (strcmp(diag.m_state, m_expList[i].m_state) == 0 && (diag.m_native % 10000 == m_expList[i].m_native % 10000 || m_expList[i].m_native == -1)) { match = true; diag.m_flag = 0; continue; } diag.m_flag = 1; // mark unexpected } if (! match) { showDiags(); fatal("%s: thr %d line %d: unexpected diag [*] ret=%d cnt=%d", ccp->m_name, m_no, line, (int)ret, (int)m_diagCount); } } bool match = false; for (unsigned i = 0; i < m_expCount; i++) { if (ret == m_expList[i].m_ret) { match = true; break; } } if (! match) { showDiags(); fatal("%s: thr %d line %d: ret=%d not expected", ccp->m_name, m_no, line, ret); } // reset expected to success exp(SQL_SUCCESS, 0, 0, true); } void chk(SQLSMALLINT type, SQLHANDLE handle, int line, bool match, const char* fmt, ...) { if (match) return; va_list ap; va_start(ap, fmt); char buf[500]; vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fatal("%s: thr %d line %d: check failed - %s", ccp->m_name, m_no, line, buf); } }; #define HNull 0, SQL_NULL_HANDLE, __LINE__ #define HEnv(h) SQL_HANDLE_ENV, h, __LINE__ #define HDbc(h) SQL_HANDLE_DBC, h, __LINE__ #define HStmt(h) SQL_HANDLE_STMT, h, __LINE__ #define HDesc(h) SQL_HANDLE_DESC, h, __LINE__ // string support #define MAX_SQL 20000 static void scopy(char*& ptr, const char* fmt, ...) { va_list ap; va_start(ap, fmt); vsprintf(ptr, fmt, ap); va_end(ap); ptr += strlen(ptr); } static bool blankeq(const char* s1, const char* s2, bool caseSensitive = false) { unsigned n1 = strlen(s1); unsigned n2 = strlen(s2); unsigned i = 0; char c1 = 0; char c2 = 0; while (i < n1 || i < n2) { c1 = i < n1 ? s1[i] : 0x20; if (! caseSensitive && 'a' <= c1 && c1 <= 'z') c1 -= 'a' - 'A'; c2 = i < n2 ? s2[i] : 0x20; if (! caseSensitive && 'a' <= c2 && c2 <= 'z') c2 -= 'a' - 'A'; if (c1 != c2) break; i++; } return c1 == c2; } // columns and tables struct Col { enum Type { Char = SQL_CHAR, Varchar = SQL_VARCHAR, Int = SQL_INTEGER, Bigint = SQL_BIGINT, Real = SQL_REAL, Double = SQL_DOUBLE }; enum CType { CChar = SQL_C_CHAR, CLong = SQL_C_SLONG, CDouble = SQL_C_DOUBLE }; enum Cons { Null, // nullable NotNull, // not nullable Pk // part of primary key }; const char* m_name; Type m_type; unsigned m_length; Cons m_cons; CType m_ctype; Col() : m_type((Type)999) { } Col(const char* name, Type type, unsigned length, Cons cons, CType ctype) : m_name(name), m_type(type), m_length(length), m_cons(cons), m_ctype(ctype) { } unsigned size() const { switch (m_type) { case Char: case Varchar: return m_length; case Int: return 4; case Bigint: return 8; case Real: return 4; case Double: return 8; } assert(false); return 0; } unsigned csize() const { // size as char plus terminating null switch (m_ctype) { case CChar: return m_length + 1; case CLong: return 12; case CDouble: return 24; } assert(false); return 0; } void typespec(char*& ptr) const { switch (m_type) { case Char: scopy(ptr, "char(%d)", m_length); return; case Varchar: scopy(ptr, "varchar(%d)", m_length); return; case Int: scopy(ptr, "int"); return; case Bigint: scopy(ptr, "bigint"); return; case Real: scopy(ptr, "real"); return; case Double: scopy(ptr, "float"); return; } assert(false); } SQLSMALLINT type() const { return (SQLSMALLINT)m_type; } SQLSMALLINT ctype() const { return (SQLSMALLINT)m_ctype; } void create(char*& ptr, bool pk) const { scopy(ptr, "%s", m_name); scopy(ptr, " "); typespec(ptr); if (m_cons == Pk && pk) { scopy(ptr, " primary key"); } if (m_cons == NotNull) { scopy(ptr, " not null"); } } }; static Col ColUndef; struct Tab { const char* m_name; const Col* m_colList; unsigned m_colCount; unsigned m_pkCount; unsigned* m_pkIndex; unsigned m_nkCount; unsigned* m_nkIndex; char m_upperName[20]; Tab(const char* name, const Col* colList, unsigned colCount) : m_name(name), m_colList(colList), m_colCount(colCount) { m_pkCount = 0; m_nkCount = 0; for (unsigned i = 0; i < m_colCount; i++) { const Col& col = m_colList[i]; if (col.m_cons == Col::Pk) m_pkCount++; else m_nkCount++; } m_pkIndex = new unsigned[m_pkCount]; m_nkIndex = new unsigned[m_nkCount]; unsigned pk = 0; unsigned nk = 0; for (unsigned i = 0; i < m_colCount; i++) { const Col& col = m_colList[i]; if (col.m_cons == Col::Pk) m_pkIndex[pk++] = i; else m_nkIndex[nk++] = i; } assert(pk == m_pkCount && nk == m_nkCount); strcpy(m_upperName, m_name); for (char* p = m_upperName; *p != 0; p++) { if ('a' <= *p && *p <= 'z') *p -= 'a' - 'A'; } } ~Tab() { delete[] m_pkIndex; delete[] m_nkIndex; } void drop(char*& ptr) const { scopy(ptr, "drop table %s", m_name); } void create(char*& ptr) const { scopy(ptr, "create table %s (", m_name); for (unsigned i = 0; i < m_colCount; i++) { if (i > 0) scopy(ptr, ", "); const Col& col = m_colList[i]; col.create(ptr, m_pkCount == 1); } if (m_pkCount != 1) { scopy(ptr, ", primary key ("); for (unsigned i = 0; i < m_pkCount; i++) { const Col& col = m_colList[m_pkIndex[i]]; if (i > 0) scopy(ptr, ", "); scopy(ptr, "%s", col.m_name); } scopy(ptr, ")"); } scopy(ptr, ")"); } void wherePk(char*& ptr) const { scopy(ptr, " where"); for (unsigned i = 0; i < m_pkCount; i++) { const Col& col = m_colList[m_pkIndex[i]]; if (i > 0) scopy(ptr, " and"); scopy(ptr, " %s = ?", col.m_name); } } void whereRange(char*& ptr) const { scopy(ptr, " where"); for (unsigned i = 0; i < m_pkCount; i++) { const Col& col = m_colList[m_pkIndex[i]]; if (i > 0) scopy(ptr, " and"); scopy(ptr, " ? <= %s", col.m_name); scopy(ptr, " and "); scopy(ptr, "%s < ?", col.m_name); } } void orderPk(char*& ptr) const { scopy(ptr, " order by"); for (unsigned i = 0; i < m_pkCount; i++) { const Col& col = m_colList[m_pkIndex[i]]; if (i > 0) scopy(ptr, ", "); else scopy(ptr, " "); scopy(ptr, "%s", col.m_name); } } void selectPk(char*& ptr) const { scopy(ptr, "select * from %s", m_name); wherePk(ptr); } void selectAll(char*& ptr) const { scopy(ptr, "select * from %s", m_name); } void selectRange(char*& ptr, bool sort) const { selectAll(ptr); whereRange(ptr); if (sort) orderPk(ptr); } void selectCount(char*& ptr) const { scopy(ptr, "select count(*) from %s", m_name); } void insertAll(char*& ptr) const { scopy(ptr, "insert into %s values (", m_name); for (unsigned i = 0; i < m_colCount; i++) { if (i > 0) scopy(ptr, ", "); scopy(ptr, "?"); } scopy(ptr, ")"); } void updatePk(char*& ptr) const { scopy(ptr, "update %s set", m_name); for (unsigned i = 0; i < m_nkCount; i++) { const Col& col = m_colList[m_nkIndex[i]]; if (i > 0) scopy(ptr, ", "); else scopy(ptr, " "); scopy(ptr, "%s = ?", col.m_name); } wherePk(ptr); } void updateRange(char*& ptr) const { scopy(ptr, "update %s set", m_name); for (unsigned i = 0; i < m_nkCount; i++) { const Col& col = m_colList[m_nkIndex[i]]; if (i > 0) scopy(ptr, ", "); else scopy(ptr, " "); scopy(ptr, "%s = ?", col.m_name); // XXX constant for now } whereRange(ptr); } void deleteAll(char*& ptr) const { scopy(ptr, "delete from %s", m_name); } void deletePk(char*& ptr) const { scopy(ptr, "delete from %s", m_name); wherePk(ptr); } void deleteRange(char*& ptr) const { scopy(ptr, "delete from %s", m_name); whereRange(ptr); } // simple void insertDirect(char*& ptr, unsigned n) const { scopy(ptr, "insert into %s values (", m_name); for (unsigned i = 0; i < m_colCount; i++) { const Col& col = m_colList[i]; if (i > 0) scopy(ptr, ", "); if (col.m_type == Col::Char || col.m_type == Col::Varchar) { scopy(ptr, "'"); for (unsigned i = 0; i <= n % col.m_length; i++) scopy(ptr, "%c", 'a' + (n + i) % 26); scopy(ptr, "'"); } else if (col.m_type == Col::Int || col.m_type == Col::Bigint) { scopy(ptr, "%u", n); } else if (col.m_type == Col::Real || col.m_type == Col::Double) { scopy(ptr, "%.3f", n * 0.001); } else { assert(false); } } scopy(ptr, ")"); } void whereDirect(char*& ptr, unsigned n) const { scopy(ptr, " where"); for (unsigned i = 0; i < m_pkCount; i++) { const Col& col = m_colList[m_pkIndex[i]]; if (i > 0) scopy(ptr, ", "); else scopy(ptr, " "); scopy(ptr, "%s = ", col.m_name); if (col.m_type == Col::Char || col.m_type == Col::Varchar) { scopy(ptr, "'"); for (unsigned i = 0; i <= n % col.m_length; i++) scopy(ptr, "%c", 'a' + (n + i) % 26); scopy(ptr, "'"); } else if (col.m_type == Col::Int || col.m_type == Col::Bigint) { scopy(ptr, "%u", n); } else { assert(false); } } } void countDirect(char*& ptr, unsigned n) const { scopy(ptr, "select count(*) from %s", m_name); whereDirect(ptr, n); } void deleteDirect(char*& ptr, unsigned n) const { scopy(ptr, "delete from %s", m_name); whereDirect(ptr, n); } // joins void selectCart(char*& ptr, unsigned cnt) const { scopy(ptr, "select count(*) from"); for (unsigned j = 0; j < cnt; j++) { if (j > 0) scopy(ptr, ","); scopy(ptr, " %s", m_name); scopy(ptr, " t%u", j); } } void selectJoin(char*& ptr, unsigned cnt) const { scopy(ptr, "select * from"); for (unsigned j = 0; j < cnt; j++) { if (j > 0) scopy(ptr, ","); scopy(ptr, " %s", m_name); scopy(ptr, " t%u", j); } for (unsigned i = 0; i < m_pkCount; i++) { const Col& col = m_colList[m_pkIndex[i]]; for (unsigned j = 0; j < cnt - 1; j++) { if (i == 0 && j == 0) scopy(ptr, " where"); else scopy(ptr, " and"); scopy(ptr, " t%u.%s = t%u.%s", j, col.m_name, j + 1, col.m_name); } } } // check if selected on command line bool optok() const { return opt.m_table == 0 || strcasecmp(m_name, opt.m_table) == 0; } }; // the test tables static Col col0[] = { Col( "a", Col::Bigint, 0, Col::Pk, Col::CLong ), Col( "b", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c", Col::Char, 4, Col::NotNull, Col::CChar ), Col( "d", Col::Double, 0, Col::Null, Col::CDouble ), Col( "e", Col::Char, 40, Col::Null, Col::CChar ), Col( "f", Col::Char, 10, Col::Null, Col::CChar ) }; static Col col1[] = { Col( "c0", Col::Int, 0, Col::Pk, Col::CLong ), Col( "c1", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c2", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c3", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c4", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c5", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c6", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c7", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c8", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c9", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c10", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c11", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c12", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c13", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c14", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c15", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c16", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c17", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c18", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c19", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c20", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c21", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c22", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c23", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c24", Col::Int, 0, Col::NotNull, Col::CLong ), Col( "c25", Col::Int, 0, Col::NotNull, Col::CLong ) }; static Col col2[] = { Col( "a", Col::Int, 0, Col::Pk, Col::CLong ), Col( "c", Col::Char, 8000, Col::NotNull, Col::CChar ) }; static Col col3[] = { Col( "a", Col::Int, 0, Col::Pk, Col::CLong ), Col( "c1", Col::Varchar, 1, Col::Null, Col::CChar ), Col( "c2", Col::Varchar, 2, Col::Null, Col::CChar ), Col( "c3", Col::Varchar, 3, Col::Null, Col::CChar ), Col( "c4", Col::Varchar, 4, Col::Null, Col::CChar ), Col( "c5", Col::Varchar, 10, Col::Null, Col::CChar ), Col( "c6", Col::Varchar, 40, Col::Null, Col::CChar ), Col( "c7", Col::Varchar, 255, Col::Null, Col::CChar ), Col( "c8", Col::Varchar, 4000, Col::Null, Col::CChar ) }; static Col col4[] = { Col( "a", Col::Char, 8, Col::Pk, Col::CChar ), Col( "b", Col::Char, 8, Col::NotNull, Col::CChar ), }; static Tab tabList[] = { #define colList(x) x, arraySize(x) Tab( "tt00", colList(col0) ), Tab( "tt01", colList(col1) ), // fläskbench special Tab( "tt02", colList(col2) ), Tab( "tt03", colList(col3) ), Tab( "tt04", colList(col4) ) #undef colList }; static const unsigned tabCount = arraySize(tabList); static const unsigned maxColCount = 100; // per table - keep up to date static bool findTable() { for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (tab.optok()) return true; } return false; } static void listTables() { ndbout << "tables:" << endl; for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (i > 0) ndbout << " "; ndbout << tab.m_name; } ndbout << endl; } // data fields and rows struct Fld { const Col& m_col; union { char* m_char; long m_long; double m_double; }; SQLINTEGER m_ind; SQLINTEGER m_need; // constant Fld() : m_col(ColUndef), m_need(0) { } Fld(const Col& col) : m_col(col), m_need(SQL_LEN_DATA_AT_EXEC(0)) { switch (m_col.m_ctype) { case Col::CChar: m_char = new char[m_col.csize()]; memset(m_char, 0, m_col.csize()); break; case Col::CLong: m_long = 0; break; case Col::CDouble: m_double = 0.0; break; } m_ind = -1; } ~Fld() { switch (m_col.m_ctype) { case Col::CChar: delete[] m_char; break; case Col::CLong: break; case Col::CDouble: break; } } void zero() { switch (m_col.m_ctype) { case Col::CChar: memset(m_char, 0x1f, m_col.csize()); break; case Col::CLong: m_long = 0x1f1f1f1f; break; case Col::CDouble: m_double = 1111111.1111111; break; } m_ind = -1; } // copy values from another field void copy(const Fld& fld) { assert(&m_col == &fld.m_col); switch (m_col.m_ctype) { case Col::CChar: memcpy(m_char, fld.m_char, m_col.csize()); break; case Col::CLong: m_long = fld.m_long; break; case Col::CDouble: m_double = fld.m_double; break; default: assert(false); break; } m_ind = fld.m_ind; } SQLPOINTER caddr() { switch (m_col.m_ctype) { case Col::CChar: return (SQLPOINTER)m_char; case Col::CLong: return (SQLPOINTER)&m_long; case Col::CDouble: return (SQLPOINTER)&m_double; } assert(false); return 0; } SQLINTEGER* ind() { return &m_ind; } SQLINTEGER* need() { m_need = SQL_LEN_DATA_AT_EXEC(0); return &m_need; } void calcPk(const Test& test, unsigned rownum) { switch (m_col.m_ctype) { case Col::CChar: test.calcPk(rownum, m_char, m_col.csize()); m_ind = SQL_NTS; return; case Col::CLong: test.calcPk(rownum, &m_long); m_ind = 0; return; case Col::CDouble: assert(false); return; } assert(false); } void hashPk(const Test& test, unsigned* hash) const { switch (m_col.m_ctype) { case Col::CChar: test.hashPk(hash, m_char, m_col.csize()); return; case Col::CLong: test.hashPk(hash, m_long); return; case Col::CDouble: assert(false); return; } assert(false); } void calcNk(const Test& test, unsigned hash) { bool null = m_col.m_cons == Col::Null; switch (m_col.m_ctype) { case Col::CChar: test.calcNk(hash, m_char, m_col.csize(), &m_ind, null); return; case Col::CLong: test.calcNk(hash, &m_long, &m_ind, null); return; case Col::CDouble: test.calcNk(hash, &m_double, &m_ind, null); return; } assert(false); } bool verify(Test& test, const Fld& fld) { assert(&m_col == &fld.m_col); switch (m_col.m_ctype) { case Col::CChar: return test.verify(m_char, m_ind, fld.m_char, fld.m_ind, m_col.csize()); case Col::CLong: return test.verify(m_long, m_ind, fld.m_long, fld.m_ind); case Col::CDouble: return test.verify(m_double, m_ind, fld.m_double, fld.m_ind); } assert(false); return false; } // debug void print() const { if (m_ind == SQL_NULL_DATA) ndbout << "NULL"; else { switch (m_col.m_ctype) { case Col::CChar: ndbout << m_char; break; case Col::CLong: ndbout << (int)m_long; break; case Col::CDouble: ndbout << m_double; break; } } } }; struct Row { const Tab& m_tab; Fld* m_fldList; Row(const Tab& tab) : m_tab(tab) { m_fldList = new Fld[m_tab.m_colCount]; for (unsigned i = 0; i < m_tab.m_colCount; i++) { const Col& col = m_tab.m_colList[i]; void* place = &m_fldList[i]; new (place) Fld(col); } } ~Row() { delete[] m_fldList; } // copy values from another row void copy(const Row& row) { assert(&m_tab == &row.m_tab); for (unsigned i = 0; i < m_tab.m_colCount; i++) { Fld& fld = m_fldList[i]; fld.copy(row.m_fldList[i]); } } // primary key value is determined by row number void calcPk(Test& test, unsigned rownum) { for (unsigned i = 0; i < m_tab.m_pkCount; i++) { Fld& fld = m_fldList[m_tab.m_pkIndex[i]]; fld.calcPk(test, rownum); } } // other fields are determined by primary key value void calcNk(Test& test) { unsigned hash = test.m_salt; for (unsigned i = 0; i < m_tab.m_pkCount; i++) { Fld& fld = m_fldList[m_tab.m_pkIndex[i]]; fld.hashPk(test, &hash); } for (unsigned i = 0; i < m_tab.m_colCount; i++) { const Col& col = m_tab.m_colList[i]; if (col.m_cons == Col::Pk) continue; Fld& fld = m_fldList[i]; fld.calcNk(test, hash); } } // verify against another row bool verifyPk(Test& test, const Row& row) const { assert(&m_tab == &row.m_tab); for (unsigned i = 0; i < m_tab.m_pkCount; i++) { Fld& fld = m_fldList[m_tab.m_pkIndex[i]]; if (! fld.verify(test, row.m_fldList[m_tab.m_pkIndex[i]])) { ndbout << "verify failed: tab=" << m_tab.m_name << " col=" << fld.m_col.m_name << endl; return false; } } return true; } bool verifyNk(Test& test, const Row& row) const { assert(&m_tab == &row.m_tab); for (unsigned i = 0; i < m_tab.m_nkCount; i++) { Fld& fld = m_fldList[m_tab.m_nkIndex[i]]; if (! fld.verify(test, row.m_fldList[m_tab.m_nkIndex[i]])) { ndbout << "verify failed: tab=" << m_tab.m_name << " col=" << fld.m_col.m_name << endl; return false; } } return true; } bool verify(Test& test, const Row& row) const { return verifyPk(test, row) && verifyNk(test, row); } // debug void print() const { ndbout << "row"; for (unsigned i = 0; i < m_tab.m_colCount; i++) { ndbout << " " << i << "="; Fld& fld = m_fldList[i]; fld.print(); } ndbout << endl; } }; // set ODBC version - required static void setVersion(Test& test, SQLHANDLE hEnv) { test.run(HEnv(hEnv), SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)); } // set autocommit static void setAutocommit(Test& test, SQLHANDLE hDbc, bool on) { SQLUINTEGER value = on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)value, SQL_IS_UINTEGER)); SQLUINTEGER value2 = (SQLUINTEGER)-1; test.run(HDbc(hDbc), SQLGetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&value2, SQL_IS_UINTEGER, 0)); test.chk(HDbc(hDbc), value2 == value, "got %u != %u", (unsigned)value2, (unsigned)value); } // subroutines - migrate simple common routines here static void allocEnv(Test& test, SQLHANDLE& hEnv) { test.run(HNull, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv)); setVersion(test, hEnv); } static void allocDbc(Test& test, SQLHANDLE hEnv, SQLHANDLE& hDbc) { test.run(HEnv(hEnv), SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc)); } static void allocConn(Test& test, SQLHANDLE hEnv, SQLHANDLE& hDbc) { allocDbc(test, hEnv, hDbc); #ifdef unixODBC test.exp(SQL_SUCCESS_WITH_INFO, "IM003", 0, false); // unicode?? test.exp(SQL_SUCCESS_WITH_INFO, "01000", 0, false); // version?? #endif test.run(HDbc(hDbc), SQLConnect(hDbc, (SQLCHAR*)opt.m_dsn, SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS)); } static void allocStmt(Test& test, SQLHANDLE hDbc, SQLHANDLE& hStmt) { test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt)); } static void allocAll(Test& test, SQLHANDLE& hEnv, SQLHANDLE& hDbc, SQLHANDLE& hStmt) { allocEnv(test, hEnv); allocConn(test, hEnv, hDbc); allocStmt(test, hDbc, hStmt); } static void allocAll(Test& test, SQLHANDLE& hEnv, SQLHANDLE& hDbc, SQLHANDLE* hStmtList, unsigned nStmt) { allocEnv(test, hEnv); allocConn(test, hEnv, hDbc); for (unsigned i = 0; i < nStmt; i++) allocStmt(test, hDbc, hStmtList[i]); } static void freeEnv(Test& test, SQLHANDLE hEnv) { test.run(HNull, SQLFreeHandle(SQL_HANDLE_ENV, hEnv)); } static void freeDbc(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc) { test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc)); } static void freeConn(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc) { test.run(HDbc(hDbc), SQLDisconnect(hDbc)); test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc)); } static void freeStmt(Test& test, SQLHANDLE hDbc, SQLHANDLE hStmt) { test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_STMT, hStmt)); } static void freeAll(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc, SQLHANDLE hStmt) { freeStmt(test, hDbc, hStmt); freeConn(test, hEnv, hDbc); freeEnv(test, hEnv); } static void freeAll(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc, SQLHANDLE* hStmtList, unsigned nStmt) { for (unsigned i = 0; i < nStmt; i++) freeStmt(test, hDbc, hStmtList[i]); freeConn(test, hEnv, hDbc); freeEnv(test, hEnv); } #define chkTuplesFetched(/*Test&*/ _test, /*SQLHANDLE*/ _hStmt, /*SQLUINTEGER*/ _countExp) \ do { \ SQLUINTEGER _count = (SQLUINTEGER)-1; \ getTuplesFetched(_test, _hStmt, &_count); \ test.chk(HStmt(_hStmt), _count == _countExp, "tuples: got %ld != %ld", (long)_count, (long)_countExp); \ } while (0) static void getTuplesFetched(Test& test, SQLHANDLE hStmt, SQLUINTEGER* count) { *count = (SQLUINTEGER)-1; test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_NDB_TUPLES_FETCHED, count, SQL_IS_POINTER, 0)); } static void selectCount(Test& test, SQLHANDLE hStmt, const char* sql, long* count) { if (opt.m_v >= 3) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); SQLINTEGER ind; test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, count, 0, &ind)); ind = -1; *count = -1; test.run(HStmt(hStmt), SQLExecute(hStmt)); unsigned k = 0; while (1) { if (k == 1) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (k == 1) break; k++; } test.chk(HStmt(hStmt), ind == sizeof(long), "got %d != %d", (int)ind, (int)sizeof(long)); test.chk(HStmt(hStmt), *count >= 0, "got %ld < 0", *count); chkTuplesFetched(test, hStmt, *count); #ifndef iODBC // test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); #else test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_CLOSE)); #endif } static void selectCount(Test& test, SQLHANDLE hStmt, const Tab& tab, long* count) { static char sql[MAX_SQL], *sqlptr; // XXX static or core tab.selectCount(sqlptr = sql); selectCount(test, hStmt, sql, count); } static void verifyCount(Test& test, SQLHANDLE hStmt, const Tab& tab, long countExp) { long count = -1; selectCount(test, hStmt, tab, &count); test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp); } #define chkRowCount(/*Test&*/ _test, /*SQLHANDLE*/ _hStmt, /*SQLINTEGER*/ _countExp) \ do { \ SQLINTEGER _count = -1; \ getRowCount(_test, _hStmt, &_count); \ test.chk(HStmt(_hStmt), _count == _countExp, "rowcount: got %ld != %ld", (long)_count, (long)_countExp); \ } while (0) static void getRowCount(Test& test, SQLHANDLE hStmt, SQLINTEGER* count) { *count = -1; test.run(HStmt(hStmt), SQLRowCount(hStmt, count)); } // handle allocation static void testAlloc(Test& test) { const unsigned n1 = (opt.m_scale >> 8) & 0xf; // default 500 = 0x1f4 const unsigned n2 = (opt.m_scale >> 4) & 0xf; const unsigned n3 = (opt.m_scale >> 0) & 0xf; const unsigned count = n1 + n1 * n2 + n1 * n2 * n3; SQLHANDLE hEnvList[0xf]; SQLHANDLE hDbcList[0xf][0xf]; SQLHANDLE hStmtList[0xf][0xf][0xf]; for (unsigned i1 = 0; i1 < n1; i1++) { SQLHANDLE& hEnv = hEnvList[i1]; test.run(HNull, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv)); test.run(HEnv(hEnv), SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)); for (unsigned i2 = 0; i2 < n2; i2++) { SQLHANDLE& hDbc = hDbcList[i1][i2]; test.run(HEnv(hEnv), SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc)); #ifdef unixODBC test.exp(SQL_SUCCESS_WITH_INFO, "IM003", 0, false); // unicode?? test.exp(SQL_SUCCESS_WITH_INFO, "01000", 0, false); // version?? #endif test.run(HDbc(hDbc), SQLConnect(hDbc, (SQLCHAR*)opt.m_dsn, SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS)); // some attributes test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_AUTO_IPD, (SQLPOINTER)SQL_TRUE, SQL_IS_UINTEGER)); test.exp(SQL_ERROR, "HYC00", -1, true); // not supported test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)SQL_TXN_SERIALIZABLE, SQL_IS_UINTEGER)); test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)"DEFAULT", strlen("DEFAULT"))); for (unsigned i3 = 0; i3 < n3; i3++) { SQLHANDLE& hStmt = hStmtList[i1][i2][i3]; test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt)); SQLHANDLE ipd0, ipd1; SQLHANDLE ird0, ird1; SQLHANDLE apd0, apd1, apd2; SQLHANDLE ard0, ard1, ard2; // get ipd0 = ird0 = apd0 = ard0 = 0; test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, &ipd0, SQL_IS_POINTER, 0)); test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, &ird0, SQL_IS_POINTER, 0)); test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &apd0, SQL_IS_POINTER, 0)); test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, &ard0, SQL_IS_POINTER, 0)); #ifndef unixODBC test.chk(HStmt(hStmt), ipd0 != 0, "got 0"); test.chk(HStmt(hStmt), ird0 != 0, "got 0"); test.chk(HStmt(hStmt), apd0 != 0, "got 0"); test.chk(HStmt(hStmt), ard0 != 0, "got 0"); #endif // alloc ipd1 = ird1 = apd1 = ard1 = 0; test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ipd1)); test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ird1)); test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &apd1)); test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ard1)); test.chk(HDbc(hDbc), ipd1 != 0 && ird1 != 0 && apd1 != 0 && ard1 != 0, "got null"); // set test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, ipd1, SQL_IS_POINTER)); test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, ird1, SQL_IS_POINTER)); test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, apd1, SQL_IS_POINTER)); test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, ard1, SQL_IS_POINTER)); // get apd2 = ard2 = 0; test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &apd2, SQL_IS_POINTER, 0)); test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, &ard2, SQL_IS_POINTER, 0)); test.chk(HStmt(hStmt), apd2 == apd1, "got %x != %x", (unsigned)apd2, (unsigned)apd1); test.chk(HStmt(hStmt), ard2 == ard1, "got %x != %x", (unsigned)ard2, (unsigned)ard1); // free test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ipd1)); test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ird1)); test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, apd1)); test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ard1)); } } } test.timerCnt(count); if (opt.m_v >= 3) ndbout << "allocated " << count << endl; for (unsigned i1 = 0; i1 < n1; i1++) { SQLHANDLE& hEnv = hEnvList[i1]; for (unsigned i2 = 0; i2 < n2; i2++) { SQLHANDLE& hDbc = hDbcList[i1][i2]; if (i2 % 2 == 0) { for (unsigned i3 = 0; i3 < n3; i3++) { SQLHANDLE& hStmt = hStmtList[i1][i2][i3]; test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_STMT, hStmt)); } } else { // cleaned up by SQLDisconnect } test.run(HDbc(hDbc), SQLDisconnect(hDbc)); test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc)); } test.run(HNull, SQLFreeHandle(SQL_HANDLE_ENV, hEnv)); } test.timerCnt(count); if (opt.m_v >= 3) ndbout << "freed " << count << endl; } // create tables static void testCreate(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { Tab& tab = tabList[i]; if (! tab.optok()) continue; // drop tab.drop(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); test.exp(SQL_ERROR, "IM000", 2040709, false); test.run(HStmt(hStmt), SQLExecute(hStmt)); if (test.m_ret == SQL_SUCCESS) test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DROP_TABLE, "got %d != %d", test.m_functionCode, SQL_DIAG_DROP_TABLE); if (test.m_ret == SQL_SUCCESS && opt.m_v >= 2) ndbout << "table " << tab.m_name << " dropped" << endl; if (test.m_ret != SQL_SUCCESS && opt.m_v >= 2) ndbout << "table " << tab.m_name << " does not exist" << endl; test.timerCnt(1); // create tab.create(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); test.exp(SQL_ERROR, "IM000", 2040721, false); test.run(HStmt(hStmt), SQLExecute(hStmt)); if (test.m_ret == SQL_SUCCESS) test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_CREATE_TABLE, "got %d != %d", test.m_functionCode, SQL_DIAG_CREATE_TABLE); if (test.m_ret == SQL_SUCCESS && opt.m_v >= 2) ndbout << "table " << tab.m_name << " created" << endl; if (test.m_ret != SQL_SUCCESS && opt.m_v >= 2) ndbout << "table " << tab.m_name << " already exists" << endl; test.timerCnt(1); } freeAll(test, hEnv, hDbc, hStmt); } // prepare without execute static void testPrepare(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); char sql[MAX_SQL], *sqlptr; for (unsigned cnt = opt.m_depth; cnt <= opt.m_depth; cnt++) { for (unsigned i = 0; i < tabCount; i++) { Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.selectJoin(sqlptr = sql, cnt); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); SQLSMALLINT colCount = -1; SQLSMALLINT colExp = cnt * tab.m_colCount; test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount)); test.chk(HStmt(hStmt), colCount == colExp, "got %d != %d", (int)colCount, (int)colExp); test.timerCnt(1); } } freeAll(test, hEnv, hDbc, hStmt); } // catalog functions static void testCatalog(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); odbc_typeinfo: { long type[] = { SQL_CHAR, SQL_VARCHAR, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_REAL, SQL_DOUBLE }; unsigned rows[] = { 1, 1, 2, 2, 2, 1, 1 // 2 for signed and unsigned }; for (unsigned i = 0; i < arraySize(type); i++) { test.run(HStmt(hStmt), SQLGetTypeInfo(hStmt, type[i])); long dataType = 0; test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SLONG, &dataType, 0, 0)); unsigned k = 0; while (1) { if (k == rows[i]) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (k == rows[i]) break; test.chk(HStmt(hStmt), dataType == type[i], "got %ld != %ld", dataType, type[i]); test.timerCnt(1); k++; } #ifndef iODBC test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); #else freeStmt(test, hDbc, hStmt); allocStmt(test, hDbc, hStmt); #endif } if (opt.m_v >= 2) ndbout << "found " << (UintPtr)arraySize(type) << " data types" << endl; test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND)); } odbc_tables: { unsigned found[tabCount]; for (unsigned i = 0; i < tabCount; i++) found[i] = 0; test.run(HStmt(hStmt), SQLTables(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0)); char tableName[200] = ""; char tableType[200] = ""; test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, tableType, sizeof(tableType), 0)); unsigned cnt = 0; while (1) { test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (test.m_ret == SQL_NO_DATA) break; test.timerCnt(1); cnt++; if (! blankeq(tableType, "TABLE")) continue; for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (! tab.optok()) continue; if (! blankeq(tab.m_name, tableName)) continue; test.chk(HStmt(hStmt), found[i] == 0, "duplicate table %s", tab.m_name); found[i]++; } } #ifndef iODBC test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); #else freeStmt(test, hDbc, hStmt); allocStmt(test, hDbc, hStmt); #endif for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (! tab.optok()) continue; test.chk(HStmt(hStmt), found[i] == 1, "table %s not found", tab.m_name); } if (opt.m_v >= 2) ndbout << "found " << cnt << " tables" << endl; test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND)); } odbc_columns: { unsigned found[tabCount][maxColCount]; for (unsigned i = 0; i < tabCount; i++) { for (unsigned j = 0; j < maxColCount; j++) found[i][j] = 0; } test.run(HStmt(hStmt), SQLColumns(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0)); char tableName[200] = ""; char columnName[200] = ""; long dataType = 0; test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, columnName, sizeof(columnName), 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 5, SQL_C_SLONG, &dataType, 0, 0)); unsigned cnt = 0; while (1) { test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (test.m_ret == SQL_NO_DATA) break; test.timerCnt(1); cnt++; for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (! tab.optok()) continue; if (! blankeq(tab.m_name, tableName)) continue; bool columnFound = false; for (unsigned j = 0; j < tab.m_colCount; j++) { const Col& col = tab.m_colList[j]; if (! blankeq(col.m_name, columnName)) continue; test.chk(HStmt(hStmt), found[i][j] == 0, "duplicate column %s.%s", tableName, columnName); found[i][j]++; columnFound = true; } test.chk(HStmt(hStmt), columnFound, "unknown column %s.%s", tableName, columnName); } } #ifndef iODBC test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); #else freeStmt(test, hDbc, hStmt); allocStmt(test, hDbc, hStmt); #endif for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (! tab.optok()) continue; for (unsigned j = 0; j < tab.m_colCount; j++) { const Col& col = tab.m_colList[j]; test.chk(HStmt(hStmt), found[i][j] == 1, "column %s.%s not found", tab.m_name, col.m_name); } } if (opt.m_v >= 2) ndbout << "found " << cnt << " columns" << endl; test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND)); } odbc_primarykeys: { // table patterns are no allowed for (unsigned i = 0; i < tabCount; i++) { const Tab& tab = tabList[i]; if (! tab.optok()) continue; char tmp[200]; // p.i.t.a strcpy(tmp, tab.m_name); for (char* a = tmp; *a != 0; a++) { if ('a' <= *a && *a <= 'z') *a -= 'a' - 'A'; } test.run(HStmt(hStmt), SQLPrimaryKeys(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)tmp, SQL_NTS)); char tableName[200] = ""; char columnName[200] = ""; long keySeq = -1; test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, columnName, sizeof(columnName), 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 5, SQL_C_SLONG, &keySeq, 0, 0)); unsigned cnt = 0; while (1) { if (cnt == tab.m_pkCount) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (test.m_ret == SQL_NO_DATA) break; test.chk(HStmt(hStmt), keySeq == 1 + cnt, "got %ld != %u", keySeq, 1 + cnt); const Col& col = tab.m_colList[tab.m_pkIndex[keySeq - 1]]; test.chk(HStmt(hStmt), blankeq(columnName, col.m_name), "got %s != %s", columnName, col.m_name); test.timerCnt(1); cnt++; } #ifndef iODBC test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); #else freeStmt(test, hDbc, hStmt); allocStmt(test, hDbc, hStmt); #endif } test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND)); } freeAll(test, hEnv, hDbc, hStmt); } // insert static void testInsert(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // prepare tab.insertAll(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); SQLSMALLINT parCount = -1; test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount)); test.chk(HStmt(hStmt), parCount == tab.m_colCount, "got %d != %d", (int)parCount, (int)tab.m_colCount); // bind parameters Row row(tab); for (unsigned j = 0; j < tab.m_colCount; j++) { Fld& fld = row.m_fldList[j]; const Col& col = fld.m_col; // every other at-exec SQLPOINTER caddr; SQLINTEGER* ind; if (opt.m_noputd || j % 2 == 0) { caddr = fld.caddr(); ind = fld.ind(); } else { caddr = (SQLPOINTER)j; ind = fld.need(); } test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, caddr, col.csize(), ind)); } // bind columns (none) SQLSMALLINT colCount = -1; test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount)); test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount); // execute for (unsigned k = 0; k < opt.m_scale; k++) { if (k % 5 == 0) { // rebind unsigned j = 0; Fld& fld = row.m_fldList[j]; const Col& col = fld.m_col; // every other at-exec SQLPOINTER caddr; SQLINTEGER* ind; if (opt.m_noputd || j % 2 == 0) { caddr = fld.caddr(); ind = fld.ind(); } else { caddr = (SQLPOINTER)j; ind = fld.need(); } test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, caddr, col.csize(), ind)); } row.calcPk(test, k); row.calcNk(test); unsigned needData = opt.m_noputd ? 0 : tab.m_colCount / 2; if (needData) test.exp(SQL_NEED_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_INSERT, "got %d != %d", test.m_functionCode, SQL_DIAG_INSERT); if (needData) { while (1) { SQLPOINTER jPtr = (SQLPOINTER)999; if (needData) test.exp(SQL_NEED_DATA, 0, 0, true); // completes SQLExecute on success test.run(HStmt(hStmt), SQLParamData(hStmt, &jPtr)); if (! needData) break; unsigned j = (unsigned)jPtr; test.chk(HStmt(hStmt), j < tab.m_colCount && j % 2 != 0, "got %u 0x%x", j, j); Fld& fld = row.m_fldList[j]; const Col& col = fld.m_col; SQLSMALLINT ctype = col.ctype(); if (k % 2 == 0 || ctype != Col::CChar) test.run(HStmt(hStmt), SQLPutData(hStmt, fld.caddr(), *fld.ind())); else { // put in pieces unsigned size = col.csize() - 1; // omit null terminator char* caddr = (char*)(fld.caddr()); unsigned off = 0; while (off < size) { unsigned m = size / 7; // bytes to put if (m == 0) m = 1; if (m > size - off) m = size - off; bool putNull = (*fld.ind() == SQL_NULL_DATA); // no null terminator SQLINTEGER len = putNull ? SQL_NULL_DATA : (int)m; test.run(HStmt(hStmt), SQLPutData(hStmt, caddr + off, len)); if (putNull) break; off += m; } } needData--; } } chkRowCount(test, hStmt, 1); chkTuplesFetched(test, hStmt, 0); } test.timerCnt(opt.m_scale); if (opt.m_v >= 3) ndbout << "inserted " << opt.m_scale << " into " << tab.m_name << endl; } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } // count static void testCount(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; long count = -1; selectCount(test, hStmt, tab, &count); test.chk(HStmt(hStmt), count == opt.m_scale * opt.m_threads, "got %ld != %u", count, opt.m_scale * opt.m_threads); test.timerCnt(count); if (opt.m_v >= 3) ndbout << "counted " << (int)count << " rows in " << tab.m_name << endl; } // scan all at same time char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.selectAll(sqlptr = sql); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } unsigned k = 0; while (1) { for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; if (k == opt.m_scale * opt.m_threads) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (k != opt.m_scale * opt.m_threads) { chkTuplesFetched(test, hStmt, k + 1); test.timerCnt(1); } else { chkTuplesFetched(test, hStmt, k); test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLMoreResults(hStmt)); } } if (k == opt.m_scale * opt.m_threads) break; k++; } if (opt.m_v >= 3) ndbout << "scanned " << opt.m_scale << " rows from each table" << endl; freeAll(test, hEnv, hDbc, hStmtList, tabCount); } // update static void testUpdatePk(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // prepare tab.updatePk(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); // bind parameters Row row(tab); SQLSMALLINT parCount = -1; test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount)); test.chk(HStmt(hStmt), parCount == tab.m_colCount, "got %d != %d", (int)parCount, (int)tab.m_colCount); for (unsigned j = 0; j < tab.m_nkCount; j++) { Fld& fld = row.m_fldList[tab.m_nkIndex[j]]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } for (unsigned j = 0; j < tab.m_pkCount; j++) { Fld& fld = row.m_fldList[tab.m_pkIndex[j]]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + tab.m_nkCount + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } // bind columns (none) SQLSMALLINT colCount = -1; test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount)); test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount); // execute for (unsigned k = 0; k < opt.m_scale; k++) { if (k % 5 == 0) { unsigned j = 0; Fld& fld = row.m_fldList[tab.m_nkIndex[j]]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } row.calcPk(test, k); row.calcNk(test); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_UPDATE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_UPDATE_WHERE); chkRowCount(test, hStmt, 1); // direct update, no read has been necessary chkTuplesFetched(test, hStmt, 0); } test.timerCnt(opt.m_scale); if (opt.m_v >= 3) ndbout << "updated " << opt.m_scale << " in " << tab.m_name << endl; } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } static void testUpdateScan(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // prepare tab.updateRange(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); // bind parameters Row row(tab); // for set clause Row rowlo(tab); // for pk ranges Row rowhi(tab); SQLSMALLINT parCount = -1; test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount)); test.chk(HStmt(hStmt), parCount == tab.m_nkCount + 2 * tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_nkCount + 2 * (int)tab.m_pkCount); for (unsigned j = 0; j < tab.m_nkCount; j++) { const Col& col = tab.m_colList[tab.m_nkIndex[j]]; Fld& fld = row.m_fldList[tab.m_nkIndex[j]]; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } bool canInterp = true; for (unsigned j = 0; j < tab.m_pkCount; j++) { const Col& col = tab.m_colList[tab.m_pkIndex[j]]; Fld& fldlo = rowlo.m_fldList[tab.m_pkIndex[j]]; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + tab.m_nkCount + 2 * j + 0, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldlo.caddr(), col.csize(), fldlo.ind())); Fld& fldhi = rowhi.m_fldList[tab.m_pkIndex[j]]; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + tab.m_nkCount + 2 * j + 1, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldhi.caddr(), col.csize(), fldhi.ind())); if (col.m_type != Col::Char) canInterp = false; // XXX no unsigned yet } // execute row.calcPk(test, 0); row.calcNk(test); rowlo.calcPk(test, 0); rowhi.calcPk(test, test.m_mul); // sucks test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_UPDATE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_UPDATE_WHERE); chkRowCount(test, hStmt, opt.m_scale); chkTuplesFetched(test, hStmt, canInterp ? opt.m_scale : opt.m_scale * opt.m_threads); test.timerCnt(opt.m_scale); if (opt.m_v >= 3) ndbout << "updated " << opt.m_scale << " in " << tab.m_name << endl; } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } // verify static void testVerifyPk(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // prepare tab.selectPk(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); // use same row for input and output Row row(tab); // bind parameters SQLSMALLINT parCount = -1; test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount)); test.chk(HStmt(hStmt), parCount == tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_pkCount); for (unsigned j = 0; j < tab.m_pkCount; j++) { Fld& fld = row.m_fldList[tab.m_pkIndex[j]]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } // bind columns SQLSMALLINT colCount = -1; test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount)); test.chk(HStmt(hStmt), colCount == tab.m_colCount, "got %d != %d", (int)colCount, (int)tab.m_colCount); for (unsigned j = 0; j < tab.m_colCount; j++) { Fld& fld = row.m_fldList[j]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, col.ctype(), fld.caddr(), col.csize(), fld.ind())); } // row for SQLGetData Row rowGet(tab); // reference row Row rowRef(tab); // execute for (unsigned k = 0; k < opt.m_scale; k++) { if (k % 5 == 0) { // rebind unsigned j = 0; Fld& fld = row.m_fldList[tab.m_pkIndex[j]]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } row.calcPk(test, k); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_SELECT_CURSOR, "got %d != %d", test.m_functionCode, SQL_DIAG_SELECT_CURSOR); // fetch for (unsigned k2 = 0; ; k2++) { if (k2 == 1) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); chkTuplesFetched(test, hStmt, 1); if (k2 == 1) break; rowRef.calcPk(test, k); test.chk(HStmt(hStmt), row.verifyPk(test, rowRef), "verify row=%d", k); if (test.m_const) rowRef.calcPk(test, 0); rowRef.calcNk(test); test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k); // SQLGetData is supported independent of SQLBindCol if (opt.m_nogetd) continue; for (unsigned j = 0; j < tab.m_colCount; j++) { Fld& fld = rowGet.m_fldList[j]; fld.zero(); const Col& col = fld.m_col; // test both variants SQLSMALLINT ctype = k % 2 == 0 ? col.ctype() : SQL_ARD_TYPE; if (ctype != Col::CChar) test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, fld.caddr(), col.csize(), fld.ind())); else { // get in pieces unsigned size = col.csize() - 1; // omit null terminator char* caddr = (char*)(fld.caddr()); unsigned off = 0; while (off < size) { unsigned m = size / 3; // bytes to get if (m == 0) m = 1; if (m > size - off) m = size - off; bool getNull = (rowRef.m_fldList[j].m_ind == SQL_NULL_DATA); if (off + m < size && ! getNull) test.exp(SQL_SUCCESS_WITH_INFO, "01004", -1, true); // include null terminator in buffer size test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, caddr + off, m + 1, fld.ind())); int ind = *fld.ind(); if (getNull) { test.chk(HStmt(hStmt), ind == SQL_NULL_DATA, "got %d", ind); break; } test.chk(HStmt(hStmt), ind == size - off, "got %d != %u", ind, size - off); off += m; } } } rowRef.calcPk(test, k); test.chk(HStmt(hStmt), rowGet.verifyPk(test, rowRef), "verify row=%d", k); if (test.m_const) rowRef.calcPk(test, 0); rowRef.calcNk(test); test.chk(HStmt(hStmt), rowGet.verifyNk(test, rowRef), "verify row=%d", k); // SQLGetData again for (unsigned j = 0; j < tab.m_colCount; j++) { Fld& fld = rowGet.m_fldList[j]; const Col& col = fld.m_col; // test both variants SQLSMALLINT ctype = k % 2 == 0 ? col.ctype() : SQL_ARD_TYPE; // expect no more data test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, fld.caddr(), col.csize(), fld.ind())); } } test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } test.timerCnt(opt.m_scale); if (opt.m_v >= 3) ndbout << "verified " << opt.m_scale << " from " << tab.m_name << endl; } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } static void testVerifyScan(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // prepare tab.selectRange(sqlptr = sql, ! opt.m_nosort); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); // bind parameters Row rowlo(tab); // use available PK fields.. Row rowhi(tab); // since we have no other way for now SQLSMALLINT parCount = -1; test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount)); test.chk(HStmt(hStmt), parCount == 2 * tab.m_pkCount, "got %d != %d", (int)parCount, 2 * (int)tab.m_pkCount); for (unsigned j = 0; j < tab.m_pkCount; j++) { const Col& col = tab.m_colList[tab.m_pkIndex[j]]; Fld& fldlo = rowlo.m_fldList[tab.m_pkIndex[j]]; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + 2 * j + 0, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldlo.caddr(), col.csize(), fldlo.ind())); Fld& fldhi = rowhi.m_fldList[tab.m_pkIndex[j]]; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + 2 * j + 1, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldhi.caddr(), col.csize(), fldhi.ind())); } // bind columns Row row(tab); SQLSMALLINT colCount = -1; test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount)); test.chk(HStmt(hStmt), colCount == tab.m_colCount, "got %d != %d", (int)colCount, (int)tab.m_colCount); for (unsigned j = 0; j < tab.m_colCount; j++) { Fld& fld = row.m_fldList[j]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, col.ctype(), fld.caddr(), col.csize(), fld.ind())); } // execute rowlo.calcPk(test, 0); rowhi.calcPk(test, test.m_mul); // sucks test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_SELECT_CURSOR, "got %d != %d", test.m_functionCode, SQL_DIAG_SELECT_CURSOR); // reference row Row rowRef(tab); // fetch unsigned k = 0; SQLUINTEGER rowCount1 = (SQLUINTEGER)-1; test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowCount1, SQL_IS_POINTER)); while (1) { unsigned countExp; if (k == opt.m_scale) { countExp = k; test.exp(SQL_NO_DATA, 0, 0, true); } else { countExp = k + 1; } test.run(HStmt(hStmt), SQLFetch(hStmt)); // let me count the ways.. chkRowCount(test, hStmt, countExp); test.chk(HStmt(hStmt), rowCount1 == countExp, "got %lu != %u", rowCount1, countExp); SQLUINTEGER rowCount2 = (SQLUINTEGER)-1; test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_ROW_NUMBER, &rowCount2, SQL_IS_POINTER, 0)); test.chk(HStmt(hStmt), rowCount2 == countExp, "got %lu != %u", rowCount2, countExp); if (k == opt.m_scale) break; if (! opt.m_nosort) { // expecting k-th row rowRef.calcPk(test, k); test.chk(HStmt(hStmt), row.verifyPk(test, rowRef), "verify row=%d", k); if (test.m_const) rowRef.calcPk(test, 0); rowRef.calcNk(test); test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k); } else { // expecting random row rowRef.copy(row); test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k); } k++; } test.timerCnt(opt.m_scale); if (opt.m_v >= 3) ndbout << "verified " << opt.m_scale << " from " << tab.m_name << endl; } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } // self-join (scan followed by pk lookups) static void testJoin(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned cnt = opt.m_depth; cnt <= opt.m_depth; cnt++) { for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.selectJoin(sqlptr = sql, cnt); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } unsigned k = 0; while (1) { for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; if (k == opt.m_scale * opt.m_threads) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (k == opt.m_scale * opt.m_threads) { chkTuplesFetched(test, hStmt, k * opt.m_depth); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } else { chkTuplesFetched(test, hStmt, (k + 1) * opt.m_depth); test.timerCnt(1); } } if (k == opt.m_scale * opt.m_threads) break; k++; } } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } // cartesian join (multiple nested scans) static void testCart(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned cnt = 2; cnt <= 2; cnt++) { unsigned rows = 1; //for (unsigned k = 0; k < opt.m_depth; k++) { //rows *= opt.m_scale * opt.m_threads; //} for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.selectCart(sqlptr = sql, cnt); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } unsigned k = 0; while (1) { for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; if (k == rows) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (k == rows) { //chkTuplesFetched(test, hStmt, k); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } else { //chkTuplesFetched(test, hStmt, k + 1); test.timerCnt(1); } } if (k == rows) break; k++; } } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } // delete static void testDeleteAll(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; Tab& tab = tabList[i]; if (! tab.optok()) continue; long count0 = -1; selectCount(test, hStmt, tab, &count0); tab.deleteAll(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); if (count0 == 0) test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLExecute(hStmt)); #ifndef iODBC test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DELETE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_DELETE_WHERE); #endif SQLINTEGER rowCount = -1; getRowCount(test, hStmt, &rowCount); test.timerCnt(rowCount); test.chk(HStmt(hStmt), rowCount == count0, "got %d != %ld", (int)rowCount, count0); chkTuplesFetched(test, hStmt, rowCount); if (opt.m_v >= 3) ndbout << "deleted " << (int)rowCount << " from " << tab.m_name << endl; long count = -1; selectCount(test, hStmt, tab, &count); test.chk(HStmt(hStmt), count == 0, "got %ld != 0", count); } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } static void testDeletePk(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // prepare tab.deletePk(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); // bind parameters Row row(tab); SQLSMALLINT parCount = -1; test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount)); test.chk(HStmt(hStmt), parCount == tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_colCount); for (unsigned j = 0; j < tab.m_pkCount; j++) { Fld& fld = row.m_fldList[tab.m_pkIndex[j]]; const Col& col = fld.m_col; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind())); } // bind columns (none) SQLSMALLINT colCount = -1; test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount)); test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount); // execute for (unsigned k = 0; k < opt.m_scale; k++) { row.calcPk(test, k); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DELETE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_DELETE_WHERE); chkRowCount(test, hStmt, 1); // direct delete, no fetch required chkTuplesFetched(test, hStmt, 0); } test.timerCnt(opt.m_scale); if (opt.m_v >= 3) ndbout << "updated " << opt.m_scale << " in " << tab.m_name << endl; } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } static void testTrans(Test& test) { #ifdef unixODBC if (opt.m_v >= 1) ndbout << "unixODBC does not support transactions - test skipped" << endl; #else SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; // delete all for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.deleteAll(sqlptr = sql); test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); SQLINTEGER rowCount = -1; getRowCount(test, hStmt, &rowCount); if (opt.m_v >= 3) ndbout << "deleted " << (int)rowCount << " from " << tab.m_name << endl; } setAutocommit(test, hDbc, false); if (opt.m_v >= 2) ndbout << "set autocommit OFF" << endl; for (int commit = 0; commit < opt.m_scale; commit += 1) { bool rollback = (commit % 2 == 0); // XXX delete with no data leaves trans in error state for 2nd table if (commit > 0 && rollback) { // previous case was commit for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.deleteDirect(sqlptr = sql, 0); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT)); } // insert for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.insertDirect(sqlptr = sql, 0); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); if (opt.m_v >= 2) ndbout << tab.m_name << ": inserted 1 row" << endl; } // count them via pk for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.countDirect(sqlptr = sql, 0); long count = -1; long countExp = 1; selectCount(test, hStmt, sql, &count); test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp); } // count them via scan for (unsigned i = 0; i < tabCount; i++) { // XXX hupp no work break; SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; long count = -1; long countExp = 1; selectCount(test, hStmt, tab, &count); test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp); } // rollback or commit if (rollback) { if (opt.m_v >= 2) ndbout << "end trans ROLLBACK" << endl; test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_ROLLBACK)); } else { if (opt.m_v >= 2) ndbout << "end trans COMMIT" << endl; test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT)); } // count them via pk again for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; tab.countDirect(sqlptr = sql, 0); long count = -1; long countExp = rollback ? 0 : 1; selectCount(test, hStmt, sql, &count); test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp); } // count them via scan again for (unsigned i = 0; i < tabCount; i++) { // XXX hupp no work break; SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; long count = -1; long countExp = rollback ? 0 : 1; selectCount(test, hStmt, tab, &count); test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp); } } freeAll(test, hEnv, hDbc, hStmtList, tabCount); #endif } static void testConcur(Test& test) { SQLHANDLE hEnv, hDbc, hStmtList[tabCount]; allocAll(test, hEnv, hDbc, hStmtList, tabCount); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { SQLHANDLE& hStmt = hStmtList[i]; const Tab& tab = tabList[i]; if (! tab.optok()) continue; // delete all tab.deleteAll(sqlptr = sql); test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // insert some unsigned rowcount = 10; for (unsigned n = 0; n < rowcount; n++) { tab.insertDirect(sqlptr = sql, n); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } verifyCount(test, hStmt, tab, rowcount); // start query scan followed by pk lookups tab.selectJoin(sqlptr = sql, 2); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // start fetch unsigned k = 0; while (1) { if (k > 0) test.exp(SQL_ERROR, "24000", -1, true); // commit closed cursor test.run(HStmt(hStmt), SQLFetch(hStmt)); if (k > 0) break; // delete some random row tab.deleteDirect(sqlptr = sql, k); // try using same statement test.exp(SQL_ERROR, "24000", -1, true); // cursor is open test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // try using different statement SQLHANDLE hStmt2; allocStmt(test, hDbc, hStmt2); test.run(HStmt(hStmt2), SQLExecDirect(hStmt2, (SQLCHAR*)sql, SQL_NTS)); k++; } test.exp(SQL_ERROR, "24000", -1, true); // cursor is not open test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); test.timerCnt(rowcount); } freeAll(test, hEnv, hDbc, hStmtList, tabCount); } static void testReadcom(Test& test) { testDeleteAll(test); testInsert(test); const unsigned nc = 3; SQLHANDLE hEnv[nc], hDbc[nc], hStmt[nc]; char sql[MAX_SQL], *sqlptr; for (unsigned j = 0; j < nc; j++) allocAll(test, hEnv[j], hDbc[j], hStmt[j]); for (unsigned i = 0; i < tabCount; i++) { Tab& tab = tabList[i]; if (! tab.optok()) continue; long count; // check count count = -1; selectCount(test, hStmt[0], tab, &count); test.chk(HStmt(hStmt[0]), count == opt.m_scale, "got %d != %d", (int)count, (int)opt.m_scale); // scan delete uncommitted with handle 0 setAutocommit(test, hDbc[0], false); tab.deleteAll(sqlptr = sql); if (opt.m_scale == 0) test.exp(SQL_NO_DATA, 0, 0, false); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt[0]), SQLExecDirect(hStmt[0], (SQLCHAR*)sql, SQL_NTS)); // scan via other tx should not hang and see all rows for (unsigned j = 0; j < nc; j++) { count = -1; int want = j == 0 ? 0 : opt.m_scale; selectCount(test, hStmt[j], tab, &count); test.chk(HStmt(hStmt[j]), count == want, "tx %u: got %d != %d", j, (int)count, want); if (opt.m_v >= 2) ndbout << "tx " << j << " ok !" << endl; } // setting autocommit on commits the delete setAutocommit(test, hDbc[0], true); // check count count = -1; selectCount(test, hStmt[0], tab, &count); test.chk(HStmt(hStmt[0]), count == 0, "got %d != 0", (int)count); } for (unsigned j = 0; j < nc; j++) freeAll(test, hEnv[j], hDbc[j], hStmt[j]); } static void testPerf(Test& test) { if (test.m_stuff == 0) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); char sql[MAX_SQL], *sqlptr; for (unsigned i = 0; i < tabCount; i++) { Tab& tab = tabList[i]; if (! tab.optok()) continue; test.exp(SQL_NO_DATA, 0, 0, false); tab.deleteAll(sqlptr = sql); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); long count0 = -1; // XXX triggers SEGV somewhere //selectCount(test, hStmt, tab, &count0); //test.chk(HStmt(hStmt), count0 == 0, "got %d != 0", (int)count0); } freeAll(test, hEnv, hDbc, hStmt); return; } assert(test.m_stuff == 1 || test.m_stuff == 2); bool ndbapi = (test.m_stuff == 1); tt01: { const unsigned OFF = 1000000; const unsigned N = 25; Tab& tab = tabList[1]; if (! tab.optok()) goto out; if (ndbapi) { #ifndef ndbODBC if (opt.m_v >= 1) ndbout << "running via DM - test skipped" << endl; #else Ndb* ndb = new Ndb("TEST_DB"); ndb->init(); if (ndb->waitUntilReady() != 0) { ndbout << ndb->getNdbError() << endl; fatal("waitUntilReady"); } Uint32 val[1+N]; // insert for (unsigned k = 1; k <= opt.m_scale; k++) { NdbConnection* con = ndb->startTransaction(); if (con == 0) { ndbout << ndb->getNdbError() << endl; fatal("startTransaction"); } NdbOperation* op = con->getNdbOperation(tab.m_upperName); if (op == 0) { ndbout << con->getNdbError() << endl; fatal("getNdbOperation"); } if (op->insertTuple() == -1) { ndbout << op->getNdbError() << endl; fatal("insertTuple"); } for (unsigned j = 0; j <= N; j++) { val[j] = (j == 0 ? k + test.m_no * OFF : k * j); if (j == 0) { if (op->equal(j, val[j]) == -1) { ndbout << op->getNdbError() << endl; fatal("equal"); } } else { if (op->setValue(j, val[j]) == -1) { ndbout << op->getNdbError() << endl; fatal("setValue"); } } } if (con->execute(Commit) == -1) { ndbout << con->getNdbError() << endl; fatal("execute"); } ndb->closeTransaction(con); } test.timerCnt(opt.m_scale); // select PK for (unsigned k = 1; k <= opt.m_scale; k++) { NdbConnection* con = ndb->startTransaction(); if (con == 0) { ndbout << ndb->getNdbError() << endl; fatal("startTransaction"); } NdbOperation* op = con->getNdbOperation(tab.m_upperName); if (op == 0) { ndbout << con->getNdbError() << endl; fatal("getNdbOperation"); } if (op->readTuple() == -1) { ndbout << op->getNdbError() << endl; fatal("insertTuple"); } for (unsigned j = 0; j <= N; j++) { val[j] = (j == 0 ? k + test.m_no * OFF : 0); if (j == 0) { if (op->equal(j, val[j]) == -1) { ndbout << op->getNdbError() << endl; fatal("equal"); } } else { if (op->getValue(j, (char*)&val[j]) == 0) { ndbout << op->getNdbError() << endl; fatal("getValue"); } } } if (con->execute(Commit) == -1) { ndbout << con->getNdbError() << endl; fatal("execute"); } for (unsigned j = 1; j <= N; j++) { assert(val[j] == k * j); } ndb->closeTransaction(con); } test.timerCnt(opt.m_scale); // delete PK for (unsigned k = 1; k <= opt.m_scale; k++) { NdbConnection* con = ndb->startTransaction(); if (con == 0) { ndbout << ndb->getNdbError() << endl; fatal("startTransaction"); } NdbOperation* op = con->getNdbOperation(tab.m_upperName); if (op == 0) { ndbout << con->getNdbError() << endl; fatal("getNdbOperation"); } if (op->deleteTuple() == -1) { ndbout << op->getNdbError() << endl; fatal("deleteTuple"); } unsigned j = 0; val[j] = k + test.m_no * OFF; if (op->equal(j, val[j]) == -1) { ndbout << op->getNdbError() << endl; fatal("equal"); } if (con->execute(Commit) == -1) { ndbout << con->getNdbError() << endl; fatal("execute"); } ndb->closeTransaction(con); } test.timerCnt(opt.m_scale); delete ndb; #endif } else { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); long val[1+N]; char sql[MAX_SQL], *sqlptr; // insert tab.insertAll(sqlptr = sql); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); for (unsigned j = 0; j <= N; j++) { test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &val[j], 0, 0)); } test.m_perf = true; for (unsigned k = 1; k <= opt.m_scale; k++) { for (unsigned j = 0; j <= N; j++) { val[j] = (j == 0 ? k + test.m_no * OFF : k * j); } test.run(HStmt(hStmt), SQLExecute(hStmt)); } test.m_perf = false; test.timerCnt(opt.m_scale); // select PK tab.selectPk(sqlptr = sql); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); for (unsigned j = 0; j <= N; j++) { test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, SQL_C_SLONG, &val[j], 0, 0)); } test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + N + 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &val[0], 0, 0)); test.m_perf = true; for (unsigned k = 1; k <= opt.m_scale; k++) { val[0] = k + test.m_no * OFF; test.run(HStmt(hStmt), SQLExecute(hStmt)); test.run(HStmt(hStmt), SQLFetch(hStmt)); for (unsigned j = 1; j <= N; j++) { assert(val[j] == k * j); } test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } test.m_perf = false; test.timerCnt(opt.m_scale); // delete PK tab.deletePk(sqlptr = sql); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); unsigned j = 0; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &val[j], 0, 0)); test.m_perf = true; for (unsigned k = 1; k <= opt.m_scale; k++) { val[j] = k + test.m_no * OFF; test.run(HStmt(hStmt), SQLExecute(hStmt)); } test.m_perf = false; test.timerCnt(opt.m_scale); freeAll(test, hEnv, hDbc, hStmt); } out: ; } } struct Sql { const char* m_sql; int m_functionCode; int m_rowCount; int m_tuplesFetched; long m_lastValue; unsigned long m_bindValue; int m_ret; const char* m_state; SQLINTEGER m_native; bool m_reset; // run this function instead typedef void (*TestFunc)(Test& test); TestFunc m_testFunc; Sql() : m_sql(0) { } Sql(const char* do_cmd) : m_sql(do_cmd) { } Sql(const char* sql, int functionCode, int rowCount, int tuplesFetched, long lastValue, long bindValue) : m_sql(sql), m_functionCode(functionCode), m_rowCount(rowCount), m_tuplesFetched(tuplesFetched), m_lastValue(lastValue), m_bindValue(bindValue), m_ret(SQL_SUCCESS), m_state(0), m_native(0), m_reset(true), m_testFunc(0) { } // the 4 numbers after SQL_DIAG... rowCount tuplesFetched lastValue bindValue Sql(const char* sql, int functionCode, int rowCount, int tuplesFetched, long lastValue, long bindValue, int ret, const char* state, SQLINTEGER native, bool reset) : m_sql(sql), m_functionCode(functionCode), m_rowCount(rowCount), m_tuplesFetched(tuplesFetched), m_lastValue(lastValue), m_bindValue(bindValue), m_ret(ret), m_state(state), m_native(native), m_reset(reset), m_testFunc(0) { } Sql(const char* text, TestFunc testFunc) : m_sql(text), m_testFunc(testFunc) { } static const char* set_autocommit_on() { return "set autocommit on"; } static const char* set_autocommit_off() { return "set autocommit off"; } static const char* do_commit() { return "commit"; } static const char* do_rollback() { return "rollback"; } }; // 90 static const Sql miscSql90[] = { Sql("select * from dual", SQL_DIAG_SELECT_CURSOR, 1, 0, -1, -1), Sql("drop table tt90a", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt90a (a int, b int, c int, primary key(b, c)) storage(large) logging", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql() }; // 91 static const Sql miscSql91[] = { Sql("drop table tt91a", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt91a (a bigint unsigned primary key, b bigint unsigned not null, c varchar(10))", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("insert into tt91a values (1, 111, 'aaa')", SQL_DIAG_INSERT, 1, 0, -1, -1), // fails Sql("insert into tt91a values (2, null, 'ccc')", SQL_DIAG_INSERT, -1, 0, -1, -1, SQL_ERROR, "IM000", 2014203, true), Sql("update tt91a set b = 222 where a = 2", SQL_DIAG_UPDATE_WHERE, 0, 0, -1, -1, SQL_NO_DATA, 0, 0, true), // two more Sql("insert into tt91a values (2, 222, 'ccc')", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("insert into tt91a values (3, 333, 'bbb')", SQL_DIAG_INSERT, 1, 0, -1, -1), // direct update Sql("update tt91a set b = 112 where a = 1", SQL_DIAG_UPDATE_WHERE, 1, 0, -1, -1), Sql("update tt91a set b = 113 where a = 1 and b > 111", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), // update and delete with interpreted scan Sql("update tt91a set b = 114 where b < 114", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("delete from tt91a where b < 115", SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1), Sql("insert into tt91a values (1, 111, 'aaa')", SQL_DIAG_INSERT, 1, 0, -1, -1), // check rows: 1,111,aaa + 2,222,ccc + 3,333,bbb Sql("select * from tt91a order by c", SQL_DIAG_SELECT_CURSOR, 3, 3, 2, -1), Sql("select * from tt91a order by c desc", SQL_DIAG_SELECT_CURSOR, 3, 3, 1, -1), Sql("select * from tt91a where a = 2", SQL_DIAG_SELECT_CURSOR, 1, 1, -1, -1), Sql("select * from tt91a where a + b = 224", SQL_DIAG_SELECT_CURSOR, 1, 3, -1, -1), Sql("select * from tt91a where a = 4", SQL_DIAG_SELECT_CURSOR, 0, 0, -1, -1), Sql("select b-a from tt91a order by a-b", SQL_DIAG_SELECT_CURSOR, 3, 3, 110, -1), Sql("select sum(a+b) from tt91a", SQL_DIAG_SELECT_CURSOR, 1, 3, 672, -1), Sql("select x.b, y.b, z.b from tt91a x, tt91a y, tt91a z where x.b <= y.b and y.b < z.b order by x.b", SQL_DIAG_SELECT_CURSOR, 4, 13, 222, -1), Sql("select x.b, y.b, z.b from tt91a x, tt91a y, tt91a z where x.b + y.b = z.b order by x.b", SQL_DIAG_SELECT_CURSOR, 3, 15, 222, -1), // tmp index Sql("create unique hash index xx91a on tt91a(b)", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), Sql("select x.b, y.b, z.b from tt91a x, tt91a y, tt91a z where x.b + y.b = z.b order by x.b", SQL_DIAG_SELECT_CURSOR, 3, 15, 222, -1), Sql("drop index xx91a on tt91a", SQL_DIAG_DROP_INDEX, -1, -1, -1, -1), // add some duplicates Sql("insert into tt91a values (4, 222, 'ccc')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("insert into tt91a values (5, 333, 'bbb')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("insert into tt91a values (6, 333, 'bbb')", SQL_DIAG_INSERT, 1, -1, -1, -1), // check rows: 1,111,aaa + 2 * 2,222,ccc + 3 * 3,333,bbb Sql("select count(*) from tt91a", SQL_DIAG_SELECT_CURSOR, 1, -1, 6, -1), Sql("select a+b from tt91a where (b = 111 or b = 222 ) and (b = 222 or b = 333) and a > 1 and a < 3", SQL_DIAG_SELECT_CURSOR, 1, -1, 224, -1), Sql("select sum(a) from tt91a having min(a) = 1 and max(a) = 6", SQL_DIAG_SELECT_CURSOR, 1, -1, 21, -1), Sql("select sum(a) from tt91a where a = 2 or a = 4 having min(a) = 2 and max(a) = 4", SQL_DIAG_SELECT_CURSOR, 1, -1, 6, -1), Sql("select sum(a) from tt91a having min(a) = 1 and max(a) = 5", SQL_DIAG_SELECT_CURSOR, 0, -1, -1, -1), Sql("select sum(a), b from tt91a group by b order by b", SQL_DIAG_SELECT_CURSOR, 3, -1, 14, -1), Sql("select sum(a), b, c from tt91a group by b, c order by c", SQL_DIAG_SELECT_CURSOR, 3, -1, 6, -1), Sql("select b, sum(a) from tt91a group by b having b = 37 * sum(a)", SQL_DIAG_SELECT_CURSOR, 1, -1, 222, -1), // simple varchar vs interpreter test Sql("select count(*) from tt91a where c = 'ccc'", SQL_DIAG_SELECT_CURSOR, 1, 2, 2, -1), Sql("select count(*) from tt91a where c like '%b%'", SQL_DIAG_SELECT_CURSOR, 1, 3, 3, -1), // interpreter limits (crashes in api on v211) #if NDB_VERSION_MAJOR >= 3 Sql("select count(*) from tt91a where a in (99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2)", SQL_DIAG_SELECT_CURSOR, 1, 5, 5, -1), Sql("select count(*) from tt91a where c in ('xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','bbb','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy')", SQL_DIAG_SELECT_CURSOR, 1, 3, 3, -1), #endif // distinct Sql("select distinct b from tt91a order by b", SQL_DIAG_SELECT_CURSOR, 3, -1, 333, -1), // some illegal groupings Sql("select a from tt91a group by b", -1, -1, -1, -1, -1, SQL_ERROR, "IM000", -1, -1), Sql("select sum(a) from tt91a group by b having a = 2", -1, -1, -1, -1, -1, SQL_ERROR, "IM000", -1, -1), Sql("select sum(a) from tt91a group by b order by a", -1, -1, -1, -1, -1, SQL_ERROR, "IM000", -1, -1), // string functions Sql("insert into tt91a (c, b, a) values ('abcdef', 999, 9)", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("select count(*) from tt91a where left(c, 2) = 'ab' and substr(c, 3, 2) = 'cd' and right(c, 2) = 'ef'", SQL_DIAG_SELECT_CURSOR, 1, -1, 1, -1), // nulls Sql("update tt91a set c = null where a > 8", SQL_DIAG_UPDATE_WHERE, 1, -1, -1, -1), Sql("select a from tt91a where c is null and b is not null order by a", SQL_DIAG_SELECT_CURSOR, 1, -1, 9, -1), Sql("select a from tt91a where not (c is not null or b is null) order by a", SQL_DIAG_SELECT_CURSOR, 1, -1, 9, -1), // null value guard in interpreter Sql("select count(*) from tt91a where c < 'x' or c > 'x' or c != 'x' or c = 'x'", SQL_DIAG_SELECT_CURSOR, 1, 6, 6, -1), Sql("delete from tt91a where c is null", SQL_DIAG_DELETE_WHERE, 1, -1, -1, -1), // indexes Sql("update tt91a set b = a + 5", SQL_DIAG_UPDATE_WHERE, 6, 6, -1, -1), Sql("create unique hash index xx91a on tt91a(b)", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), // scan y primary key x Sql("select x.b from tt91a x, tt91a y where x.a = y.b + 0", SQL_DIAG_SELECT_CURSOR, 1, 7, 11, -1), // scan x index y Sql("select x.b from tt91a x, tt91a y where x.a + 0 = y.b", SQL_DIAG_SELECT_CURSOR, 1, -1, 11, -1), // scan x scan y Sql("select x.b from tt91a x, tt91a y where x.a + 0 = y.b + 0", SQL_DIAG_SELECT_CURSOR, 1, -1, 11, -1), // dml ops Sql("delete from tt91a where b = 11 and a > 999", SQL_DIAG_DELETE_WHERE, 0, 1, -1, -1, SQL_NO_DATA, 0, 0, true), Sql("delete from tt91a where b = 11", SQL_DIAG_DELETE_WHERE, 1, 0, -1, -1), Sql("delete from tt91a where b = 11", SQL_DIAG_DELETE_WHERE, 0, 0, -1, -1, SQL_NO_DATA, 0, 0, true), Sql("update tt91a set b = 10*10 where b = 10", SQL_DIAG_UPDATE_WHERE, 1, 0, -1, -1), Sql("update tt91a set b = 10 where b = 10*10", SQL_DIAG_UPDATE_WHERE, 1, 0, -1, -1), Sql("update tt91a set b = 10*10 where b = 10 and b >= 10", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("update tt91a set b = 10 where b = 10*10 and b >= 10*10", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), // char vs varchar Sql("drop table tt91b", SQL_DIAG_DROP_TABLE, -1, -1, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt91b (a int primary key, b char(5), c varchar(5))", SQL_DIAG_CREATE_TABLE, -1, -1, -1, -1), Sql("insert into tt91b values (1, 'abc', 'abc')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("insert into tt91b values (2, 'xyz', 'xyz')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("insert into tt91b values (3, 'xyz', 'xyz ')", SQL_DIAG_INSERT, 1, -1, -1, -1), // char = char strips blanks Sql("select count(*) from tt91b x where (x.b = 'abc') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1), Sql("select count(*) from tt91b x where (x.b = 'abc')", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("select count(*) from tt91b x where (x.b = 'abc ') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1), Sql("select count(*) from tt91b x where (x.b = 'abc ')", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), // varchar = char Sql("select count(*) from tt91b x where (x.c = 'abc') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1), Sql("select count(*) from tt91b x where (x.c = 'abc')", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("select count(*) from tt91b x where (x.c = 'abc ') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 0, -1), Sql("select count(*) from tt91b x where (x.c = 'abc ')", SQL_DIAG_SELECT_CURSOR, 1, 0, 0, -1), // char = varchar Sql("select count(*) from tt91b x, tt91b y where (x.b = y.c) or x.a = x.a+1 or y.a = y.a+1", SQL_DIAG_SELECT_CURSOR, 1, -1, 2, -1), Sql("select count(*) from tt91b x, tt91b y where (x.b = y.c)", SQL_DIAG_SELECT_CURSOR, 1, -1, 2, -1), // varchar = varchar Sql("select count(*) from tt91b x, tt91b y where (x.c = y.c) or x.a = x.a+1 or y.a = y.a+1", SQL_DIAG_SELECT_CURSOR, 1, -1, 3, -1), Sql("select count(*) from tt91b x, tt91b y where (x.c = y.c)", SQL_DIAG_SELECT_CURSOR, 1, -1, 3, -1), // less Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.b < y.b) or x.a = x.a+1 or y.a = y.a+1 order by x.a, y.a", SQL_DIAG_SELECT_CURSOR, 2, -1, 13, -1), Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.b < y.b) order by x.a, y.a", SQL_DIAG_SELECT_CURSOR, 2, -1, 13, -1), Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.c < y.c) or x.a = x.a+1 or y.a = y.a+1 order by x.a, y.a", SQL_DIAG_SELECT_CURSOR, 3, -1, 23, -1), Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.c < y.c) order by x.a, y.a", SQL_DIAG_SELECT_CURSOR, 3, -1, 23, -1), // like Sql("select count(*) from tt91b x where (x.b like 'a%') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1), Sql("select count(*) from tt91b x where (x.b like 'a%')", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("select count(*) from tt91b x where (x.b like 'x%z') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 0, -1), Sql("select count(*) from tt91b x where (x.b like 'x%z')", SQL_DIAG_SELECT_CURSOR, 1, 0, 0, -1), Sql("select count(*) from tt91b x where (x.a+0 = 2 and x.c like 'x%z') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1), Sql("select count(*) from tt91b x where (x.a+0 = 2 and x.c like 'x%z')", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("select count(*) from tt91b x where (x.a+0 = 3 and x.c like 'x%z ') or x.a = x.a+1", SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1), Sql("select count(*) from tt91b x where (x.a+0 = 3 and x.c like 'x%z ')", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql() }; // 92 static void testMisc92a(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); char sql[MAX_SQL]; char tname[20]; sprintf(tname, "tt92%c", 0140 + test.m_no); if (test.m_loop == 1) { lock_mutex(); sprintf(sql, "drop table %s", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.exp(SQL_ERROR, "IM000", 2040709, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); sprintf(sql, "create table %s (a int unsigned primary key, b int unsigned not null)", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); unlock_mutex(); } else { sprintf(sql, "delete from %s", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); test.run(HStmt(hStmt), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT)); } for (int on = true; on >= false; on--) { if (opt.m_v >= 2) ndbout << "set autocommit " << (on ? "ON" : "OFF") << endl; setAutocommit(test, hDbc, on); // insert rows if (opt.m_v >= 2) ndbout << "SQL: insert into " << tname << " ..." << opt.m_scale << endl; for (unsigned k = 0; k < opt.m_scale; k++) { sprintf(sql, "insert into %s values (%u, %u)", tname, k, 10 * k); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } // commit always test.run(HStmt(hStmt), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT)); // scan delete sprintf(sql, "delete from %s", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // rollback or commit test.run(HStmt(hStmt), SQLEndTran(SQL_HANDLE_DBC, hDbc, on ? SQL_COMMIT : SQL_ROLLBACK)); // count long count = -1; sprintf(sql, "select count(*) from %s", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; selectCount(test, hStmt, sql, &count); test.chk(HStmt(hStmt), count == on ? 0 : opt.m_scale, "%s: got %d != %d", tname, (int)count, (int)opt.m_scale); } freeAll(test, hEnv, hDbc, hStmt); } static const Sql miscSql92[] = { // create in C func Sql("testMisc92a", testMisc92a), Sql() }; // 93 static void testMisc93a(Test& test) { SQLHANDLE hEnv[2], hDbc[2], hStmt[2]; allocAll(test, hEnv[0], hDbc[0], hStmt[0]); allocAll(test, hEnv[1], hDbc[1], hStmt[1]); char sql[MAX_SQL]; // select via primary key setAutocommit(test, hDbc[0], false); sprintf(sql, "select c1 from tt93a where c0 = 1"); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt[0]), SQLExecDirect(hStmt[0], (SQLCHAR*)sql, SQL_NTS)); // update via another trans must time out sprintf(sql, "update tt93a set c1 = 'b' where c0 = 1"); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt[1]), SQLExecDirect(hStmt[1], (SQLCHAR*)sql, SQL_NTS)); freeAll(test, hEnv[0], hDbc[0], hStmt[0]); freeAll(test, hEnv[1], hDbc[1], hStmt[1]); } static const Sql miscSql93[] = { // create in C func Sql("drop table tt93a", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt93a (c0 int primary key, c1 char(10))", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("insert into tt93a values(1, 'a')", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("testMisc93a", testMisc93a), Sql() }; // 95 static const Sql miscSql95[] = { Sql("drop table tt95a", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt95a (a int not null, b char(10) not null, c int not null, d char(10), primary key(a, b)) storage(small)", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), // ordered index create and drop Sql("create index xx95a on tt95a (c, d) nologging", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), Sql("drop index xx95a on tt95a", SQL_DIAG_DROP_INDEX, -1, -1, -1, -1), Sql("create index xx95a on tt95a (c) nologging", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), Sql("insert into tt95a values(1, 'a', 10, 'b')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("insert into tt95a values(2, 'a', 20, 'b')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("insert into tt95a values(3, 'a', 30, 'b')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("select a from tt95a where c = 20", SQL_DIAG_SELECT_CURSOR, 1, 1, 2, -1), Sql("delete from tt95a where c = 10", SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1), Sql("update tt95a set c = 300 where c = 30", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("delete from tt95a where c = 300", SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1), Sql("delete from tt95a", SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1), // simple insert and rollback Sql("-- simple insert and rollback"), Sql(Sql::set_autocommit_off()), Sql("insert into tt95a values(1, 'a', 10, 'b')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("select count(*) from tt95a", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("select count(*) from tt95a where c = 10", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql(Sql::do_rollback()), Sql(Sql::set_autocommit_on()), Sql("select count(*) from tt95a", SQL_DIAG_SELECT_CURSOR, 1, 0, 0, -1), // simple update and rollback Sql("-- simple update and rollback"), Sql("insert into tt95a values(1, 'a', 10, 'b')", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql(Sql::set_autocommit_off()), Sql("update tt95a set c = 20 where c = 10", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("select count(*) from tt95a", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("select count(*) from tt95a where c = 20", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql(Sql::do_rollback()), Sql(Sql::set_autocommit_on()), Sql("select count(*) from tt95a where c = 10", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), // simple delete and rollback Sql("-- simple delete and rollback"), Sql(Sql::set_autocommit_off()), Sql("delete from tt95a where c = 10", SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1), Sql("select count(*) from tt95a", SQL_DIAG_SELECT_CURSOR, 0, 0, 0, -1), Sql("select count(*) from tt95a where c = 10", SQL_DIAG_SELECT_CURSOR, 0, 0, 0, -1), Sql(Sql::do_rollback()), Sql(Sql::set_autocommit_on()), Sql("select count(*) from tt95a where c = 10", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), // multiple update Sql("-- multiple update and rollback"), Sql(Sql::set_autocommit_off()), Sql("update tt95a set c = 20 where c = 10", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("select count(*) from tt95a where c = 20", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("update tt95a set c = 30 where c = 20", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("select count(*) from tt95a where c = 30", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("update tt95a set c = 40 where c = 30", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("select count(*) from tt95a where c = 40", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql("update tt95a set c = 50 where c = 40", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), Sql("select count(*) from tt95a where c = 50", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), Sql(Sql::do_rollback()), Sql(Sql::set_autocommit_on()), Sql("select count(*) from tt95a where c = 10", SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1), // another variant which found no tuple via index (aligment issue) Sql("drop table tt95b", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt95b (a int primary key, b char(10) not null, c int not null)", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("create index xx95b on tt95b (b, c) nologging", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), Sql("insert into tt95b values(0,'0123456789',1)", SQL_DIAG_INSERT, 1, -1, -1, -1), Sql("select a from tt95b where b='0123456789'", SQL_DIAG_SELECT_CURSOR, 1, 1, 0, -1), // update index key to different value Sql("update tt95b set b = '9876543210' where b = '0123456789'", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), // same value goes nuts... Sql("update tt95b set b = '9876543210'", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), #if 0 // ...if done via index key (variant of halloween problem) Sql("update tt95b set b = '9876543210' where b = '9876543210'", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1), #endif Sql() }; // 96 static void testMisc96a(Test& test) { // single thread if (test.m_no != 1) return; SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); char sql[MAX_SQL], *sqlptr; char tname[20]; strcpy(tname, "tt96a"); // drop table scopy(sqlptr = sql, "drop table %s", tname); test.exp(SQL_ERROR, "IM000", 2040709, false); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // create table with many attributes unsigned attrs = 1 + opt.m_scale; if (attrs > MAX_ATTRIBUTES_IN_TABLE) attrs = MAX_ATTRIBUTES_IN_TABLE; if (attrs > 64) attrs = 64; scopy(sqlptr = sql, "create table %s (c0 int primary key", tname); for (unsigned j = 1; j < attrs; j++) { if (j % 2 == 0) scopy(sqlptr, ", c%d int unsigned not null", j); else scopy(sqlptr, ", c%d char(10) not null", j); } scopy(sqlptr, ")"); if (opt.m_fragtype != 0) scopy(sqlptr, " storage(%s)", opt.m_fragtype); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // create or drop indexes const unsigned seed = 1000037 * test.m_loop + 1000039 * opt.m_scale; srandom(seed); const unsigned imax = opt.m_scale < 20 ? opt.m_scale : 20; AttributeMask* imasks = new AttributeMask[imax]; unsigned ccnt = 0; unsigned dcnt = 0; for (unsigned n = 0; n < imax; n++) imasks[n].clear(); while (ccnt + dcnt < opt.m_scale) { char iname[20]; unsigned n = urandom(imax); sprintf(iname, "xx96a%02d", n); AttributeMask& imask = imasks[n]; unsigned sel = urandom(10); if (imask.isclear()) { // create one unsigned ncol = 0; unsigned cols[MAX_ATTRIBUTES_IN_INDEX]; unsigned cnum = urandom(attrs); cols[ncol++] = cnum; while (ncol < MAX_ATTRIBUTES_IN_INDEX) { unsigned sel2 = urandom(10); if (sel2 < 2) break; unsigned cnum2 = urandom(attrs); if (sel2 < 9 && cnum2 == 0) continue; unsigned j; for (j = 0; j < ncol; j++) { if (cols[j] == cnum2) break; } if (j == ncol) cols[ncol++] = cnum2; } if (sel < 3) { scopy(sqlptr = sql, "create unique hash index %s on %s (", iname, tname); for (unsigned j = 0; j < ncol; j++) scopy(sqlptr, "%sc%d", j == 0 ? "" : ", ", cols[j]); scopy(sqlptr, ")"); } else { scopy(sqlptr = sql, "create index %s on %s (", iname, tname); for (unsigned j = 0; j < ncol; j++) scopy(sqlptr, "%sc%d", j == 0 ? "" : ", ", cols[j]); scopy(sqlptr, ") nologging"); } if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); for (unsigned j = 0; j < ncol; j++) imask.set(cols[j]); ccnt++; } else if (sel < 5 && ccnt > dcnt + 1) { scopy(sqlptr = sql, "drop index %s on %s", iname, tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); imask.clear(); dcnt++; } } // insert unique data unsigned rows = opt.m_scale; unsigned* uval = new unsigned[rows]; for (unsigned i = 0; i < rows; i++) { uval[i] = urandom(4); scopy(sqlptr = sql, "insert into %s values(", tname); for (unsigned j = 0; j < attrs; j++) { if (j != 0) scopy(sqlptr, ","); unsigned v = (i << 10) | (j << 2) | uval[i]; if (j == 0) scopy(sqlptr, "%u", i); else if (j % 2 == 0) scopy(sqlptr, "%u", v); else scopy(sqlptr, "'%010u'", v); } scopy(sqlptr, ")"); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } // update each row via random index for (unsigned i = 0; i < rows; i++) { unsigned uold = uval[i]; uval[i] = 3 - uval[i]; AttributeMask imask; do { unsigned j = urandom(imax); imask = imasks[j]; } while (imask.isclear()); scopy(sqlptr = sql, "update %s set", tname); for (unsigned j = 1; j < attrs; j++) { if (j != 1) scopy(sqlptr, ","); /* * Equality update is just barely doable before savepoints * provided we change value of keys in every index. */ unsigned v = (i << 10) | (j << 2) | uval[i]; if (j == 0) ; else if (j % 2 == 0) scopy(sqlptr, " c%d=%u", j, v); else scopy(sqlptr, " c%d='%010u'", j, v); } scopy(sqlptr, " where 1=1"); while (! imask.isclear()) { unsigned j = urandom(attrs); if (imask.get(j)) { unsigned v = (i << 10) | (j << 2) | uold; scopy(sqlptr, " and c%d=", j); if (j == 0) scopy(sqlptr, "%u", i); else if (j % 2 == 0) scopy(sqlptr, "%u", v); else scopy(sqlptr, "'%010u'", v); imask.clear(j); } } if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); chkRowCount(test, hStmt, 1); } // delete all scopy(sqlptr = sql, "delete from %s", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // if (opt.m_v >= 2) ndbout << tname << ": creates " << ccnt << " drops " << dcnt << endl; delete [] imasks; delete [] uval; freeAll(test, hEnv, hDbc, hStmt); } static const Sql miscSql96[] = { Sql("testMisc96a", testMisc96a), Sql() }; // 97 static void testMisc97a(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); const char* tname = "TT97A"; const char* iname = "XX97A"; char sql[MAX_SQL]; // create in some thread lock_mutex(); if (my_sema == 0) { if (opt.m_v >= 1) ndbout << "thread " << test.m_no << " does setup" << endl; sprintf(sql, "drop table %s", tname); if (opt.m_v >= 2) ndbout << "SQL[" << test.m_no << "]: " << sql << endl; test.exp(SQL_ERROR, "IM000", 2040709, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); // a-pk b-index c-counter sprintf(sql, "create table %s (a int primary key, b int, c int) storage(small)", tname); if (opt.m_v >= 2) ndbout << "SQL[" << test.m_no << "]: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); for (unsigned i = 0; i < opt.m_scale; i++) { sprintf(sql, "insert into %s values (%d, %d, %d)", tname, i, 10 * i, 0); if (opt.m_v >= 3) ndbout << "SQL[" << test.m_no << "]: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } sprintf(sql, "create index %s on %s (b) nologging", iname, tname); if (opt.m_v >= 2) ndbout << "SQL[" << test.m_no << "]: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); my_sema = 1; } unlock_mutex(); assert(my_sema == 1); // parallel run - default rotating pk, ts, is // frob: low 3 hex digits give alt sequence e.g. 0x311 = pk, pk, is // frob: 4-th hex digit non-zero says use NDB API e.g. 0x1000 unsigned typelist[3] = { 1, 2, 3 }; for (unsigned i = 0; i < 3; i++) { unsigned t = (opt.m_frob >> (i * 4)) & 0xf; if (t != 0) typelist[i] = t; } unsigned type = typelist[(test.m_no - 1) % 3]; if ((opt.m_frob & 0xf000) == 0) { for (unsigned i = 0; i < opt.m_scale; i++) { if (type == 1) { // pk update sprintf(sql, "update %s set c = c + 1 where a = %d", tname, i % opt.m_scale); if (opt.m_v >= 3) ndbout << lock << "SQL[" << test.m_no << "]: " << sql << endl << unlock; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } if (type == 2) { // table scan update sprintf(sql, "update %s set c = c + 1 where b + 0 = %d", tname, 10 * i); if (opt.m_v >= 3) ndbout << lock << "SQL[" << test.m_no << "]: " << sql << endl << unlock; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } if (type == 3) { // index scan update sprintf(sql, "update %s set c = c + 1 where b = %d", tname, 10 * i); if (opt.m_v >= 3) ndbout << lock << "SQL[" << test.m_no << "]: " << sql << endl << unlock; test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); } } } else { #ifdef ndbODBC #define CHK(o, x) do { if (! (x)) { fatal("line %d: %d %s", __LINE__, o->getNdbError().code, o->getNdbError().message); } } while (0) Ndb* ndb = new Ndb("TEST_DB"); ndb->init(); CHK(ndb, ndb->waitUntilReady() == 0); Int32 a, b, c; for (unsigned i = 0; i < opt.m_scale; i++) { if (type == 1) { // pk update with exclusive read NdbConnection* con; NdbOperation* op; CHK(ndb, (con = ndb->startTransaction()) != 0); a = i; c = -1; CHK(con, (op = con->getNdbOperation(tname)) != 0); CHK(op, op->readTupleExclusive() == 0); CHK(op, op->equal((unsigned)0, (char*)&a, 0) == 0); CHK(op, op->getValue(2, (char*)&c) != 0); CHK(con, con->execute(NoCommit) == 0); c = c + 1; CHK(con, (op = con->getNdbOperation(tname)) != 0); CHK(op, op->updateTuple() == 0); CHK(op, op->equal((unsigned)0, (char*)&a, 0) == 0); CHK(op, op->setValue(2, (char*)&c) == 0); CHK(con, con->execute(Commit) == 0); ndb->closeTransaction(con); if (opt.m_v >= 3) ndbout << lock << "thr " << test.m_no << " pk a=" << i << " c=" << c << endl << unlock; } if (type == 2) { // table scan update NdbConnection* con; NdbOperation* op; CHK(ndb, (con = ndb->startTransaction()) != 0); CHK(con, (op = con->getNdbOperation(tname)) != 0); CHK(con, op->openScanExclusive(240) == 0); CHK(op, op->getValue((unsigned)0, (char*)&a) != 0); CHK(op, op->getValue(2, (char*)&c) != 0); CHK(con, con->executeScan() == 0); unsigned rows = 0; unsigned updates = 0; while (1) { int ret; a = -1; c = -1; CHK(con, (ret = con->nextScanResult()) == 0 || ret == 1); if (ret == 1) break; rows++; if (a == i) { NdbConnection* con2; NdbOperation* op2; CHK(ndb, (con2 = ndb->startTransaction()) != 0); CHK(op, (op2 = op->takeOverForUpdate(con2)) != 0); c = c + 1; CHK(op2, op2->setValue(2, (char*)&c) == 0); CHK(con2, con2->execute(Commit) == 0); ndb->closeTransaction(con2); updates++; if (opt.m_v >= 3) ndbout << lock << "thr " << test.m_no << " ts rows=" << rows << " a=" << i << " c=" << c << endl << unlock; // test stop scan too CHK(con, con->stopScan() == 0); break; } } ndb->closeTransaction(con); test.chk(HStmt(hStmt), updates == 1, "got %u != 1", updates); } if (type == 3) { // index scan update NdbConnection* con; NdbOperation* op; CHK(ndb, (con = ndb->startTransaction()) != 0); CHK(con, (op = con->getNdbOperation(iname, tname)) != 0); CHK(con, op->openScanExclusive(240) == 0); b = 10 * i; CHK(con, op->setBound((unsigned)0, 4, &b, sizeof(b)) == 0); CHK(op, op->getValue((unsigned)0, (char*)&a) != 0); CHK(op, op->getValue(2, (char*)&c) != 0); CHK(con, con->executeScan() == 0); unsigned rows = 0; unsigned updates = 0; while (1) { int ret; a = -1; c = -1; CHK(con, (ret = con->nextScanResult()) == 0 || ret == 1); if (ret == 1) break; rows++; if (a == i) { NdbConnection* con2; NdbOperation* op2; CHK(ndb, (con2 = ndb->startTransaction()) != 0); CHK(op, (op2 = op->takeOverForUpdate(con2)) != 0); c = c + 1; CHK(op2, op2->setValue(2, (char*)&c) == 0); CHK(con2, con2->execute(Commit) == 0); ndb->closeTransaction(con2); updates++; if (opt.m_v >= 3) ndbout << lock << "thr " << test.m_no << " is rows=" << rows << " a=" << i << " c=" << c << endl << unlock; // test stop scan too CHK(con, con->stopScan() == 0); break; } } ndb->closeTransaction(con); test.chk(HStmt(hStmt), rows == 1, "got %u != 1", rows); test.chk(HStmt(hStmt), updates == 1, "got %u != 1", updates); } } delete ndb; #undef CHK #endif } // verify result lock_mutex(); if (++my_sema == 1 + opt.m_threads) { if (opt.m_v >= 1) ndbout << "thread " << test.m_no << " does verification" << endl; sprintf(sql, "select * from %s order by a", tname); if (opt.m_v >= 2) ndbout << "SQL[" << test.m_no << "]: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); long a, b, c; test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, &a, 0, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SLONG, &b, 0, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_SLONG, &c, 0, 0)); for (unsigned i = 0; i < opt.m_scale; i++) { a = b = c = -1; test.run(HStmt(hStmt), SQLFetch(hStmt)); test.chk(HStmt(hStmt), a == i, "a: got %ld != %u", a, i); test.chk(HStmt(hStmt), b == 10 * i, "b: got %ld != %u", b, 10 * i); test.chk(HStmt(hStmt), c == opt.m_threads, "c: got %ld != %u", c, opt.m_threads); if (opt.m_v >= 4) ndbout << "verified " << i << endl; } test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (opt.m_v >= 2) ndbout << "thr " << test.m_no << " verified " << opt.m_scale << " rows" << endl; my_sema = 0; } unlock_mutex(); freeAll(test, hEnv, hDbc, hStmt); } static const Sql miscSql97[] = { Sql("testMisc97a", testMisc97a), Sql() }; // 99 static void testMisc99a(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); // bad const char* sqlInsertBad = "insert into tt99a values(?, ?, ?, ?, ?)"; test.exp(SQL_ERROR, "21S01", -1, true); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlInsertBad, SQL_NTS)); // good const char* sqlInsert = "insert into tt99a (col1, col2, col3, col4, col5) values(?, ?, ?, ?, ?)"; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlInsert, SQL_NTS)); unsigned long value; for (unsigned i = 1; i <= 5; i++) { test.run(HStmt(hStmt), SQLBindParameter(hStmt, i, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &value, 0, 0)); } const unsigned long base = 1000000000; const unsigned long scale = 10; for (value = base; value < base + scale; value++) { test.run(HStmt(hStmt), SQLExecute(hStmt)); } // bug1: re-analyze of converted expression... const char* sqlSelect = "select col5 from tt99a where col2 + 0 = ?"; unsigned long output; test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_ULONG, &output, 0, 0)); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlSelect, SQL_NTS)); // bug2: previous bind must survive a new SQLPrepare if (0) { test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &value, 0, 0)); } for (value = base; value < base + scale; value++) { if (value > base + 4) { // bug1: ...when IPD changed by JDBC test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &value, 0, 0)); } test.run(HStmt(hStmt), SQLExecute(hStmt)); output = (unsigned long)-1; test.run(HStmt(hStmt), SQLFetch(hStmt)); test.chk(HStmt(hStmt), output == value, "got %lu != %lu", output, value); test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); test.timerCnt(1); } freeAll(test, hEnv, hDbc, hStmt); } static void testMisc99c(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); const char* sql = "select b from tt99c where a = ?"; const unsigned long c1 = 2100000000U; const unsigned long c2 = 4100000000U; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); unsigned long aval, bval; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &aval, 0, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_ULONG, &bval, 0, 0)); // uno for (unsigned i = 0; i < opt.m_scale; i++) { aval = c1; bval = (unsigned long)-1; if (opt.m_v >= 2) ndbout << "SQL: " << sql << " [?=" << (Uint64)aval << "]" << endl; test.run(HStmt(hStmt), SQLExecute(hStmt)); test.run(HStmt(hStmt), SQLFetch(hStmt)); test.chk(HStmt(hStmt), bval == c2, "got %lu != %lu", bval, c2); //test.exp(SQL_NO_DATA, 0, 0, true); //test.run(HStmt(hStmt), SQLFetch(hStmt)); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } // dos for (unsigned i = 0; i < opt.m_scale; i++) { break; // XXX not yet, hangs in NDB ?!? aval = c2; bval = (unsigned long)-1; if (opt.m_v >= 2) ndbout << "SQL: " << sql << " [?=" << (Uint64)aval << "]" << endl; test.run(HStmt(hStmt), SQLExecute(hStmt)); test.run(HStmt(hStmt), SQLFetch(hStmt)); test.chk(HStmt(hStmt), bval == c1, "got %lu != %lu", bval, c2); //test.exp(SQL_NO_DATA, 0, 0, true); //test.run(HStmt(hStmt), SQLFetch(hStmt)); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } freeAll(test, hEnv, hDbc, hStmt); } static void testMisc99d(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); const char* tname = "TT99D"; char sql[MAX_SQL]; sprintf(sql, "drop table %s", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.exp(SQL_ERROR, "IM000", 2040709, false); test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); sprintf(sql, "create table %s (a bigint unsigned, b bigint, primary key (a))", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS)); sprintf(sql, "insert into %s values (?, ?)", tname); if (opt.m_v >= 2) ndbout << "SQL: " << sql << endl; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); // XXX replace by 100 when signed vs unsigned resolved const unsigned num = 78; SQLUBIGINT aval; SQLBIGINT bval; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 0, 0, &aval, 0, 0)); test.run(HStmt(hStmt), SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &bval, 0, 0)); for (SQLBIGINT i = 0; i < num; i++) { if (opt.m_v >= 3) ndbout << "insert " << i << endl; aval = i * i * i * i * i * i * i * i * i * i; // 10 bval = -aval; test.run(HStmt(hStmt), SQLExecute(hStmt)); } sprintf(sql, "select a, b from tt99d where a = ?"); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS)); SQLUBIGINT kval; test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 0, 0, &kval, 0, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_UBIGINT, &aval, 0, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SBIGINT, &bval, 0, 0)); for (SQLBIGINT i = 0; i < num; i++) { kval = i * i * i * i * i * i * i * i * i * i; // 10 if (opt.m_v >= 3) ndbout << "fetch " << i << " key " << kval << endl; test.run(HStmt(hStmt), SQLExecute(hStmt)); aval = bval = 0; test.run(HStmt(hStmt), SQLFetch(hStmt)); test.chk(HStmt(hStmt), aval == kval && bval == -kval, "got %llu, %lld != %llu, %lld", aval, bval, kval, -kval); test.exp(SQL_NO_DATA, 0, 0, true); test.run(HStmt(hStmt), SQLFetch(hStmt)); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); } freeAll(test, hEnv, hDbc, hStmt); } static void testMiscC2(Test& test) { SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); #if 0 { char POP[255]; char PORT[255]; char ACCESSNODE[255]; const char* sqlSelect = "select PORT from AAA where POP=? and ACCESSNODE=?"; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlSelect, SQL_NTS)); for (int j=0; j<5; j++) { printf("Loop %u\n", j); printf("LINE %u\n", __LINE__); test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, POP, 255, 0)); test.run(HStmt(hStmt), SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, ACCESSNODE, 255, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_CHAR, PORT, 255, 0)); sprintf(POP, "a"); sprintf(ACCESSNODE, "b"); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.run(HStmt(hStmt), SQLFetch(hStmt)); printf("got %s\n", PORT); printf("LINE %u\n", __LINE__); test.exp(SQL_NO_DATA, 0, 0, true); printf("LINE %u\n", __LINE__); test.run(HStmt(hStmt), SQLFetch(hStmt)); printf("LINE %u\n", __LINE__); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); printf("LINE %u\n", __LINE__); } } return; #endif char POP[255]; char PORT[255]; char ACCESSNODE[255]; unsigned long VLAN = 0; unsigned long SNMP_INDEX = 0; unsigned long PORT_STATE = 0; unsigned long STATIC_PORT = 0; unsigned long COMMENT = 0; const char* sqlSelect = "select PORT, PORT_STATE from PORTS where POP=? and ACCESSNODE=?"; test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlSelect, SQL_NTS)); for (int j=0; j<5; j++) { printf("Loop %u\n", j); printf("LINE %u\n", __LINE__); test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, POP, 255, 0)); test.run(HStmt(hStmt), SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, ACCESSNODE, 255, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_CHAR, PORT, 255, 0)); test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_ULONG, &PORT_STATE, 0, 0)); sprintf(POP, "row%u.i%u.bredband.com", 2, 3); sprintf(ACCESSNODE, "as%u", 2); test.run(HStmt(hStmt), SQLExecute(hStmt)); for (int i=0; i < 3; i++) { PORT_STATE=0; sprintf(PORT, "XXXXXXXXXXXXXXXXXXXXX"); test.run(HStmt(hStmt), SQLFetch(hStmt)); printf("got %s %lu\n", PORT, PORT_STATE); // test.chk(HStmt(hStmt), false, "got %s != %s", "xxx", PORT); } printf("LINE %u\n", __LINE__); test.exp(SQL_NO_DATA, 0, 0, true); printf("LINE %u\n", __LINE__); test.run(HStmt(hStmt), SQLFetch(hStmt)); printf("LINE %u\n", __LINE__); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); printf("LINE %u\n", __LINE__); } } static const Sql miscSqlC2[] = { Sql("drop table PORTS", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table PORTS (POP varchar(200) not null, ACCESSNODE varchar(200) not null, PORT varchar(200) not null, VLAN int unsigned, SNMP_INDEX int unsigned, PORT_STATE int unsigned, STATIC_PORT int unsigned, COMMENT int unsigned, primary key (POP,ACCESSNODE,PORT))", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("create index xxPORTS on PORTS (POP, ACCESSNODE) nologging", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), Sql("insert into PORTS values ('row2.i3.bredband.com','as2','Fa0/0',0,1,2,3,4)", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("insert into PORTS values ('row2.i3.bredband.com','as2','Fa0/1',1,2,3,4,5)", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("insert into PORTS values ('row2.i3.bredband.com','as2','Fa0/2',2,3,4,5,6)", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("select PORT, PORT_STATE from PORTS where POP='row2.i3.bredband.com' and ACCESSNODE='as2'", SQL_DIAG_SELECT_CURSOR, 3, 3, -1, -1), Sql("drop table AAA", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table AAA (POP varchar(200), ACCESSNODE varchar(200) not null, PORT varchar(200) not null, primary key (POP,ACCESSNODE,PORT))", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("create index xxAAA on AAA (POP, ACCESSNODE) nologging", SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1), Sql("insert into AAA values ('a','b','A')", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("testMiscC2", testMiscC2), Sql() }; /* > SELECT PORT, PORT_STATE FROM PORTS where pop=? and accessnode=? > SELECT VLAN, SNMP_INDEX, PORT_STATE, STATIC_PORT, COMMENT FROM PORTS WHERE POP=? AND ACCESSNODE=? AND PORT=? > select count(*) from ports > select snmp_index from ports where pop='row2.i3.bredband.com' and accessnode='as2' and port='Fa0/2' > SELECT MAC, MAC_EXPIRE, IP, IP_EXPIRE, HOSTNAME, DETECTED, STATUS, STATIC_DNS, BLOCKED, NUM_REQUESTS, ACCESSTYPE, OS_TYPE, GATE_WAY, DIRTY_FLAG, LOCKED_IP FROM CLIENTS WHERE PORT=? AND ACCESSNODE=? AND POP=? > SELECT SERVICES.ACCESSTYPE, SERVICES.NUM_IP, SERVICES.TEXPIRE, SERVICES.CUSTOMER_ID, SERVICES.LEASED_NUM_IP, SERVICES.PROVIDER, SERVICES.LOCKED_IP, SERVICES.STATIC_DNS, SERVICES.SUSPENDED_SERVICE FROM SERVICES , ACCESSTYPES WHERE SERVICES.PORT = ? AND SERVICES.ACCESSNODE = ? AND SERVICES.POP = ? AND SERVICES.ACCESSTYPE=ACCESSTYPES.ACCESSTYPE */ static const Sql miscSql99[] = { Sql("drop table tt99a", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt99a (col1 int unsigned primary key, col2 int unsigned, col3 int unsigned, col4 int unsigned, col5 int unsigned, col6 varchar(7) default 'abc123')", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), // inserts 10 rows, all same, start value 1000000000 Sql("testMisc99a", testMisc99a), // interpreted scan plus bind parameter Sql("select col1 from tt99a where col2 = ?", SQL_DIAG_SELECT_CURSOR, 1, 1, 1000000004, 1000000004), Sql("select col1 from tt99a where col2 = 1000000000 + ?", SQL_DIAG_SELECT_CURSOR, 1, 1, 1000000004, 4), Sql("select col1 from tt99a where col2 = ? + 1000000000", SQL_DIAG_SELECT_CURSOR, 1, 1, 1000000004, 4), // same not interpreted, tuple count 10 Sql("select col1 from tt99a where col2 + 0 = 1000000000 + ?", SQL_DIAG_SELECT_CURSOR, 1, 10, 1000000004, 4), // varchar variations Sql("select count(*) from tt99a where col6 = 'abc123'", SQL_DIAG_SELECT_CURSOR, 1, 10, 10, -1), Sql("select count(*) from tt99a where left(col6, ?) = 'abc1'", SQL_DIAG_SELECT_CURSOR, 1, 10, 10, 4), Sql("select count(*) from tt99a where left(col6, ?) = 'abc1'", SQL_DIAG_SELECT_CURSOR, 1, 10, 0, 3), // tpc-b inspired, wrong optimization to direct update Sql("drop table tt99b", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt99b(a int primary key, b int not null, c double precision)", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("insert into tt99b values(1, 10, 100.0)", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("insert into tt99b values(9, 90, 900.0)", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("create unique hash index tt99y on tt99b (b)", SQL_DIAG_CREATE_INDEX, -1, 0, -1, -1), // first scan update.. Sql("update tt99b set c = c + ? where a+0 = 1", SQL_DIAG_UPDATE_WHERE, 1, 2, -1, 10), Sql("update tt99b set c = c + ? where b+0 = 10", SQL_DIAG_UPDATE_WHERE, 1, 2, -1, 10), // then optimized.. Sql("update tt99b set c = c + ? where a = 1", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, 10), Sql("update tt99b set c = c + ? where b = 10", SQL_DIAG_UPDATE_WHERE, 1, 1, -1, 10), // verify.. Sql("select count(*) from tt99b where 100-1 < c and c < 140-1", SQL_DIAG_SELECT_CURSOR, 1, 2, 0, -1), Sql("select count(*) from tt99b where 140-.001 < c and c < 140+.001", SQL_DIAG_SELECT_CURSOR, 1, 2, 1, -1), // unsigned test Sql("drop table tt99c", SQL_DIAG_DROP_TABLE, -1, 0, -1, -1, SQL_ERROR, "IM000", 2040709, false), Sql("create table tt99c(a int unsigned primary key, b int unsigned)", SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1), Sql("insert into tt99c values(2100000000, 4100000000)", SQL_DIAG_INSERT, 1, 0, -1, -1), Sql("insert into tt99c (a, b) select b, a from tt99c", SQL_DIAG_INSERT, 1, 1, -1, -1), Sql("testMisc99c", testMisc99c), // new external type SQL_C_[SU]BIGINT Sql("testMisc99d", testMisc99d), Sql() }; static const struct { const Sql* sql; int minscale; } miscSql[11] = { { miscSql90, 0 }, { miscSql91, 0 }, { miscSql92, 0 }, { miscSql93, 0 }, { 0, 0 }, // 94 { miscSql95, 0 }, { miscSql96, 0 }, { miscSql97, 0 }, { 0, 0 }, // 98 { miscSql99, 0 }, { miscSqlC2, 0 } }; static void testSql(Test& test) { const unsigned salt = test.m_stuff; // mess if (opt.m_scale < miscSql[salt].minscale) { if (opt.m_v >= 1) ndbout << "skip - requires scale >= " << miscSql[salt].minscale << endl; return; } assert(0 <= salt && salt < 11 && miscSql[salt].sql != 0); SQLHANDLE hEnv, hDbc, hStmt; allocAll(test, hEnv, hDbc, hStmt); for (unsigned i = 0; ; i++) { const Sql& sql = miscSql[salt].sql[i]; if (sql.m_sql == 0) break; if (opt.m_v >= 2) ndbout << "SQL: " << sql.m_sql << endl; if (sql.m_testFunc != 0) { (*sql.m_testFunc)(test); continue; } if (strncmp(sql.m_sql, "--", 2) == 0) { continue; } if (strcmp(sql.m_sql, Sql::set_autocommit_on()) == 0) { setAutocommit(test, hDbc, true); continue; } if (strcmp(sql.m_sql, Sql::set_autocommit_off()) == 0) { setAutocommit(test, hDbc, false); continue; } if (strcmp(sql.m_sql, Sql::do_commit()) == 0) { test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT)); continue; } if (strcmp(sql.m_sql, Sql::do_rollback()) == 0) { test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_ROLLBACK)); continue; } if (opt.m_v >= 3) { ndbout << "expect:"; ndbout << " ret=" << sql.m_ret; ndbout << " rows=" << sql.m_rowCount; ndbout << " tuples=" << sql.m_tuplesFetched; ndbout << endl; } test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND)); test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_RESET_PARAMS)); // prep test.exp(sql.m_ret, sql.m_state, sql.m_native, false); test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql.m_sql, SQL_NTS)); if (test.m_ret != SQL_SUCCESS) continue; // bind between prep and exec like JDBC unsigned long bindValue = sql.m_bindValue; for (int k = 0; k <= 1; k++) { if (bindValue != -1) { assert(strchr(sql.m_sql, '?') != 0); test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &bindValue, 0, 0)); } if (k == 0) { if (bindValue != -1) { test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_RESET_PARAMS)); // exec with unbound parameter test.exp(SQL_ERROR, "HY010", -1, true); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == sql.m_functionCode || sql.m_functionCode == -1, "func: got %d != %d", (int)test.m_functionCode, (int)sql.m_functionCode); } } else { // exec test.exp(sql.m_ret, sql.m_state, sql.m_native, sql.m_reset); test.run(HStmt(hStmt), SQLExecute(hStmt)); test.chk(HStmt(hStmt), test.m_functionCode == sql.m_functionCode || sql.m_functionCode == -1, "func: got %d != %d", (int)test.m_functionCode, (int)sql.m_functionCode); } } if (sql.m_rowCount != -1) { if (sql.m_functionCode == SQL_DIAG_SELECT_CURSOR) { long lastValue; if (sql.m_lastValue != -1) test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, &lastValue, 0, 0)); unsigned k = 0; do { int rowCount = 0; lastValue = -1; while (1) { test.exp(SQL_NO_DATA, 0, 0, false); test.run(HStmt(hStmt), SQLFetch(hStmt)); if (test.m_ret == SQL_NO_DATA) break; rowCount++; } test.chk(HStmt(hStmt), rowCount == sql.m_rowCount, "rowCount: got %d != %d", (int)rowCount, (int)sql.m_rowCount); if (sql.m_tuplesFetched != -1) chkTuplesFetched(test, hStmt, sql.m_tuplesFetched); if (rowCount > 0 && sql.m_lastValue != -1) test.chk(HStmt(hStmt), lastValue == sql.m_lastValue, "lastValue: got %ld != %ld", (long)lastValue, (long)sql.m_lastValue); test.run(HStmt(hStmt), SQLCloseCursor(hStmt)); if (++k >= opt.m_scale) break; test.run(HStmt(hStmt), SQLExecute(hStmt)); } while (1); test.timerCnt(opt.m_scale); } else { assert(sql.m_lastValue == -1); chkRowCount(test, hStmt, sql.m_rowCount); if (sql.m_tuplesFetched != -1) chkTuplesFetched(test, hStmt, sql.m_tuplesFetched); test.timerCnt(1); } } } freeAll(test, hEnv, hDbc, hStmt); } // name, function, runmode, salt (0=const or n/a), description static const Case caseList[] = { Case( "00alloc", testAlloc, Case::Thread, 0, "allocate handles" ), Case( "01create", testCreate, Case::Single, 0, "create tables for the test" ), Case( "02prepare", testPrepare, Case::Thread, 0, "prepare without execute" ), Case( "03catalog", testCatalog, Case::Thread, 0, "catalog functions" ), Case( "10insert", testInsert, Case::Thread, 1, "insert computed rows" ), Case( "11delall", testDeleteAll, Case::Single, 0, "delete all rows via scan" ), Case( "12insert", testInsert, Case::Thread, 1, "insert computed rows again" ), Case( "13count", testCount, Case::Single, 0, "count rows" ), Case( "14verpk", testVerifyPk, Case::Thread, 1, "verify via primary key" ), Case( "15verscan", testVerifyScan, Case::Serial, 1, "verify via range scans" ), Case( "16join", testJoin, Case::Single, 0, "multiple self-join" ), Case( "17cart", testCart, Case::Single, 0, "cartesian join" ), Case( "20updpk", testUpdatePk, Case::Thread, 2, "update via primary key" ), Case( "21verpk", testVerifyPk, Case::Thread, 2, "verify via primary key" ), Case( "22verscan", testVerifyScan, Case::Serial, 2, "verify via range scans" ), Case( "23updscan", testUpdateScan, Case::Serial, 0, "update via scan" ), Case( "24verpk", testVerifyPk, Case::Thread, 0, "verify via primary key" ), Case( "25verscan", testVerifyScan, Case::Serial, 0, "verify via range scans" ), Case( "26delpk", testDeletePk, Case::Thread, 0, "delete via primary key" ), Case( "30trans", testTrans, Case::Single, 3, "rollback and commit" ), Case( "31concur", testConcur, Case::Single, 0, "commit across open cursor" ), Case( "32readcom", testReadcom, Case::Single, 0, "read committed" ), Case( "40perf", testPerf, Case::Single, 0, "perf test prepare" ), Case( "41perf", testPerf, Case::Thread, 1, "perf test NDB API" ), Case( "42perf", testPerf, Case::Thread, 2, "perf test NDB ODBC" ), Case( "90sql", testSql, Case::Single, 0, "misc SQL: metadata" ), Case( "91sql", testSql, Case::Single, 1, "misc SQL: misc" ), Case( "92sql", testSql, Case::Thread, 2, "misc SQL: scan rollback" ), Case( "93sql", testSql, Case::Single, 3, "misc SQL: locking" ), Case( "95sql", testSql, Case::Single, 5, "misc SQL: indexes (simple)" ), Case( "96sql", testSql, Case::Single, 6, "misc SQL: indexes" ), Case( "97sql", testSql, Case::Thread, 7, "misc SQL: indexes" ), Case( "99sql", testSql, Case::Single, 9, "misc SQL: bug du jour" ), Case( "C2", testSql, Case::Single, 10, "misc SQL: C2" ) }; static const unsigned caseCount = arraySize(caseList); static bool findCase(const char* name) { for (unsigned i = 0; i < caseCount; i++) { const Case& cc = caseList[i]; if (strstr(cc.m_name, name) != 0) return true; } return false; } static void listCases() { ndbout << "cases:" << endl; unsigned m = 0; for (unsigned i = 0; i < caseCount; i++) { const Case& cc = caseList[i]; if (m < strlen(cc.m_name)) m = strlen(cc.m_name); } for (unsigned i = 0; i < caseCount; i++) { const Case& cc = caseList[i]; char buf[200]; sprintf(buf, "%-*s [%-6s] %s", m, cc.m_name, cc.modename(), cc.m_desc); ndbout << buf << endl; } } // threads extern "C" { static void* testThr(void* arg); } struct Thr { enum State { Wait = 1, // wait for test case Run = 2, // run the test case Done = 3, // done with the case Exit = 4 // exit thread }; unsigned m_no; // thread number 1 .. max NdbThread* m_thr; // thread id etc const Case* m_case; // current case State m_state; // condition variable NdbMutex* m_mutex; // condition guard NdbCondition* m_cond; void* m_status; // exit status (not used) Test m_test; // test runner Thr(unsigned no, unsigned loop) : m_no(no), m_thr(0), m_case(0), m_state(Wait), m_mutex(NdbMutex_Create()), m_cond(NdbCondition_Create()), m_status(0), m_test(no, loop) { } ~Thr() { destroy(); NdbCondition_Destroy(m_cond); NdbMutex_Destroy(m_mutex); } void create() { assert(m_thr == 0); m_thr = NdbThread_Create(testThr, (void**)this, 64*1024, "test", NDB_THREAD_PRIO_LOW); } void destroy() { if (m_thr != 0) NdbThread_Destroy(&m_thr); m_thr = 0; } void lock() { NdbMutex_Lock(m_mutex); } void unlock() { NdbMutex_Unlock(m_mutex); } void wait() { NdbCondition_Wait(m_cond, m_mutex); } void signal() { NdbCondition_Signal(m_cond); } void join() { NdbThread_WaitFor(m_thr, &m_status); m_thr = 0; } // called from main void mainStart(const Case& cc) { lock(); m_case = &cc; m_state = Run; signal(); unlock(); } void mainStop() { lock(); while (m_state != Done) { if (opt.m_v >= 4) ndbout << ::lock << "thr " << m_no << " [main] wait state=" << m_state << endl << ::unlock; wait(); } if (opt.m_v >= 4) ndbout << ::lock << "thr " << m_no << " [main] done" << endl << ::unlock; m_state = Wait; unlock(); } // run in thread void testSelf() { while (1) { lock(); while (m_state != Run && m_state != Exit) { if (opt.m_v >= 4) ndbout << ::lock << "thr " << m_no << " [self] wait state=" << m_state << endl << ::unlock; wait(); } if (m_state == Run) { if (opt.m_v >= 4) ndbout << ::lock << "thr " << m_no << " [self] run" << endl << ::unlock; assert(m_case != 0); m_test.timerOn(); m_test.runCase(*m_case); m_test.timerOff(); m_state = Done; if (opt.m_v >= 4) ndbout << ::lock << "thr " << m_no << " [self] done" << endl << ::unlock; signal(); unlock(); } else if (m_state == Exit) { unlock(); break; } else { assert(false); } } if (opt.m_v >= 4) ndbout << ::lock << "thr " << m_no << " [self] exit" << endl << ::unlock; } }; static void* testThr(void* arg) { Thr& thr = *(Thr*)arg; thr.testSelf(); return 0; } #ifdef DMALLOC extern "C" { static int malloc_bytes = 0; static int free_bytes = 0; static void malloc_track(const char *file, const unsigned int line, const int func_id, const DMALLOC_SIZE byte_size, const DMALLOC_SIZE alignment, const DMALLOC_PNT old_addr, const DMALLOC_PNT new_addr) { if (func_id == DMALLOC_FUNC_MALLOC) { malloc_bytes += byte_size; return; } if (func_id == DMALLOC_FUNC_FREE) { DMALLOC_SIZE size = 0; dmalloc_examine(old_addr, &size, 0, 0, 0); free_bytes += size; // XXX useless - byte_size and size are 0 return; } } } #endif /* DMALLOC */ static void testMain() { #ifndef NDB_LINUX /* valgrind-1.0.3 does not support */ NdbThread_SetConcurrencyLevel(opt.m_threads + 2); #endif #ifdef DMALLOC dmalloc_track(malloc_track); #endif Test test(0, 0); #ifdef ndbODBC Ndb* ndb = 0; if (1) { // pre-alloc one Ndb object ndb = new Ndb("TEST_DB"); ndb->init(); if (ndb->waitUntilReady() != 0) { ndbout << ndb->getNdbError() << endl; fatal("waitUntilReady"); } } #endif for (unsigned loop = 1; opt.m_loop == 0 || loop <= opt.m_loop; loop++) { if (opt.m_v >= 2) ndbout << "loop " << loop << endl; // create new set of threads in each loop Thr** thrList = new Thr* [1 + opt.m_threads]; for (unsigned n = 1; n <= opt.m_threads; n++) { Thr& thr = *(thrList[n] = new Thr(n, loop)); thr.create(); if (opt.m_v >= 4) ndbout << "thr " << n << " [main] created" << endl; } #ifdef DMALLOC malloc_bytes = free_bytes = 0; #endif for (unsigned i = 0; i < caseCount; i++) { const Case& cc = caseList[i]; if (! cc.matchcase()) continue; if (opt.m_v >= 2) ndbout << "RUN: " << cc.m_name << " - " << cc.m_desc << endl; test.timerOn(); for (unsigned subloop = 1; subloop <= opt.m_subloop; subloop++) { my_sema = 0; if (opt.m_v >= 3) ndbout << "subloop " << subloop << endl; if (cc.m_mode == Case::Single) { Thr& thr = *thrList[1]; thr.mainStart(cc); thr.mainStop(); test.timerCnt(thr.m_test); } else if (cc.m_mode == Case::Serial) { for (unsigned n = 1; n <= opt.m_threads; n++) { Thr& thr = *thrList[n]; thr.mainStart(cc); thr.mainStop(); test.timerCnt(thr.m_test); } } else if (cc.m_mode == Case::Thread) { for (unsigned n = 1; n <= opt.m_threads; n++) { Thr& thr = *thrList[n]; thr.mainStart(cc); } for (unsigned n = 1; n <= opt.m_threads; n++) { Thr& thr = *thrList[n]; thr.mainStop(); test.timerCnt(thr.m_test); } } else { assert(false); } } test.timerOff(); if (opt.m_v >= 1) ndbout << cc.m_name << " total " << test << endl; } #ifdef DMALLOC if (opt.m_v >= 9) // XXX useless now ndbout << "malloc " << malloc_bytes << " free " << free_bytes << " lost " << malloc_bytes - free_bytes << endl; #endif // tell threads to exit for (unsigned n = 1; n <= opt.m_threads; n++) { Thr& thr = *thrList[n]; thr.lock(); thr.m_state = Thr::Exit; thr.signal(); thr.unlock(); if (opt.m_v >= 4) ndbout << "thr " << n << " [main] told to exit" << endl; } for (unsigned n = 1; n <= opt.m_threads; n++) { Thr& thr = *thrList[n]; thr.join(); if (opt.m_v >= 4) ndbout << "thr " << n << " [main] joined" << endl; delete &thr; } delete[] thrList; } #ifdef ndbODBC delete ndb; #endif } static bool str2num(const char* arg, const char* str, unsigned* num, unsigned lo = 0, unsigned hi = 0) { char* end = 0; long n = strtol(str, &end, 0); if (end == 0 || *end != 0 || n < 0) { ndbout << arg << " " << str << " is invalid number" << endl; return false; } if (lo != 0 && n < lo) { ndbout << arg << " " << str << " is too small min = " << lo << endl; return false; } if (hi != 0 && n > hi) { ndbout << arg << " " << str << " is too large max = " << hi << endl; return false; } *num = n; return true; } NDB_COMMAND(testOdbcDriver, "testOdbcDriver", "testOdbcDriver", "testOdbcDriver", 65535) { while (++argv, --argc > 0) { const char* arg = argv[0]; if (strcmp(arg, "-case") == 0) { if (++argv, --argc > 0) { assert(opt.m_namecnt < arraySize(opt.m_name)); opt.m_name[opt.m_namecnt++] = argv[0]; if (findCase(argv[0])) continue; } } if (strcmp(arg, "-core") == 0) { opt.m_core = true; continue; } if (strcmp(arg, "-depth") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_depth)) continue; } } if (strcmp(arg, "-dsn") == 0) { if (++argv, --argc > 0) { opt.m_dsn = argv[0]; continue; } } if (strcmp(arg, "-frob") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_frob)) continue; } } if (strcmp(arg, "-errs") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_errs)) continue; } } if (strcmp(arg, "-fragtype") == 0) { if (++argv, --argc > 0) { opt.m_fragtype = argv[0]; continue; } } if (strcmp(arg, "-home") == 0) { if (++argv, --argc > 0) { opt.m_home = argv[0]; continue; } } if (strcmp(arg, "-loop") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_loop)) continue; } } if (strcmp(arg, "-nogetd") == 0) { opt.m_nogetd = true; continue; } if (strcmp(arg, "-noputd") == 0) { opt.m_noputd = true; continue; } if (strcmp(arg, "-nosort") == 0) { opt.m_nosort = true; continue; } if (strcmp(arg, "-scale") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_scale)) continue; } } if (strcmp(arg, "-serial") == 0) { opt.m_serial = true; continue; } if (strcmp(arg, "-skip") == 0) { if (++argv, --argc > 0) { assert(opt.m_skipcnt < arraySize(opt.m_skip)); opt.m_skip[opt.m_skipcnt++] = argv[0]; if (findCase(argv[0])) continue; } } if (strcmp(arg, "-subloop") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_subloop)) continue; } } if (strcmp(arg, "-table") == 0) { if (++argv, --argc > 0) { opt.m_table = argv[0]; if (findTable()) continue; } } if (strcmp(arg, "-threads") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_threads, 1, MAX_THR)) continue; } } if (strcmp(arg, "-trace") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_trace)) continue; } } if (strcmp(arg, "-v") == 0) { if (++argv, --argc > 0) { if (str2num(arg, argv[0], &opt.m_v)) continue; } } if (strncmp(arg, "-v", 2) == 0 && isdigit(arg[2])) { if (str2num(arg, &arg[2], &opt.m_v)) continue; } printusage(); return NDBT_ProgramExit(NDBT_WRONGARGS); } homeEnv: { static char env[1000]; if (opt.m_home != 0) { sprintf(env, "NDB_HOME=%s", opt.m_home); putenv(env); } } traceEnv: { static char env[40]; sprintf(env, "NDB_ODBC_TRACE=%u", opt.m_trace); putenv(env); } debugEnv: { static char env[40]; sprintf(env, "NDB_ODBC_DEBUG=%d", 1); putenv(env); } testMain(); return NDBT_ProgramExit(NDBT_OK); } // vim: set sw=4: