testOdbcDriver.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:158k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2003 MySQL AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. /* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  14.    This program is free software; you can redistribute it and/or modify
  15.    it under the terms of the GNU General Public License as published by
  16.    the Free Software Foundation; either version 2 of the License, or
  17.    (at your option) any later version.
  18.    This program is distributed in the hope that it will be useful,
  19.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.    GNU General Public License for more details.
  22.    You should have received a copy of the GNU General Public License
  23.    along with this program; if not, write to the Free Software
  24.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  25. /*
  26.  * testOdbcDriver
  27.  *
  28.  * Test of ODBC and SQL using a fixed set of tables.
  29.  */
  30. #include <ndb_global.h>
  31. #undef test
  32. #include <ndb_version.h>
  33. #include <kernel/ndb_limits.h>
  34. #include <Bitmask.hpp>
  35. #include <kernel/AttributeList.hpp>
  36. #ifdef ndbODBC
  37. #include <NdbApi.hpp>
  38. #endif
  39. #include <sqlext.h>
  40. #undef BOOL
  41. #include <NdbMain.h>
  42. #include <NdbOut.hpp>
  43. #include <NdbThread.h>
  44. #include <NdbMutex.h>
  45. #include <NdbCondition.h>
  46. #include <NdbTick.h>
  47. #include <NdbSleep.h>
  48. #ifdef ndbODBC
  49. #include <NdbTest.hpp>
  50. #else
  51. #define NDBT_OK 0
  52. #define NDBT_FAILED 1
  53. #define NDBT_WRONGARGS 2
  54. static int
  55. NDBT_ProgramExit(int rcode)
  56. {
  57.     const char* rtext = "Unknown";
  58.     switch (rcode) {
  59.     case NDBT_OK:
  60. rtext = "OK";
  61. break;
  62.     case NDBT_FAILED:
  63. rtext = "Failed";
  64. break;
  65.     case NDBT_WRONGARGS:
  66. rtext = "Wrong arguments";
  67. break;
  68.     };
  69.     ndbout_c("nNDBT_ProgramExit: %d - %sn", rcode, rtext);
  70.     return rcode;
  71. }
  72. #endif
  73. #ifdef DMALLOC
  74. #include <dmalloc.h>
  75. #endif
  76. #define arraySize(x) (sizeof(x)/sizeof(x[0]))
  77. #define SQL_ATTR_NDB_TUPLES_FETCHED 66601
  78. // options
  79. #define MAX_THR 128 // max threads
  80. struct Opt {
  81.     const char* m_name[100];
  82.     unsigned m_namecnt;
  83.     bool m_core;
  84.     unsigned m_depth;
  85.     const char* m_dsn;
  86.     unsigned m_errs;
  87.     const char* m_fragtype;
  88.     unsigned m_frob;
  89.     const char* m_home;
  90.     unsigned m_loop;
  91.     bool m_nogetd;
  92.     bool m_noputd;
  93.     bool m_nosort;
  94.     unsigned m_scale;
  95.     bool m_serial;
  96.     const char* m_skip[100];
  97.     unsigned m_skipcnt;
  98.     unsigned m_subloop;
  99.     const char* m_table;
  100.     unsigned m_threads;
  101.     unsigned m_trace;
  102.     unsigned m_v;
  103.     Opt() :
  104. m_namecnt(0),
  105. m_core(false),
  106. m_depth(5),
  107. m_dsn("NDB"),
  108. m_errs(0),
  109.         m_fragtype(0),
  110.         m_frob(0),
  111. m_home(0),
  112. m_loop(1),
  113. m_nogetd(false),
  114. m_noputd(false),
  115. m_nosort(false),
  116. m_scale(100),
  117. m_serial(false),
  118. m_skipcnt(0),
  119. m_subloop(1),
  120. m_table(0),
  121. m_threads(1),
  122. m_trace(0),
  123. m_v(1) {
  124. for (unsigned i = 0; i < arraySize(m_name); i++)
  125.     m_name[i] = 0;
  126. for (unsigned i = 0; i < arraySize(m_skip); i++)
  127.     m_skip[i] = 0;
  128.     }
  129. };
  130. static Opt opt;
  131. static void listCases();
  132. static void listTables();
  133. static void printusage()
  134. {
  135.     Opt d;
  136.     ndbout
  137. << "usage: testOdbcDriver [options]" << endl
  138. << "-case name  run only named tests (substring match - can be repeated)" << endl
  139. << "-core       dump core on failure" << endl
  140. << "-depth N    join depth - default " << d.m_depth << endl
  141. << "-dsn string data source name - default " << d.m_dsn << endl
  142. << "-errs N     allow N errors before quitting - default " << d.m_errs << endl
  143.         << "-fragtype t fragment type single/small/medium/large" << d.m_errs << endl
  144.         << "-frob X     case-dependent tweak (number)" << endl
  145. << "-home dir   set NDB_HOME (contains Ndb.cfg)" << endl
  146. << "-loop N     loop N times (0 = forever) - default " << d.m_loop << endl
  147. << "-nogetd     do not use SQLGetData - default " << d.m_nogetd << endl
  148. << "-noputd     do not use SQLPutData - default " << d.m_noputd << endl
  149. << "-nosort     no order-by in verify scan (checks non-Pk values only)" << endl
  150. << "-scale N    row count etc - default " << d.m_scale << endl
  151. << "-serial     run multi-threaded test cases one at a time" << endl
  152. << "-skip name  skip named tests (substring match - can be repeated)" << endl
  153. << "-subloop N  loop count per case (same threads) - default " << d.m_subloop << endl
  154. << "-table T    do only table T (table name on built-in list)" << endl
  155. << "-threads N  number of threads (max " << MAX_THR << ") - default " << d.m_threads << endl
  156. << "-trace N    trace in NDB ODBC driver - default " << d.m_trace << endl
  157. << "-v N        verbosity - default " << d.m_v << endl
  158. ;
  159.     listCases();
  160.     listTables();
  161. }
  162. static void
  163. fatal(const char* fmt, ...)
  164. {
  165.     va_list ap;
  166.     char buf[200];
  167.     va_start(ap, fmt);
  168.     vsnprintf(buf, sizeof(buf), fmt, ap);
  169.     va_end(ap);
  170.     ndbout << buf << endl;
  171.     if (opt.m_errs != 0) {
  172. opt.m_errs--;
  173. return;
  174.     }
  175.     if (opt.m_core)
  176. abort();
  177.     NDBT_ProgramExit(NDBT_FAILED);
  178.     exit(1);
  179. }
  180. static void
  181. cleanprint(const char* s, unsigned n)
  182. {
  183.     for (unsigned i = 0; i < n; i++) {
  184. char b[10];
  185. if (0x20 < s[i] && s[i] <= 0x7e)
  186.     sprintf(b, "%c", s[i]);
  187. else
  188.     sprintf(b, "\%02x", (unsigned)s[i]);
  189. ndbout << b;
  190.     }
  191. }
  192. // global mutex
  193. static NdbMutex my_mutex = NDB_MUTEX_INITIALIZER;
  194. static void lock_mutex() { NdbMutex_Lock(&my_mutex); }
  195. static void unlock_mutex() { NdbMutex_Unlock(&my_mutex); }
  196. // semaphore zeroed before each call to a test routine
  197. static unsigned my_sema = 0;
  198. // print mutex
  199. static NdbMutex out_mutex = NDB_MUTEX_INITIALIZER;
  200. static NdbOut& lock(NdbOut& out) { NdbMutex_Lock(&out_mutex); return out; }
  201. static NdbOut& unlock(NdbOut& out) { NdbMutex_Unlock(&out_mutex); return out; }
  202. static unsigned
  203. urandom(unsigned n)
  204. {
  205.     assert(n != 0);
  206.     unsigned i = random();
  207.     return i % n;
  208. }
  209. // test cases
  210. struct Test;
  211. struct Case {
  212.     enum Mode {
  213. Single = 1, // single thread
  214. Serial = 2, // all threads but one at a time
  215. Thread = 3 // all threads in parallel
  216.     };
  217.     const char* m_name;
  218.     void (*m_func)(Test& test);
  219.     Mode m_mode;
  220.     unsigned m_stuff;
  221.     const char* m_desc;
  222.     Case(const char* name, void (*func)(Test& test), Mode mode, unsigned stuff, const char* desc) :
  223. m_name(name),
  224. m_func(func),
  225. m_mode(mode),
  226. m_stuff(stuff),
  227. m_desc(desc) {
  228.     }
  229.     const char* modename() const {
  230. const char* s = "?";
  231. if (m_mode == Case::Single)
  232.     return "Single";
  233. if (m_mode == Case::Serial)
  234.     return "Serial";
  235. if (m_mode == Case::Thread)
  236.     return "Thread";
  237. return "?";
  238.     }
  239.     bool matchcase() const {
  240. if (opt.m_namecnt == 0)
  241.     return ! skipcase();
  242. for (unsigned i = 0; i < opt.m_namecnt; i++) {
  243.     if (strstr(m_name, opt.m_name[i]) != 0)
  244. return ! skipcase();
  245. }
  246. return false;
  247.     }
  248. private:
  249.     bool skipcase() const {
  250. for (unsigned i = 0; i < opt.m_skipcnt; i++) {
  251.     if (strstr(m_name, opt.m_skip[i]) != 0)
  252. return true;
  253. }
  254. return false;
  255.     }
  256. };
  257. // calculate values
  258. struct Calc {
  259.     enum { m_mul = 1000000 };
  260.     unsigned m_no;
  261.     unsigned m_base;
  262.     unsigned m_salt; // modifies non-PK values
  263.     bool m_const; // base non-PK values on PK of row 0
  264.     Calc(unsigned no) :
  265. m_no(no),
  266. m_salt(0),
  267. m_const(false) {
  268. m_base = m_no * m_mul;
  269.     }
  270.     void calcPk(unsigned rownum, char* v, unsigned n) const {
  271. char b[10];
  272. sprintf(b, "%08x", m_base + rownum);
  273. for (unsigned i = 0; i < n; i++) {
  274.     char c = i < n - 1 ? b[i % 8] : 0;
  275.     v[i] = c;
  276. }
  277.     }
  278.     void calcPk(unsigned rownum, long* v) const {
  279. *v = m_base + rownum;
  280.     }
  281.     void hashPk(unsigned* hash, const char* v, unsigned n) const {
  282. for (unsigned i = 0; i < n; i++) {
  283.     *hash ^= (v[i] << i);
  284. }
  285.     }
  286.     void hashPk(unsigned* hash, long v) const {
  287. *hash ^= v;
  288.     }
  289.     void calcNk(unsigned hash, char* v, unsigned n, SQLINTEGER* ind, bool null) const {
  290. unsigned m = hash % n;
  291. for (unsigned i = 0; i < n; i++) {
  292.     char c = i < m ? 'a' + (hash + i) % ('z' - 'a' + 1) : i < n - 1 ? ' ' : 0;
  293.     v[i] = c;
  294. }
  295. *ind = null && hash % 9 == 0 ? SQL_NULL_DATA : SQL_NTS;
  296.     }
  297.     void calcNk(unsigned hash, long* v, SQLINTEGER* ind, bool null) const {
  298. *v = long(hash);
  299. *ind = null && hash % 7 == 0 ? SQL_NULL_DATA : 0;
  300.     }
  301.     void calcNk(unsigned hash, double* v, SQLINTEGER* ind, bool null) const {
  302. *v = long(hash) / 1000.0;
  303. *ind = null && hash % 5 == 0 ? SQL_NULL_DATA : 0;
  304.     }
  305.     bool verify(const char* v1, SQLINTEGER ind1, const char* v2, SQLINTEGER ind2, unsigned n) {
  306. if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA)
  307.     return true;
  308. if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA)
  309.     if (memcmp(v1, v2, n) == 0)
  310. return true;
  311. if (ind1 == SQL_NULL_DATA)
  312.     v1 = "NULL";
  313. if (ind2 == SQL_NULL_DATA)
  314.     v2 = "NULL";
  315. ndbout << "verify failed: got ";
  316. if (ind1 == SQL_NULL_DATA)
  317.     ndbout << "NULL";
  318. else
  319.     cleanprint(v1, n);
  320. ndbout << " != ";
  321. if (ind2 == SQL_NULL_DATA)
  322.     ndbout << "NULL";
  323. else
  324.     cleanprint(v2, n);
  325. ndbout << endl;
  326. return false;
  327.     }
  328.     bool verify(long v1, SQLINTEGER ind1, long v2, SQLINTEGER ind2) {
  329. char buf1[40], buf2[40];
  330. if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA)
  331.     return true;
  332. if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA)
  333.     if (v1 == v2)
  334. return true;
  335. if (ind1 == SQL_NULL_DATA)
  336.     strcpy(buf1, "NULL");
  337. else
  338.     sprintf(buf1, "%ld", v1);
  339. if (ind2 == SQL_NULL_DATA)
  340.     strcpy(buf2, "NULL");
  341. else
  342.     sprintf(buf2, "%ld", v2);
  343. ndbout << "verify failed: got " << buf1 << " != " << buf2 << endl;
  344. return false;
  345.     }
  346.     bool verify(double v1, SQLINTEGER ind1, double v2, SQLINTEGER ind2) {
  347. char buf1[40], buf2[40];
  348. if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA)
  349.     return true;
  350. if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA)
  351.     if (fabs(v1 - v2) < 1) // XXX
  352. return true;
  353. if (ind1 == SQL_NULL_DATA)
  354.     strcpy(buf1, "NULL");
  355. else
  356.     sprintf(buf1, "%.10f", v1);
  357. if (ind2 == SQL_NULL_DATA)
  358.     strcpy(buf2, "NULL");
  359. else
  360.     sprintf(buf2, "%.10f", v2);
  361. ndbout << "verify failed: got " << buf1 << " != " << buf2 << endl;
  362. return false;
  363.     }
  364. };
  365. #if defined(NDB_SOLARIS) || defined(NDB_LINUX) || defined(NDB_MACOSX)
  366. #define HAVE_SBRK
  367. #else
  368. #undef HAVE_SBRK
  369. #endif
  370. struct Timer {
  371.     Timer() :
  372. m_cnt(0),
  373. m_calls(0),
  374. m_on(0),
  375. m_msec(0)
  376. #ifndef NDB_WIN32
  377. ,
  378. m_brk(0),
  379. m_incr(0)
  380. #endif
  381. {
  382.     }
  383.     void timerOn() {
  384. m_cnt = 0;
  385. m_calls = 0;
  386. m_on = NdbTick_CurrentMillisecond();
  387. #ifdef HAVE_SBRK
  388. m_brk = (int)sbrk(0);
  389. #endif
  390.     }
  391.     void timerOff() {
  392. m_msec = NdbTick_CurrentMillisecond() - m_on;
  393. if (m_msec <= 0)
  394.     m_msec = 1;
  395. #ifdef HAVE_SBRK
  396. m_incr = (int)sbrk(0) - m_brk;
  397. if (m_incr < 0)
  398.     m_incr = 0;
  399. #endif
  400.     }
  401.     void timerCnt(unsigned cnt) {
  402. m_cnt += cnt;
  403.     }
  404.     void timerCnt(const Timer& timer) {
  405. m_cnt += timer.m_cnt;
  406. m_calls += timer.m_calls;
  407.     }
  408.     friend NdbOut& operator<<(NdbOut& out, const Timer& timer) {
  409. out << timer.m_cnt << " ( " << 1000 * timer.m_cnt / timer.m_msec << "/sec )";
  410. #ifdef HAVE_SBRK
  411. out << " - " << timer.m_incr << " sbrk";
  412. if (opt.m_namecnt != 0) { // per case meaningless if many cases
  413.     if (timer.m_cnt > 0)
  414. out << " ( " << timer.m_incr / timer.m_cnt << "/cnt )";
  415. }
  416. #endif
  417. out << " - " << timer.m_calls << " calls";
  418. return out;
  419.     }
  420. protected:
  421.     unsigned m_cnt; // count rows or whatever
  422.     unsigned m_calls; // count ODBC function calls
  423.     NDB_TICKS m_on;
  424.     unsigned m_msec;
  425. #ifdef HAVE_SBRK
  426.     int m_brk;
  427.     int m_incr;
  428. #endif
  429. };
  430. #define MAX_MESSAGE 500
  431. #define MAX_DIAG 20
  432. struct Diag {
  433.     char m_state[5+1];
  434.     SQLINTEGER m_native;
  435.     char m_message[MAX_MESSAGE];
  436.     unsigned m_flag; // temp use
  437.     Diag() {
  438. strcpy(m_state, "00000");
  439. m_native = 0;
  440. memset(m_message, 0, sizeof(m_message));
  441. m_flag = 0;
  442.     }
  443.     const char* text() {
  444. snprintf(m_buf, sizeof(m_buf), "%s %d '%s'", m_state, (int)m_native, m_message);
  445. return m_buf;
  446.     }
  447.     void getDiag(SQLSMALLINT type, SQLHANDLE handle, unsigned k, unsigned count) {
  448. int ret;
  449. SQLSMALLINT length = -1;
  450. memset(m_message, 0, MAX_MESSAGE);
  451. ret = SQLGetDiagRec(type, handle, k, (SQLCHAR*)m_state, &m_native, (SQLCHAR*)m_message, MAX_MESSAGE, &length);
  452. if (k <= count && ret != SQL_SUCCESS)
  453.     fatal("SQLGetDiagRec %d of %d: return %d != SQL_SUCCESS", k, count, (int)ret);
  454. if (k <= count && strlen(m_message) != length)
  455.     fatal("SQLGetDiagRec %d of %d: message length %d != %d", k, count, strlen(m_message), length);
  456. if (k > count && ret != SQL_NO_DATA)
  457.     fatal("SQLGetDiagRec %d of %d: return %d != SQL_NO_DATA", k, count, (int)ret);
  458. m_flag = 0;
  459.     }
  460. private:
  461.     char m_buf[MAX_MESSAGE];
  462. };
  463. struct Diags {
  464.     Diag m_diag[MAX_DIAG];
  465.     SQLINTEGER m_diagCount;
  466.     SQLINTEGER m_rowCount;
  467.     SQLINTEGER m_functionCode;
  468.     void getDiags(SQLSMALLINT type, SQLHANDLE handle) {
  469. int ret;
  470. m_diagCount = -1;
  471. ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_NUMBER, &m_diagCount, SQL_IS_INTEGER, 0);
  472. if (ret == SQL_INVALID_HANDLE)
  473.     return;
  474. if (ret != SQL_SUCCESS)
  475.     fatal("SQLGetDiagField: return %d != SQL_SUCCESS", (int)ret);
  476. if (m_diagCount < 0 || m_diagCount > MAX_DIAG)
  477.     fatal("SQLGetDiagField: count %d", (int)m_diagCount);
  478. for (unsigned k = 0; k < MAX_DIAG; k++) {
  479.     m_diag[k].getDiag(type, handle, k + 1, m_diagCount);
  480.     if (k == m_diagCount + 1)
  481. break;
  482. }
  483. m_rowCount = -1;
  484. m_functionCode = SQL_DIAG_UNKNOWN_STATEMENT;
  485. if (type == SQL_HANDLE_STMT) {
  486.     ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_ROW_COUNT, &m_rowCount, SQL_IS_INTEGER, 0);
  487. #ifndef iODBC
  488.     if (ret != SQL_SUCCESS)
  489. fatal("SQLGetDiagField: return %d != SQL_SUCCESS", (int)ret);
  490. #endif
  491.     ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_DYNAMIC_FUNCTION_CODE, &m_functionCode, SQL_IS_INTEGER, 0);
  492. }
  493.     }
  494.     void showDiags() {
  495. for (unsigned k = 0; 0 <= m_diagCount && k < m_diagCount; k++) {
  496.     Diag& diag = m_diag[k];
  497.     ndbout << "diag " << k + 1;
  498.     ndbout << (diag.m_flag ? " [*]" : " [ ]");
  499.     ndbout << " " << diag.text() << endl;
  500.     if (k > 10)
  501. abort();
  502. }
  503.     }
  504. };
  505. struct Exp {
  506.     int m_ret;
  507.     const char* m_state;
  508.     SQLINTEGER m_native;
  509.     Exp() : m_ret(SQL_SUCCESS), m_state(""), m_native(0) {}
  510.     Exp(int ret, const char* state) : m_ret(ret), m_state(state) {}
  511. };
  512. struct Test : Calc, Timer, Diags {
  513.     Test(unsigned no, unsigned loop) :
  514. Calc(no),
  515. m_loop(loop),
  516. m_stuff(0),
  517. m_perf(false),
  518. ccp(0) {
  519. exp(SQL_SUCCESS, 0, 0, true);
  520.     }
  521.     unsigned m_loop; // current loop
  522.     Exp m_expList[20]; // expected results
  523.     unsigned m_expCount;
  524.     int m_ret; // actual return code
  525.     int m_stuff; // the stuff of abuse
  526.     bool m_perf; // check no diags on success
  527.     const Case* ccp; // current case
  528.     void exp(int ret, const char* state, SQLINTEGER native, bool reset) {
  529. if (reset)
  530.     m_expCount = 0;
  531. unsigned i = m_expCount++;
  532. assert(i < arraySize(m_expList) - 1);
  533. m_expList[i].m_ret = ret;
  534. m_expList[i].m_state = state == 0 ? "" : state;
  535. m_expList[i].m_native = native;
  536.     }
  537.     void runCase(const Case& cc) {
  538. ccp = &cc;
  539. if (opt.m_v >= 3)
  540.     ndbout << cc.m_name << ": start" << endl;
  541. m_rowCount = -1;
  542. NDB_TICKS m_ms1 = NdbTick_CurrentMillisecond();
  543. m_salt = m_loop | (16 << cc.m_stuff);
  544. m_const = cc.m_stuff == 0;
  545. m_stuff = cc.m_stuff;
  546. (*cc.m_func)(*this);
  547. NDB_TICKS m_ms2 = NdbTick_CurrentMillisecond();
  548.     }
  549.     void run(SQLSMALLINT type, SQLHANDLE handle, int line, int ret) {
  550. m_calls++;
  551. m_ret = ret;
  552. if (m_perf && (m_ret == SQL_SUCCESS))
  553.     return;
  554. m_diagCount = 0;
  555. if (handle != SQL_NULL_HANDLE)
  556.     getDiags(type, handle);
  557. if (m_diagCount <= 0 && (ret != SQL_SUCCESS && ret != SQL_INVALID_HANDLE && ret != SQL_NEED_DATA && ret != SQL_NO_DATA)) {
  558.     fatal("%s: thr %d line %d: ret=%d but no diag records", ccp->m_name, m_no, line, ret);
  559. }
  560. for (unsigned k = 0; 0 <= m_diagCount && k < m_diagCount; k++) {
  561.     Diag& diag = m_diag[k];
  562.     bool match = false;
  563.     for (unsigned i = 0; i < m_expCount; i++) {
  564. 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)) {
  565.     match = true;
  566.     diag.m_flag = 0;
  567.     continue;
  568. }
  569. diag.m_flag = 1; // mark unexpected
  570.     }
  571.     if (! match) {
  572. showDiags();
  573. fatal("%s: thr %d line %d: unexpected diag [*] ret=%d cnt=%d", ccp->m_name, m_no, line, (int)ret, (int)m_diagCount);
  574.     }
  575. }
  576. bool match = false;
  577. for (unsigned i = 0; i < m_expCount; i++) {
  578.     if (ret == m_expList[i].m_ret) {
  579. match = true;
  580. break;
  581.     }
  582. }
  583. if (! match) {
  584.     showDiags();
  585.     fatal("%s: thr %d line %d: ret=%d not expected", ccp->m_name, m_no, line, ret);
  586. }
  587. // reset expected to success
  588. exp(SQL_SUCCESS, 0, 0, true);
  589.     }
  590.     void chk(SQLSMALLINT type, SQLHANDLE handle, int line, bool match, const char* fmt, ...) {
  591. if (match)
  592.     return;
  593. va_list ap;
  594. va_start(ap, fmt);
  595. char buf[500];
  596. vsnprintf(buf, sizeof(buf), fmt, ap);
  597. va_end(ap);
  598. fatal("%s: thr %d line %d: check failed - %s", ccp->m_name, m_no, line, buf);
  599.     }
  600. };
  601. #define HNull 0, SQL_NULL_HANDLE, __LINE__
  602. #define HEnv(h) SQL_HANDLE_ENV, h, __LINE__
  603. #define HDbc(h) SQL_HANDLE_DBC, h, __LINE__
  604. #define HStmt(h) SQL_HANDLE_STMT, h, __LINE__
  605. #define HDesc(h) SQL_HANDLE_DESC, h, __LINE__
  606. // string support
  607. #define MAX_SQL 20000
  608. static void
  609. scopy(char*& ptr, const char* fmt, ...)
  610. {
  611.     va_list ap;
  612.     va_start(ap, fmt);
  613.     vsprintf(ptr, fmt, ap);
  614.     va_end(ap);
  615.     ptr += strlen(ptr);
  616. }
  617. static bool
  618. blankeq(const char* s1, const char* s2, bool caseSensitive = false)
  619. {
  620.     unsigned n1 = strlen(s1);
  621.     unsigned n2 = strlen(s2);
  622.     unsigned i = 0;
  623.     char c1 = 0;
  624.     char c2 = 0;
  625.     while (i < n1 || i < n2) {
  626. c1 = i < n1 ? s1[i] : 0x20;
  627. if (! caseSensitive && 'a' <= c1 && c1 <= 'z')
  628.     c1 -= 'a' - 'A';
  629. c2 = i < n2 ? s2[i] : 0x20;
  630. if (! caseSensitive && 'a' <= c2 && c2 <= 'z')
  631.     c2 -= 'a' - 'A';
  632. if (c1 != c2)
  633.     break;
  634. i++;
  635.     }
  636.     return c1 == c2;
  637. }
  638. // columns and tables
  639. struct Col {
  640.     enum Type {
  641. Char = SQL_CHAR,
  642. Varchar = SQL_VARCHAR,
  643. Int = SQL_INTEGER,
  644. Bigint = SQL_BIGINT,
  645. Real = SQL_REAL,
  646. Double = SQL_DOUBLE
  647.     };
  648.     enum CType {
  649. CChar = SQL_C_CHAR,
  650. CLong = SQL_C_SLONG,
  651. CDouble = SQL_C_DOUBLE
  652.     };
  653.     enum Cons {
  654. Null, // nullable
  655. NotNull, // not nullable
  656. Pk // part of primary key
  657.     };
  658.     const char* m_name;
  659.     Type m_type;
  660.     unsigned m_length;
  661.     Cons m_cons;
  662.     CType m_ctype;
  663.     Col() :
  664. m_type((Type)999) {
  665.     }
  666.     Col(const char* name, Type type, unsigned length, Cons cons, CType ctype) :
  667. m_name(name),
  668. m_type(type),
  669. m_length(length),
  670. m_cons(cons),
  671. m_ctype(ctype) {
  672.     }
  673.     unsigned size() const {
  674. switch (m_type) {
  675. case Char:
  676. case Varchar:
  677.     return m_length;
  678. case Int:
  679.     return 4;
  680. case Bigint:
  681.     return 8;
  682. case Real:
  683.     return 4;
  684. case Double:
  685.     return 8;
  686. }
  687. assert(false);
  688. return 0;
  689.     }
  690.     unsigned csize() const { // size as char plus terminating null
  691. switch (m_ctype) {
  692. case CChar:
  693.     return m_length + 1;
  694. case CLong:
  695.     return 12;
  696. case CDouble:
  697.     return 24;
  698. }
  699. assert(false);
  700. return 0;
  701.     }
  702.     void typespec(char*& ptr) const {
  703. switch (m_type) {
  704. case Char:
  705.     scopy(ptr, "char(%d)", m_length);
  706.     return;
  707. case Varchar:
  708.     scopy(ptr, "varchar(%d)", m_length);
  709.     return;
  710. case Int:
  711.     scopy(ptr, "int");
  712.     return;
  713. case Bigint:
  714.     scopy(ptr, "bigint");
  715.     return;
  716. case Real:
  717.     scopy(ptr, "real");
  718.     return;
  719. case Double:
  720.     scopy(ptr, "float");
  721.     return;
  722. }
  723. assert(false);
  724.     }
  725.     SQLSMALLINT type() const {
  726. return (SQLSMALLINT)m_type;
  727.     }
  728.     SQLSMALLINT ctype() const {
  729. return (SQLSMALLINT)m_ctype;
  730.     }
  731.     void create(char*& ptr, bool pk) const {
  732. scopy(ptr, "%s", m_name);
  733. scopy(ptr, " ");
  734. typespec(ptr);
  735. if (m_cons == Pk && pk) {
  736.     scopy(ptr, " primary key");
  737. }
  738. if (m_cons == NotNull) {
  739.     scopy(ptr, " not null");
  740. }
  741.     }
  742. };
  743. static Col ColUndef;
  744. struct Tab {
  745.     const char* m_name;
  746.     const Col* m_colList;
  747.     unsigned m_colCount;
  748.     unsigned m_pkCount;
  749.     unsigned* m_pkIndex;
  750.     unsigned m_nkCount;
  751.     unsigned* m_nkIndex;
  752.     char m_upperName[20];
  753.     Tab(const char* name, const Col* colList, unsigned colCount) :
  754. m_name(name),
  755. m_colList(colList),
  756. m_colCount(colCount) {
  757. m_pkCount = 0;
  758. m_nkCount = 0;
  759. for (unsigned i = 0; i < m_colCount; i++) {
  760.     const Col& col = m_colList[i];
  761.     if (col.m_cons == Col::Pk)
  762. m_pkCount++;
  763.     else
  764. m_nkCount++;
  765. }
  766. m_pkIndex = new unsigned[m_pkCount];
  767. m_nkIndex = new unsigned[m_nkCount];
  768. unsigned pk = 0;
  769. unsigned nk = 0;
  770. for (unsigned i = 0; i < m_colCount; i++) {
  771.     const Col& col = m_colList[i];
  772.     if (col.m_cons == Col::Pk)
  773. m_pkIndex[pk++] = i;
  774.     else
  775. m_nkIndex[nk++] = i;
  776. }
  777. assert(pk == m_pkCount && nk == m_nkCount);
  778. strcpy(m_upperName, m_name);
  779. for (char* p = m_upperName; *p != 0; p++) {
  780.     if ('a' <= *p && *p <= 'z')
  781. *p -= 'a' - 'A';
  782. }
  783.     }
  784.     ~Tab() {
  785. delete[] m_pkIndex;
  786. delete[] m_nkIndex;
  787.     }
  788.     void drop(char*& ptr) const {
  789. scopy(ptr, "drop table %s", m_name);
  790.     }
  791.     void create(char*& ptr) const {
  792. scopy(ptr, "create table %s (", m_name);
  793. for (unsigned i = 0; i < m_colCount; i++) {
  794.     if (i > 0)
  795. scopy(ptr, ", ");
  796.     const Col& col = m_colList[i];
  797.     col.create(ptr, m_pkCount == 1);
  798. }
  799. if (m_pkCount != 1) {
  800.     scopy(ptr, ", primary key (");
  801.     for (unsigned i = 0; i < m_pkCount; i++) {
  802. const Col& col = m_colList[m_pkIndex[i]];
  803. if (i > 0)
  804.     scopy(ptr, ", ");
  805. scopy(ptr, "%s", col.m_name);
  806.     }
  807.     scopy(ptr, ")");
  808. }
  809. scopy(ptr, ")");
  810.     }
  811.     void wherePk(char*& ptr) const {
  812. scopy(ptr, " where");
  813. for (unsigned i = 0; i < m_pkCount; i++) {
  814.     const Col& col = m_colList[m_pkIndex[i]];
  815.     if (i > 0)
  816. scopy(ptr, " and");
  817.     scopy(ptr, " %s = ?", col.m_name);
  818. }
  819.     }
  820.     void whereRange(char*& ptr) const {
  821. scopy(ptr, " where");
  822. for (unsigned i = 0; i < m_pkCount; i++) {
  823.     const Col& col = m_colList[m_pkIndex[i]];
  824.     if (i > 0)
  825. scopy(ptr, " and");
  826.     scopy(ptr, " ? <= %s", col.m_name);
  827.     scopy(ptr, " and ");
  828.     scopy(ptr, "%s < ?", col.m_name);
  829. }
  830.     }
  831.     void orderPk(char*& ptr) const {
  832. scopy(ptr, " order by");
  833. for (unsigned i = 0; i < m_pkCount; i++) {
  834.     const Col& col = m_colList[m_pkIndex[i]];
  835.     if (i > 0)
  836. scopy(ptr, ", ");
  837.     else
  838. scopy(ptr, " ");
  839.     scopy(ptr, "%s", col.m_name);
  840. }
  841.     }
  842.     void selectPk(char*& ptr) const {
  843. scopy(ptr, "select * from %s", m_name);
  844. wherePk(ptr);
  845.     }
  846.     void selectAll(char*& ptr) const {
  847. scopy(ptr, "select * from %s", m_name);
  848.     }
  849.     void selectRange(char*& ptr, bool sort) const {
  850. selectAll(ptr);
  851. whereRange(ptr);
  852. if (sort)
  853.     orderPk(ptr);
  854.     }
  855.     void selectCount(char*& ptr) const {
  856. scopy(ptr, "select count(*) from %s", m_name);
  857.     }
  858.     void insertAll(char*& ptr) const {
  859. scopy(ptr, "insert into %s values (", m_name);
  860. for (unsigned i = 0; i < m_colCount; i++) {
  861.     if (i > 0)
  862. scopy(ptr, ", ");
  863.     scopy(ptr, "?");
  864. }
  865. scopy(ptr, ")");
  866.     }
  867.     void updatePk(char*& ptr) const {
  868. scopy(ptr, "update %s set", m_name);
  869. for (unsigned i = 0; i < m_nkCount; i++) {
  870.     const Col& col = m_colList[m_nkIndex[i]];
  871.     if (i > 0)
  872. scopy(ptr, ", ");
  873.     else
  874. scopy(ptr, " ");
  875.     scopy(ptr, "%s = ?", col.m_name);
  876. }
  877. wherePk(ptr);
  878.     }
  879.     void updateRange(char*& ptr) const {
  880. scopy(ptr, "update %s set", m_name);
  881. for (unsigned i = 0; i < m_nkCount; i++) {
  882.     const Col& col = m_colList[m_nkIndex[i]];
  883.     if (i > 0)
  884. scopy(ptr, ", ");
  885.     else
  886. scopy(ptr, " ");
  887.     scopy(ptr, "%s = ?", col.m_name); // XXX constant for now
  888. }
  889. whereRange(ptr);
  890.     }
  891.     void deleteAll(char*& ptr) const {
  892. scopy(ptr, "delete from %s", m_name);
  893.     }
  894.     void deletePk(char*& ptr) const {
  895. scopy(ptr, "delete from %s", m_name);
  896. wherePk(ptr);
  897.     }
  898.     void deleteRange(char*& ptr) const {
  899. scopy(ptr, "delete from %s", m_name);
  900. whereRange(ptr);
  901.     }
  902.     // simple
  903.     void insertDirect(char*& ptr, unsigned n) const {
  904. scopy(ptr, "insert into %s values (", m_name);
  905. for (unsigned i = 0; i < m_colCount; i++) {
  906.     const Col& col = m_colList[i];
  907.     if (i > 0)
  908. scopy(ptr, ", ");
  909.     if (col.m_type == Col::Char || col.m_type == Col::Varchar) {
  910. scopy(ptr, "'");
  911. for (unsigned i = 0; i <= n % col.m_length; i++)
  912.     scopy(ptr, "%c", 'a' + (n + i) % 26);
  913. scopy(ptr, "'");
  914.     } else if (col.m_type == Col::Int || col.m_type == Col::Bigint) {
  915. scopy(ptr, "%u", n);
  916.     } else if (col.m_type == Col::Real || col.m_type == Col::Double) {
  917. scopy(ptr, "%.3f", n * 0.001);
  918.     } else {
  919. assert(false);
  920.     }
  921. }
  922. scopy(ptr, ")");
  923.     }
  924.     void whereDirect(char*& ptr, unsigned n) const {
  925. scopy(ptr, " where");
  926. for (unsigned i = 0; i < m_pkCount; i++) {
  927.     const Col& col = m_colList[m_pkIndex[i]];
  928.     if (i > 0)
  929. scopy(ptr, ", ");
  930.     else
  931. scopy(ptr, " ");
  932.     scopy(ptr, "%s = ", col.m_name);
  933.     if (col.m_type == Col::Char || col.m_type == Col::Varchar) {
  934. scopy(ptr, "'");
  935. for (unsigned i = 0; i <= n % col.m_length; i++)
  936.     scopy(ptr, "%c", 'a' + (n + i) % 26);
  937. scopy(ptr, "'");
  938.     } else if (col.m_type == Col::Int || col.m_type == Col::Bigint) {
  939. scopy(ptr, "%u", n);
  940.     } else {
  941. assert(false);
  942.     }
  943. }
  944.     }
  945.     void countDirect(char*& ptr, unsigned n) const {
  946. scopy(ptr, "select count(*) from %s", m_name);
  947. whereDirect(ptr, n);
  948.     }
  949.     void deleteDirect(char*& ptr, unsigned n) const {
  950. scopy(ptr, "delete from %s", m_name);
  951. whereDirect(ptr, n);
  952.     }
  953.     // joins
  954.     void selectCart(char*& ptr, unsigned cnt) const {
  955. scopy(ptr, "select count(*) from");
  956. for (unsigned j = 0; j < cnt; j++) {
  957.     if (j > 0)
  958. scopy(ptr, ",");
  959.     scopy(ptr, " %s", m_name);
  960.     scopy(ptr, " t%u", j);
  961. }
  962.     }
  963.     void selectJoin(char*& ptr, unsigned cnt) const {
  964. scopy(ptr, "select * from");
  965. for (unsigned j = 0; j < cnt; j++) {
  966.     if (j > 0)
  967. scopy(ptr, ",");
  968.     scopy(ptr, " %s", m_name);
  969.     scopy(ptr, " t%u", j);
  970. }
  971. for (unsigned i = 0; i < m_pkCount; i++) {
  972.     const Col& col = m_colList[m_pkIndex[i]];
  973.     for (unsigned j = 0; j < cnt - 1; j++) {
  974. if (i == 0 && j == 0)
  975.     scopy(ptr, " where");
  976. else
  977.     scopy(ptr, " and");
  978. scopy(ptr, " t%u.%s = t%u.%s", j, col.m_name, j + 1, col.m_name);
  979.     }
  980. }
  981.     }
  982.     // check if selected on command line
  983.     bool optok() const {
  984. return opt.m_table == 0 || strcasecmp(m_name, opt.m_table) == 0;
  985.     }
  986. };
  987. // the test tables
  988. static Col col0[] = {
  989.     Col( "a", Col::Bigint, 0, Col::Pk, Col::CLong ),
  990.     Col( "b", Col::Int, 0, Col::NotNull, Col::CLong ),
  991.     Col( "c", Col::Char, 4, Col::NotNull, Col::CChar ),
  992.     Col( "d", Col::Double, 0, Col::Null, Col::CDouble ),
  993.     Col( "e", Col::Char, 40, Col::Null, Col::CChar ),
  994.     Col( "f", Col::Char, 10, Col::Null, Col::CChar )
  995. };
  996. static Col col1[] = {
  997.     Col( "c0", Col::Int, 0, Col::Pk, Col::CLong ),
  998.     Col( "c1", Col::Int, 0, Col::NotNull, Col::CLong ),
  999.     Col( "c2", Col::Int, 0, Col::NotNull, Col::CLong ),
  1000.     Col( "c3", Col::Int, 0, Col::NotNull, Col::CLong ),
  1001.     Col( "c4", Col::Int, 0, Col::NotNull, Col::CLong ),
  1002.     Col( "c5", Col::Int, 0, Col::NotNull, Col::CLong ),
  1003.     Col( "c6", Col::Int, 0, Col::NotNull, Col::CLong ),
  1004.     Col( "c7", Col::Int, 0, Col::NotNull, Col::CLong ),
  1005.     Col( "c8", Col::Int, 0, Col::NotNull, Col::CLong ),
  1006.     Col( "c9", Col::Int, 0, Col::NotNull, Col::CLong ),
  1007.     Col( "c10", Col::Int, 0, Col::NotNull, Col::CLong ),
  1008.     Col( "c11", Col::Int, 0, Col::NotNull, Col::CLong ),
  1009.     Col( "c12", Col::Int, 0, Col::NotNull, Col::CLong ),
  1010.     Col( "c13", Col::Int, 0, Col::NotNull, Col::CLong ),
  1011.     Col( "c14", Col::Int, 0, Col::NotNull, Col::CLong ),
  1012.     Col( "c15", Col::Int, 0, Col::NotNull, Col::CLong ),
  1013.     Col( "c16", Col::Int, 0, Col::NotNull, Col::CLong ),
  1014.     Col( "c17", Col::Int, 0, Col::NotNull, Col::CLong ),
  1015.     Col( "c18", Col::Int, 0, Col::NotNull, Col::CLong ),
  1016.     Col( "c19", Col::Int, 0, Col::NotNull, Col::CLong ),
  1017.     Col( "c20", Col::Int, 0, Col::NotNull, Col::CLong ),
  1018.     Col( "c21", Col::Int, 0, Col::NotNull, Col::CLong ),
  1019.     Col( "c22", Col::Int, 0, Col::NotNull, Col::CLong ),
  1020.     Col( "c23", Col::Int, 0, Col::NotNull, Col::CLong ),
  1021.     Col( "c24", Col::Int, 0, Col::NotNull, Col::CLong ),
  1022.     Col( "c25", Col::Int, 0, Col::NotNull, Col::CLong )
  1023. };
  1024. static Col col2[] = {
  1025.     Col( "a", Col::Int, 0, Col::Pk, Col::CLong ),
  1026.     Col( "c", Col::Char, 8000, Col::NotNull, Col::CChar )
  1027. };
  1028. static Col col3[] = {
  1029.     Col( "a", Col::Int, 0, Col::Pk, Col::CLong ),
  1030.     Col( "c1", Col::Varchar, 1, Col::Null, Col::CChar ),
  1031.     Col( "c2", Col::Varchar, 2, Col::Null, Col::CChar ),
  1032.     Col( "c3", Col::Varchar, 3, Col::Null, Col::CChar ),
  1033.     Col( "c4", Col::Varchar, 4, Col::Null, Col::CChar ),
  1034.     Col( "c5", Col::Varchar, 10, Col::Null, Col::CChar ),
  1035.     Col( "c6", Col::Varchar, 40, Col::Null, Col::CChar ),
  1036.     Col( "c7", Col::Varchar, 255, Col::Null, Col::CChar ),
  1037.     Col( "c8", Col::Varchar, 4000, Col::Null, Col::CChar )
  1038. };
  1039. static Col col4[] = {
  1040.     Col( "a", Col::Char, 8, Col::Pk, Col::CChar ),
  1041.     Col( "b", Col::Char, 8, Col::NotNull, Col::CChar ),
  1042. };
  1043. static Tab tabList[] = {
  1044. #define colList(x) x, arraySize(x)
  1045.     Tab( "tt00", colList(col0) ),
  1046.     Tab( "tt01", colList(col1) ), // fl鋝kbench special
  1047.     Tab( "tt02", colList(col2) ),
  1048.     Tab( "tt03", colList(col3) ),
  1049.     Tab( "tt04", colList(col4) )
  1050. #undef colList
  1051. };
  1052. static const unsigned tabCount = arraySize(tabList);
  1053. static const unsigned maxColCount = 100; // per table - keep up to date
  1054. static bool
  1055. findTable()
  1056. {
  1057.     for (unsigned i = 0; i < tabCount; i++) {
  1058. const Tab& tab = tabList[i];
  1059. if (tab.optok())
  1060.     return true;
  1061.     }
  1062.     return false;
  1063. }
  1064. static void
  1065. listTables()
  1066. {
  1067.     ndbout << "tables:" << endl;
  1068.     for (unsigned i = 0; i < tabCount; i++) {
  1069. const Tab& tab = tabList[i];
  1070. if (i > 0)
  1071.     ndbout << " ";
  1072. ndbout << tab.m_name;
  1073.     }
  1074.     ndbout << endl;
  1075. }
  1076. // data fields and rows
  1077. struct Fld {
  1078.     const Col& m_col;
  1079.     union {
  1080. char* m_char;
  1081. long m_long;
  1082. double m_double;
  1083.     };
  1084.     SQLINTEGER m_ind;
  1085.     SQLINTEGER m_need; // constant
  1086.     Fld() :
  1087. m_col(ColUndef),
  1088. m_need(0) {
  1089.     }
  1090.     Fld(const Col& col) :
  1091. m_col(col),
  1092. m_need(SQL_LEN_DATA_AT_EXEC(0)) {
  1093. switch (m_col.m_ctype) {
  1094. case Col::CChar:
  1095.     m_char = new char[m_col.csize()];
  1096.     memset(m_char, 0, m_col.csize());
  1097.     break;
  1098. case Col::CLong:
  1099.     m_long = 0;
  1100.     break;
  1101. case Col::CDouble:
  1102.     m_double = 0.0;
  1103.     break;
  1104. }
  1105. m_ind = -1;
  1106.     }
  1107.     ~Fld() {
  1108. switch (m_col.m_ctype) {
  1109. case Col::CChar:
  1110.     delete[] m_char;
  1111.     break;
  1112. case Col::CLong:
  1113.     break;
  1114. case Col::CDouble:
  1115.     break;
  1116. }
  1117.     }
  1118.     void zero() {
  1119. switch (m_col.m_ctype) {
  1120. case Col::CChar:
  1121.     memset(m_char, 0x1f, m_col.csize());
  1122.     break;
  1123. case Col::CLong:
  1124.     m_long = 0x1f1f1f1f;
  1125.     break;
  1126. case Col::CDouble:
  1127.     m_double = 1111111.1111111;
  1128.     break;
  1129. }
  1130. m_ind = -1;
  1131.     }
  1132.     // copy values from another field
  1133.     void copy(const Fld& fld) {
  1134. assert(&m_col == &fld.m_col);
  1135. switch (m_col.m_ctype) {
  1136. case Col::CChar:
  1137.     memcpy(m_char, fld.m_char, m_col.csize());
  1138.     break;
  1139. case Col::CLong:
  1140.     m_long = fld.m_long;
  1141.     break;
  1142. case Col::CDouble:
  1143.     m_double = fld.m_double;
  1144.     break;
  1145. default:
  1146.     assert(false);
  1147.     break;
  1148. }
  1149. m_ind = fld.m_ind;
  1150.     }
  1151.     SQLPOINTER caddr() {
  1152. switch (m_col.m_ctype) {
  1153. case Col::CChar:
  1154.     return (SQLPOINTER)m_char;
  1155. case Col::CLong:
  1156.     return (SQLPOINTER)&m_long;
  1157. case Col::CDouble:
  1158.     return (SQLPOINTER)&m_double;
  1159. }
  1160. assert(false);
  1161. return 0;
  1162.     }
  1163.     SQLINTEGER* ind() {
  1164. return &m_ind;
  1165.     }
  1166.     SQLINTEGER* need() {
  1167. m_need = SQL_LEN_DATA_AT_EXEC(0);
  1168. return &m_need;
  1169.     }
  1170.     void calcPk(const Test& test, unsigned rownum) {
  1171. switch (m_col.m_ctype) {
  1172. case Col::CChar:
  1173.     test.calcPk(rownum, m_char, m_col.csize());
  1174.     m_ind = SQL_NTS;
  1175.     return;
  1176. case Col::CLong:
  1177.     test.calcPk(rownum, &m_long);
  1178.     m_ind = 0;
  1179.     return;
  1180. case Col::CDouble:
  1181.     assert(false);
  1182.     return;
  1183. }
  1184. assert(false);
  1185.     }
  1186.     void hashPk(const Test& test, unsigned* hash) const {
  1187. switch (m_col.m_ctype) {
  1188. case Col::CChar:
  1189.     test.hashPk(hash, m_char, m_col.csize());
  1190.     return;
  1191. case Col::CLong:
  1192.     test.hashPk(hash, m_long);
  1193.     return;
  1194. case Col::CDouble:
  1195.     assert(false);
  1196.     return;
  1197. }
  1198. assert(false);
  1199.     }
  1200.     void calcNk(const Test& test, unsigned hash) {
  1201. bool null = m_col.m_cons == Col::Null;
  1202. switch (m_col.m_ctype) {
  1203. case Col::CChar:
  1204.     test.calcNk(hash, m_char, m_col.csize(), &m_ind, null);
  1205.     return;
  1206. case Col::CLong:
  1207.     test.calcNk(hash, &m_long, &m_ind, null);
  1208.     return;
  1209. case Col::CDouble:
  1210.     test.calcNk(hash, &m_double, &m_ind, null);
  1211.     return;
  1212. }
  1213. assert(false);
  1214.     }
  1215.     bool verify(Test& test, const Fld& fld) {
  1216. assert(&m_col == &fld.m_col);
  1217. switch (m_col.m_ctype) {
  1218. case Col::CChar:
  1219.     return test.verify(m_char, m_ind, fld.m_char, fld.m_ind, m_col.csize());
  1220. case Col::CLong:
  1221.     return test.verify(m_long, m_ind, fld.m_long, fld.m_ind);
  1222. case Col::CDouble:
  1223.     return test.verify(m_double, m_ind, fld.m_double, fld.m_ind);
  1224. }
  1225. assert(false);
  1226. return false;
  1227.     }
  1228.     // debug
  1229.     void print() const {
  1230. if (m_ind == SQL_NULL_DATA)
  1231.     ndbout << "NULL";
  1232. else {
  1233.     switch (m_col.m_ctype) {
  1234.     case Col::CChar:
  1235. ndbout << m_char;
  1236. break;
  1237.     case Col::CLong:
  1238. ndbout << (int)m_long;
  1239. break;
  1240.     case Col::CDouble:
  1241. ndbout << m_double;
  1242. break;
  1243.     }
  1244. }
  1245.     }
  1246. };
  1247. struct Row {
  1248.     const Tab& m_tab;
  1249.     Fld* m_fldList;
  1250.     Row(const Tab& tab) :
  1251. m_tab(tab) {
  1252. m_fldList = new Fld[m_tab.m_colCount];
  1253. for (unsigned i = 0; i < m_tab.m_colCount; i++) {
  1254.     const Col& col = m_tab.m_colList[i];
  1255.     void* place = &m_fldList[i];
  1256.     new (place) Fld(col);
  1257. }
  1258.     }
  1259.     ~Row() {
  1260. delete[] m_fldList;
  1261.     }
  1262.     // copy values from another row
  1263.     void copy(const Row& row) {
  1264. assert(&m_tab == &row.m_tab);
  1265. for (unsigned i = 0; i < m_tab.m_colCount; i++) {
  1266.     Fld& fld = m_fldList[i];
  1267.     fld.copy(row.m_fldList[i]);
  1268. }
  1269.     }
  1270.     // primary key value is determined by row number
  1271.     void calcPk(Test& test, unsigned rownum) {
  1272. for (unsigned i = 0; i < m_tab.m_pkCount; i++) {
  1273.     Fld& fld = m_fldList[m_tab.m_pkIndex[i]];
  1274.     fld.calcPk(test, rownum);
  1275. }
  1276.     }
  1277.     // other fields are determined by primary key value
  1278.     void calcNk(Test& test) {
  1279. unsigned hash = test.m_salt;
  1280. for (unsigned i = 0; i < m_tab.m_pkCount; i++) {
  1281.     Fld& fld = m_fldList[m_tab.m_pkIndex[i]];
  1282.     fld.hashPk(test, &hash);
  1283. }
  1284. for (unsigned i = 0; i < m_tab.m_colCount; i++) {
  1285.     const Col& col = m_tab.m_colList[i];
  1286.     if (col.m_cons == Col::Pk)
  1287. continue;
  1288.     Fld& fld = m_fldList[i];
  1289.     fld.calcNk(test, hash);
  1290. }
  1291.     }
  1292.     // verify against another row
  1293.     bool verifyPk(Test& test, const Row& row) const {
  1294. assert(&m_tab == &row.m_tab);
  1295. for (unsigned i = 0; i < m_tab.m_pkCount; i++) {
  1296.     Fld& fld = m_fldList[m_tab.m_pkIndex[i]];
  1297.     if (! fld.verify(test, row.m_fldList[m_tab.m_pkIndex[i]])) {
  1298. ndbout << "verify failed: tab=" << m_tab.m_name << " col=" << fld.m_col.m_name << endl;
  1299. return false;
  1300.     }
  1301. }
  1302. return true;
  1303.     }
  1304.     bool verifyNk(Test& test, const Row& row) const {
  1305. assert(&m_tab == &row.m_tab);
  1306. for (unsigned i = 0; i < m_tab.m_nkCount; i++) {
  1307.     Fld& fld = m_fldList[m_tab.m_nkIndex[i]];
  1308.     if (! fld.verify(test, row.m_fldList[m_tab.m_nkIndex[i]])) {
  1309. ndbout << "verify failed: tab=" << m_tab.m_name << " col=" << fld.m_col.m_name << endl;
  1310. return false;
  1311.     }
  1312. }
  1313. return true;
  1314.     }
  1315.     bool verify(Test& test, const Row& row) const {
  1316. return verifyPk(test, row) && verifyNk(test, row);
  1317.     }
  1318.     // debug
  1319.     void print() const {
  1320. ndbout << "row";
  1321. for (unsigned i = 0; i < m_tab.m_colCount; i++) {
  1322.     ndbout << " " << i << "=";
  1323.     Fld& fld = m_fldList[i];
  1324.     fld.print();
  1325. }
  1326. ndbout << endl;
  1327.     }
  1328. };
  1329. // set ODBC version - required
  1330. static void
  1331. setVersion(Test& test, SQLHANDLE hEnv)
  1332. {
  1333.     test.run(HEnv(hEnv), SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER));
  1334. }
  1335. // set autocommit
  1336. static void
  1337. setAutocommit(Test& test, SQLHANDLE hDbc, bool on)
  1338. {
  1339.     SQLUINTEGER value = on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
  1340.     test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)value, SQL_IS_UINTEGER));
  1341.     SQLUINTEGER value2 = (SQLUINTEGER)-1;
  1342.     test.run(HDbc(hDbc), SQLGetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&value2, SQL_IS_UINTEGER, 0));
  1343.     test.chk(HDbc(hDbc), value2 == value, "got %u != %u", (unsigned)value2, (unsigned)value);
  1344. }
  1345. // subroutines - migrate simple common routines here
  1346. static void
  1347. allocEnv(Test& test, SQLHANDLE& hEnv)
  1348. {
  1349.     test.run(HNull, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv));
  1350.     setVersion(test, hEnv);
  1351. }
  1352. static void
  1353. allocDbc(Test& test, SQLHANDLE hEnv, SQLHANDLE& hDbc)
  1354. {
  1355.     test.run(HEnv(hEnv), SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
  1356. }
  1357. static void
  1358. allocConn(Test& test, SQLHANDLE hEnv, SQLHANDLE& hDbc)
  1359. {
  1360.     allocDbc(test, hEnv, hDbc);
  1361. #ifdef unixODBC
  1362.     test.exp(SQL_SUCCESS_WITH_INFO, "IM003", 0, false); // unicode??
  1363.     test.exp(SQL_SUCCESS_WITH_INFO, "01000", 0, false); // version??
  1364. #endif
  1365.     test.run(HDbc(hDbc), SQLConnect(hDbc, (SQLCHAR*)opt.m_dsn, SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS));
  1366. }
  1367. static void
  1368. allocStmt(Test& test, SQLHANDLE hDbc, SQLHANDLE& hStmt)
  1369. {
  1370.     test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
  1371. }
  1372. static void
  1373. allocAll(Test& test, SQLHANDLE& hEnv, SQLHANDLE& hDbc, SQLHANDLE& hStmt)
  1374. {
  1375.     allocEnv(test, hEnv);
  1376.     allocConn(test, hEnv, hDbc);
  1377.     allocStmt(test, hDbc, hStmt);
  1378. }
  1379. static void
  1380. allocAll(Test& test, SQLHANDLE& hEnv, SQLHANDLE& hDbc, SQLHANDLE* hStmtList, unsigned nStmt)
  1381. {
  1382.     allocEnv(test, hEnv);
  1383.     allocConn(test, hEnv, hDbc);
  1384.     for (unsigned i = 0; i < nStmt; i++)
  1385. allocStmt(test, hDbc, hStmtList[i]);
  1386. }
  1387. static void
  1388. freeEnv(Test& test, SQLHANDLE hEnv)
  1389. {
  1390.     test.run(HNull, SQLFreeHandle(SQL_HANDLE_ENV, hEnv));
  1391. }
  1392. static void
  1393. freeDbc(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc)
  1394. {
  1395.     test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc));
  1396. }
  1397. static void
  1398. freeConn(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc)
  1399. {
  1400.     test.run(HDbc(hDbc), SQLDisconnect(hDbc));
  1401.     test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc));
  1402. }
  1403. static void
  1404. freeStmt(Test& test, SQLHANDLE hDbc, SQLHANDLE hStmt)
  1405. {
  1406.     test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_STMT, hStmt));
  1407. }
  1408. static void
  1409. freeAll(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc, SQLHANDLE hStmt)
  1410. {
  1411.     freeStmt(test, hDbc, hStmt);
  1412.     freeConn(test, hEnv, hDbc);
  1413.     freeEnv(test, hEnv);
  1414. }
  1415. static void
  1416. freeAll(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc, SQLHANDLE* hStmtList, unsigned nStmt)
  1417. {
  1418.     for (unsigned i = 0; i < nStmt; i++)
  1419. freeStmt(test, hDbc, hStmtList[i]);
  1420.     freeConn(test, hEnv, hDbc);
  1421.     freeEnv(test, hEnv);
  1422. }
  1423. #define chkTuplesFetched(/*Test&*/ _test, /*SQLHANDLE*/ _hStmt, /*SQLUINTEGER*/ _countExp) 
  1424. do { 
  1425.     SQLUINTEGER _count = (SQLUINTEGER)-1; 
  1426.     getTuplesFetched(_test, _hStmt, &_count); 
  1427.     test.chk(HStmt(_hStmt), _count == _countExp, "tuples: got %ld != %ld", (long)_count, (long)_countExp); 
  1428. } while (0)
  1429. static void
  1430. getTuplesFetched(Test& test, SQLHANDLE hStmt, SQLUINTEGER* count)
  1431. {
  1432.     *count = (SQLUINTEGER)-1;
  1433.     test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_NDB_TUPLES_FETCHED, count, SQL_IS_POINTER, 0));
  1434. }
  1435. static void
  1436. selectCount(Test& test, SQLHANDLE hStmt, const char* sql, long* count)
  1437. {
  1438.     if (opt.m_v >= 3)
  1439. ndbout << "SQL: " << sql << endl;
  1440.     test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  1441.     SQLINTEGER ind;
  1442.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, count, 0, &ind));
  1443.     ind = -1;
  1444.     *count = -1;
  1445.     test.run(HStmt(hStmt), SQLExecute(hStmt));
  1446.     unsigned k = 0;
  1447.     while (1) {
  1448. if (k == 1)
  1449.     test.exp(SQL_NO_DATA, 0, 0, true);
  1450. test.run(HStmt(hStmt), SQLFetch(hStmt));
  1451. if (k == 1)
  1452.     break;
  1453. k++;
  1454.     }
  1455.     test.chk(HStmt(hStmt), ind == sizeof(long), "got %d != %d", (int)ind, (int)sizeof(long));
  1456.     test.chk(HStmt(hStmt), *count >= 0, "got %ld < 0", *count);
  1457.     chkTuplesFetched(test, hStmt, *count);
  1458. #ifndef iODBC
  1459.     //
  1460.     test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  1461. #else
  1462.     test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_CLOSE));
  1463. #endif
  1464. }
  1465. static void
  1466. selectCount(Test& test, SQLHANDLE hStmt, const Tab& tab, long* count)
  1467. {
  1468.     static char sql[MAX_SQL], *sqlptr; // XXX static or core
  1469.     tab.selectCount(sqlptr = sql);
  1470.     selectCount(test, hStmt, sql, count);
  1471. }
  1472. static void
  1473. verifyCount(Test& test, SQLHANDLE hStmt, const Tab& tab, long countExp)
  1474. {
  1475.     long count = -1;
  1476.     selectCount(test, hStmt, tab, &count);
  1477.     test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp);
  1478. }
  1479. #define chkRowCount(/*Test&*/ _test, /*SQLHANDLE*/ _hStmt, /*SQLINTEGER*/ _countExp) 
  1480. do { 
  1481.     SQLINTEGER _count = -1; 
  1482.     getRowCount(_test, _hStmt, &_count); 
  1483.     test.chk(HStmt(_hStmt), _count == _countExp, "rowcount: got %ld != %ld", (long)_count, (long)_countExp); 
  1484. } while (0)
  1485. static void
  1486. getRowCount(Test& test, SQLHANDLE hStmt, SQLINTEGER* count)
  1487. {
  1488.     *count = -1;
  1489.     test.run(HStmt(hStmt), SQLRowCount(hStmt, count));
  1490. }
  1491. // handle allocation
  1492. static void
  1493. testAlloc(Test& test)
  1494. {
  1495.     const unsigned n1 = (opt.m_scale >> 8) & 0xf; // default 500 = 0x1f4
  1496.     const unsigned n2 = (opt.m_scale >> 4) & 0xf;
  1497.     const unsigned n3 = (opt.m_scale >> 0) & 0xf;
  1498.     const unsigned count = n1 + n1 * n2 + n1 * n2 * n3;
  1499.     SQLHANDLE hEnvList[0xf];
  1500.     SQLHANDLE hDbcList[0xf][0xf];
  1501.     SQLHANDLE hStmtList[0xf][0xf][0xf];
  1502.     for (unsigned i1 = 0; i1 < n1; i1++) {
  1503. SQLHANDLE& hEnv = hEnvList[i1];
  1504. test.run(HNull, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv));
  1505. test.run(HEnv(hEnv), SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER));
  1506. for (unsigned i2 = 0; i2 < n2; i2++) {
  1507.     SQLHANDLE& hDbc = hDbcList[i1][i2];
  1508.     test.run(HEnv(hEnv), SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
  1509. #ifdef unixODBC
  1510.     test.exp(SQL_SUCCESS_WITH_INFO, "IM003", 0, false); // unicode??
  1511.     test.exp(SQL_SUCCESS_WITH_INFO, "01000", 0, false); // version??
  1512. #endif
  1513.     test.run(HDbc(hDbc), SQLConnect(hDbc, (SQLCHAR*)opt.m_dsn, SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS));
  1514.     // some attributes
  1515.     test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute
  1516.     test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_AUTO_IPD, (SQLPOINTER)SQL_TRUE, SQL_IS_UINTEGER));
  1517.     test.exp(SQL_ERROR, "HYC00", -1, true); // not supported
  1518.     test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)SQL_TXN_SERIALIZABLE, SQL_IS_UINTEGER));
  1519.     test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)"DEFAULT", strlen("DEFAULT")));
  1520.     for (unsigned i3 = 0; i3 < n3; i3++) {
  1521. SQLHANDLE& hStmt = hStmtList[i1][i2][i3];
  1522. test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
  1523. SQLHANDLE ipd0, ipd1;
  1524. SQLHANDLE ird0, ird1;
  1525. SQLHANDLE apd0, apd1, apd2;
  1526. SQLHANDLE ard0, ard1, ard2;
  1527. // get
  1528. ipd0 = ird0 = apd0 = ard0 = 0;
  1529. test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, &ipd0, SQL_IS_POINTER, 0));
  1530. test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, &ird0, SQL_IS_POINTER, 0));
  1531. test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &apd0, SQL_IS_POINTER, 0));
  1532. test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, &ard0, SQL_IS_POINTER, 0));
  1533. #ifndef unixODBC
  1534. test.chk(HStmt(hStmt), ipd0 != 0, "got 0");
  1535. test.chk(HStmt(hStmt), ird0 != 0, "got 0");
  1536. test.chk(HStmt(hStmt), apd0 != 0, "got 0");
  1537. test.chk(HStmt(hStmt), ard0 != 0, "got 0");
  1538. #endif
  1539. // alloc
  1540. ipd1 = ird1 = apd1 = ard1 = 0;
  1541. test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ipd1));
  1542. test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ird1));
  1543. test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &apd1));
  1544. test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ard1));
  1545. test.chk(HDbc(hDbc), ipd1 != 0 && ird1 != 0 && apd1 != 0 && ard1 != 0, "got null");
  1546. // set
  1547. test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute
  1548. test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, ipd1, SQL_IS_POINTER));
  1549. test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute
  1550. test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, ird1, SQL_IS_POINTER));
  1551. test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, apd1, SQL_IS_POINTER));
  1552. test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, ard1, SQL_IS_POINTER));
  1553. // get
  1554. apd2 = ard2 = 0;
  1555. test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &apd2, SQL_IS_POINTER, 0));
  1556. test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, &ard2, SQL_IS_POINTER, 0));
  1557. test.chk(HStmt(hStmt), apd2 == apd1, "got %x != %x", (unsigned)apd2, (unsigned)apd1);
  1558. test.chk(HStmt(hStmt), ard2 == ard1, "got %x != %x", (unsigned)ard2, (unsigned)ard1);
  1559. // free
  1560. test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ipd1));
  1561. test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ird1));
  1562. test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, apd1));
  1563. test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ard1));
  1564.     }
  1565. }
  1566.     }
  1567.     test.timerCnt(count);
  1568.     if (opt.m_v >= 3)
  1569. ndbout << "allocated " << count << endl;
  1570.     for (unsigned i1 = 0; i1 < n1; i1++) {
  1571. SQLHANDLE& hEnv = hEnvList[i1];
  1572. for (unsigned i2 = 0; i2 < n2; i2++) {
  1573.     SQLHANDLE& hDbc = hDbcList[i1][i2];
  1574.     if (i2 % 2 == 0) {
  1575. for (unsigned i3 = 0; i3 < n3; i3++) {
  1576.     SQLHANDLE& hStmt = hStmtList[i1][i2][i3];
  1577.     test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_STMT, hStmt));
  1578. }
  1579.     } else {
  1580. // cleaned up by SQLDisconnect
  1581.     }
  1582.     test.run(HDbc(hDbc), SQLDisconnect(hDbc));
  1583.     test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc));
  1584. }
  1585. test.run(HNull, SQLFreeHandle(SQL_HANDLE_ENV, hEnv));
  1586.     }
  1587.     test.timerCnt(count);
  1588.     if (opt.m_v >= 3)
  1589. ndbout << "freed " << count << endl;
  1590. }
  1591. // create tables
  1592. static void
  1593. testCreate(Test& test)
  1594. {
  1595.     SQLHANDLE hEnv, hDbc, hStmt;
  1596.     allocAll(test, hEnv, hDbc, hStmt);
  1597.     char sql[MAX_SQL], *sqlptr;
  1598.     for (unsigned i = 0; i < tabCount; i++) {
  1599. Tab& tab = tabList[i];
  1600. if (! tab.optok())
  1601.     continue;
  1602. // drop
  1603. tab.drop(sqlptr = sql);
  1604. if (opt.m_v >= 2)
  1605.     ndbout << "SQL: " << sql << endl;
  1606. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  1607. test.exp(SQL_ERROR, "IM000", 2040709, false);
  1608. test.run(HStmt(hStmt), SQLExecute(hStmt));
  1609. if (test.m_ret == SQL_SUCCESS)
  1610.     test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DROP_TABLE, "got %d != %d", test.m_functionCode, SQL_DIAG_DROP_TABLE);
  1611. if (test.m_ret == SQL_SUCCESS && opt.m_v >= 2)
  1612.     ndbout << "table " << tab.m_name << " dropped" << endl;
  1613. if (test.m_ret != SQL_SUCCESS && opt.m_v >= 2)
  1614.     ndbout << "table " << tab.m_name << " does not exist" << endl;
  1615. test.timerCnt(1);
  1616. // create
  1617. tab.create(sqlptr = sql);
  1618. if (opt.m_v >= 2)
  1619.     ndbout << "SQL: " << sql << endl;
  1620. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  1621. test.exp(SQL_ERROR, "IM000", 2040721, false);
  1622. test.run(HStmt(hStmt), SQLExecute(hStmt));
  1623. if (test.m_ret == SQL_SUCCESS)
  1624.     test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_CREATE_TABLE, "got %d != %d", test.m_functionCode, SQL_DIAG_CREATE_TABLE);
  1625. if (test.m_ret == SQL_SUCCESS && opt.m_v >= 2)
  1626.     ndbout << "table " << tab.m_name << " created" << endl;
  1627. if (test.m_ret != SQL_SUCCESS && opt.m_v >= 2)
  1628.     ndbout << "table " << tab.m_name << " already exists" << endl;
  1629. test.timerCnt(1);
  1630.     }
  1631.     freeAll(test, hEnv, hDbc, hStmt);
  1632. }
  1633. // prepare without execute
  1634. static void
  1635. testPrepare(Test& test)
  1636. {
  1637.     SQLHANDLE hEnv, hDbc, hStmt;
  1638.     allocAll(test, hEnv, hDbc, hStmt);
  1639.     char sql[MAX_SQL], *sqlptr;
  1640.     for (unsigned cnt = opt.m_depth; cnt <= opt.m_depth; cnt++) {
  1641. for (unsigned i = 0; i < tabCount; i++) {
  1642.     Tab& tab = tabList[i];
  1643.     if (! tab.optok())
  1644. continue;
  1645.     tab.selectJoin(sqlptr = sql, cnt);
  1646.     if (opt.m_v >= 2)
  1647. ndbout << "SQL: " << sql << endl;
  1648.     test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  1649.     SQLSMALLINT colCount = -1;
  1650.     SQLSMALLINT colExp = cnt * tab.m_colCount;
  1651.     test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
  1652.     test.chk(HStmt(hStmt), colCount == colExp, "got %d != %d", (int)colCount, (int)colExp);
  1653.     test.timerCnt(1);
  1654. }
  1655.     }
  1656.     freeAll(test, hEnv, hDbc, hStmt);
  1657. }
  1658. // catalog functions
  1659. static void
  1660. testCatalog(Test& test)
  1661. {
  1662.     SQLHANDLE hEnv, hDbc, hStmt;
  1663.     allocAll(test, hEnv, hDbc, hStmt);
  1664.     odbc_typeinfo: {
  1665. long type[] = {
  1666.     SQL_CHAR, SQL_VARCHAR, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_REAL, SQL_DOUBLE
  1667. };
  1668. unsigned rows[] = {
  1669.     1, 1, 2, 2, 2, 1, 1 // 2 for signed and unsigned
  1670. };
  1671. for (unsigned i = 0; i < arraySize(type); i++) {
  1672.     test.run(HStmt(hStmt), SQLGetTypeInfo(hStmt, type[i]));
  1673.     long dataType = 0;
  1674.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SLONG, &dataType, 0, 0));
  1675.     unsigned k = 0;
  1676.     while (1) {
  1677. if (k == rows[i])
  1678.     test.exp(SQL_NO_DATA, 0, 0, true);
  1679. test.run(HStmt(hStmt), SQLFetch(hStmt));
  1680. if (k == rows[i])
  1681.     break;
  1682. test.chk(HStmt(hStmt), dataType == type[i], "got %ld != %ld", dataType, type[i]);
  1683. test.timerCnt(1);
  1684. k++;
  1685.     }
  1686. #ifndef iODBC
  1687.     test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  1688. #else
  1689.     freeStmt(test, hDbc, hStmt);
  1690.     allocStmt(test, hDbc, hStmt);
  1691. #endif
  1692. }
  1693. if (opt.m_v >= 2)
  1694.     ndbout << "found " << (UintPtr)arraySize(type) << " data types" << endl;
  1695. test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
  1696.     }
  1697.     odbc_tables: {
  1698. unsigned found[tabCount];
  1699. for (unsigned i = 0; i < tabCount; i++)
  1700.     found[i] = 0;
  1701. test.run(HStmt(hStmt), SQLTables(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0));
  1702. char tableName[200] = "";
  1703. char tableType[200] = "";
  1704. test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0));
  1705. test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, tableType, sizeof(tableType), 0));
  1706. unsigned cnt = 0;
  1707. while (1) {
  1708.     test.exp(SQL_NO_DATA, 0, 0, false);
  1709.     test.run(HStmt(hStmt), SQLFetch(hStmt));
  1710.     if (test.m_ret == SQL_NO_DATA)
  1711. break;
  1712.     test.timerCnt(1);
  1713.     cnt++;
  1714.     if (! blankeq(tableType, "TABLE"))
  1715. continue;
  1716.     for (unsigned i = 0; i < tabCount; i++) {
  1717. const Tab& tab = tabList[i];
  1718. if (! tab.optok())
  1719.     continue;
  1720. if (! blankeq(tab.m_name, tableName))
  1721.     continue;
  1722. test.chk(HStmt(hStmt), found[i] == 0, "duplicate table %s", tab.m_name);
  1723. found[i]++;
  1724.     }
  1725. }
  1726. #ifndef iODBC
  1727. test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  1728. #else
  1729. freeStmt(test, hDbc, hStmt);
  1730. allocStmt(test, hDbc, hStmt);
  1731. #endif
  1732. for (unsigned i = 0; i < tabCount; i++) {
  1733.     const Tab& tab = tabList[i];
  1734.     if (! tab.optok())
  1735. continue;
  1736.     test.chk(HStmt(hStmt), found[i] == 1, "table %s not found", tab.m_name);
  1737. }
  1738. if (opt.m_v >= 2)
  1739.     ndbout << "found " << cnt << " tables" << endl;
  1740. test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
  1741.     }
  1742.     odbc_columns: {
  1743. unsigned found[tabCount][maxColCount];
  1744. for (unsigned i = 0; i < tabCount; i++) {
  1745.     for (unsigned j = 0; j < maxColCount; j++)
  1746. found[i][j] = 0;
  1747. }
  1748. test.run(HStmt(hStmt), SQLColumns(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0));
  1749. char tableName[200] = "";
  1750. char columnName[200] = "";
  1751. long dataType = 0;
  1752. test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0));
  1753. test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, columnName, sizeof(columnName), 0));
  1754. test.run(HStmt(hStmt), SQLBindCol(hStmt, 5, SQL_C_SLONG, &dataType, 0, 0));
  1755. unsigned cnt = 0;
  1756. while (1) {
  1757.     test.exp(SQL_NO_DATA, 0, 0, false);
  1758.     test.run(HStmt(hStmt), SQLFetch(hStmt));
  1759.     if (test.m_ret == SQL_NO_DATA)
  1760. break;
  1761.     test.timerCnt(1);
  1762.     cnt++;
  1763.     for (unsigned i = 0; i < tabCount; i++) {
  1764. const Tab& tab = tabList[i];
  1765. if (! tab.optok())
  1766.     continue;
  1767. if (! blankeq(tab.m_name, tableName))
  1768.     continue;
  1769. bool columnFound = false;
  1770. for (unsigned j = 0; j < tab.m_colCount; j++) {
  1771.     const Col& col = tab.m_colList[j];
  1772.     if (! blankeq(col.m_name, columnName))
  1773. continue;
  1774.     test.chk(HStmt(hStmt), found[i][j] == 0, "duplicate column %s.%s", tableName, columnName);
  1775.     found[i][j]++;
  1776.     columnFound = true;
  1777. }
  1778. test.chk(HStmt(hStmt), columnFound, "unknown column %s.%s", tableName, columnName);
  1779.     }
  1780. }
  1781. #ifndef iODBC
  1782. test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  1783. #else
  1784. freeStmt(test, hDbc, hStmt);
  1785. allocStmt(test, hDbc, hStmt);
  1786. #endif
  1787. for (unsigned i = 0; i < tabCount; i++) {
  1788.     const Tab& tab = tabList[i];
  1789.     if (! tab.optok())
  1790. continue;
  1791.     for (unsigned j = 0; j < tab.m_colCount; j++) {
  1792. const Col& col = tab.m_colList[j];
  1793. test.chk(HStmt(hStmt), found[i][j] == 1, "column %s.%s not found", tab.m_name, col.m_name);
  1794.     }
  1795. }
  1796. if (opt.m_v >= 2)
  1797.     ndbout << "found " << cnt << " columns" << endl;
  1798. test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
  1799.     }
  1800.     odbc_primarykeys: {
  1801. // table patterns are no allowed
  1802. for (unsigned i = 0; i < tabCount; i++) {
  1803.     const Tab& tab = tabList[i];
  1804.     if (! tab.optok())
  1805. continue;
  1806.     char tmp[200]; // p.i.t.a
  1807.     strcpy(tmp, tab.m_name);
  1808.     for (char* a = tmp; *a != 0; a++) {
  1809. if ('a' <= *a && *a <= 'z')
  1810.     *a -= 'a' - 'A';
  1811.     }
  1812.     test.run(HStmt(hStmt), SQLPrimaryKeys(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)tmp, SQL_NTS));
  1813.     char tableName[200] = "";
  1814.     char columnName[200] = "";
  1815.     long keySeq = -1;
  1816.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0));
  1817.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, columnName, sizeof(columnName), 0));
  1818.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 5, SQL_C_SLONG, &keySeq, 0, 0));
  1819.     unsigned cnt = 0;
  1820.     while (1) {
  1821. if (cnt == tab.m_pkCount)
  1822.     test.exp(SQL_NO_DATA, 0, 0, true);
  1823. test.run(HStmt(hStmt), SQLFetch(hStmt));
  1824. if (test.m_ret == SQL_NO_DATA)
  1825.     break;
  1826. test.chk(HStmt(hStmt), keySeq == 1 + cnt, "got %ld != %u", keySeq, 1 + cnt);
  1827. const Col& col = tab.m_colList[tab.m_pkIndex[keySeq - 1]];
  1828. test.chk(HStmt(hStmt), blankeq(columnName, col.m_name), "got %s != %s", columnName, col.m_name);
  1829. test.timerCnt(1);
  1830. cnt++;
  1831.     }
  1832. #ifndef iODBC
  1833.     test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  1834. #else
  1835.     freeStmt(test, hDbc, hStmt);
  1836.     allocStmt(test, hDbc, hStmt);
  1837. #endif
  1838. }
  1839. test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
  1840.     }
  1841.     freeAll(test, hEnv, hDbc, hStmt);
  1842. }
  1843. // insert
  1844. static void
  1845. testInsert(Test& test)
  1846. {
  1847.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  1848.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  1849.     char sql[MAX_SQL], *sqlptr;
  1850.     for (unsigned i = 0; i < tabCount; i++) {
  1851. SQLHANDLE& hStmt = hStmtList[i];
  1852. const Tab& tab = tabList[i];
  1853. if (! tab.optok())
  1854.     continue;
  1855. // prepare
  1856. tab.insertAll(sqlptr = sql);
  1857. if (opt.m_v >= 2)
  1858.     ndbout << "SQL: " << sql << endl;
  1859. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  1860. SQLSMALLINT parCount = -1;
  1861. test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
  1862. test.chk(HStmt(hStmt), parCount == tab.m_colCount, "got %d != %d", (int)parCount, (int)tab.m_colCount);
  1863. // bind parameters
  1864. Row row(tab);
  1865. for (unsigned j = 0; j < tab.m_colCount; j++) {
  1866.     Fld& fld = row.m_fldList[j];
  1867.     const Col& col = fld.m_col;
  1868.     // every other at-exec
  1869.     SQLPOINTER caddr;
  1870.     SQLINTEGER* ind;
  1871.     if (opt.m_noputd || j % 2 == 0) {
  1872. caddr = fld.caddr();
  1873. ind = fld.ind();
  1874.     } else {
  1875. caddr = (SQLPOINTER)j;
  1876. ind = fld.need();
  1877.     }
  1878.     test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, caddr, col.csize(), ind));
  1879. }
  1880. // bind columns (none)
  1881. SQLSMALLINT colCount = -1;
  1882. test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
  1883. test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount);
  1884. // execute
  1885. for (unsigned k = 0; k < opt.m_scale; k++) {
  1886.     if (k % 5 == 0) {
  1887. // rebind
  1888. unsigned j = 0;
  1889. Fld& fld = row.m_fldList[j];
  1890. const Col& col = fld.m_col;
  1891. // every other at-exec
  1892. SQLPOINTER caddr;
  1893. SQLINTEGER* ind;
  1894. if (opt.m_noputd || j % 2 == 0) {
  1895.     caddr = fld.caddr();
  1896.     ind = fld.ind();
  1897. } else {
  1898.     caddr = (SQLPOINTER)j;
  1899.     ind = fld.need();
  1900. }
  1901. test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, caddr, col.csize(), ind));
  1902.     }
  1903.     row.calcPk(test, k);
  1904.     row.calcNk(test);
  1905.     unsigned needData = opt.m_noputd ? 0 : tab.m_colCount / 2;
  1906.     if (needData)
  1907. test.exp(SQL_NEED_DATA, 0, 0, true);
  1908.     test.run(HStmt(hStmt), SQLExecute(hStmt));
  1909.     test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_INSERT, "got %d != %d", test.m_functionCode, SQL_DIAG_INSERT);
  1910.     if (needData) {
  1911. while (1) {
  1912.     SQLPOINTER jPtr = (SQLPOINTER)999;
  1913.     if (needData)
  1914. test.exp(SQL_NEED_DATA, 0, 0, true);
  1915.     // completes SQLExecute on success
  1916.     test.run(HStmt(hStmt), SQLParamData(hStmt, &jPtr));
  1917.     if (! needData)
  1918. break;
  1919.     unsigned j = (unsigned)jPtr;
  1920.     test.chk(HStmt(hStmt), j < tab.m_colCount && j % 2 != 0, "got %u 0x%x", j, j);
  1921.     Fld& fld = row.m_fldList[j];
  1922.     const Col& col = fld.m_col;
  1923.     SQLSMALLINT ctype = col.ctype();
  1924.     if (k % 2 == 0 || ctype != Col::CChar)
  1925. test.run(HStmt(hStmt), SQLPutData(hStmt, fld.caddr(), *fld.ind()));
  1926.     else {
  1927. // put in pieces
  1928. unsigned size = col.csize() - 1; // omit null terminator
  1929. char* caddr = (char*)(fld.caddr());
  1930. unsigned off = 0;
  1931. while (off < size) {
  1932.     unsigned m = size / 7; // bytes to put
  1933.     if (m == 0)
  1934. m = 1;
  1935.     if (m > size - off)
  1936. m = size - off;
  1937.     bool putNull = (*fld.ind() == SQL_NULL_DATA);
  1938.     // no null terminator
  1939.     SQLINTEGER len = putNull ? SQL_NULL_DATA : (int)m;
  1940.     test.run(HStmt(hStmt), SQLPutData(hStmt, caddr + off, len));
  1941.     if (putNull)
  1942. break;
  1943.     off += m;
  1944. }
  1945.     }
  1946.     needData--;
  1947. }
  1948.     }
  1949.     chkRowCount(test, hStmt, 1);
  1950.     chkTuplesFetched(test, hStmt, 0);
  1951. }
  1952. test.timerCnt(opt.m_scale);
  1953. if (opt.m_v >= 3)
  1954.     ndbout << "inserted " << opt.m_scale <<  " into " << tab.m_name << endl;
  1955.     }
  1956.     freeAll(test, hEnv, hDbc, hStmtList, tabCount);
  1957. }
  1958. // count
  1959. static void
  1960. testCount(Test& test)
  1961. {
  1962.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  1963.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  1964.     for (unsigned i = 0; i < tabCount; i++) {
  1965. SQLHANDLE& hStmt = hStmtList[i];
  1966. const Tab& tab = tabList[i];
  1967. if (! tab.optok())
  1968.     continue;
  1969. long count = -1;
  1970. selectCount(test, hStmt, tab, &count);
  1971. test.chk(HStmt(hStmt), count == opt.m_scale * opt.m_threads, "got %ld != %u", count, opt.m_scale * opt.m_threads);
  1972. test.timerCnt(count);
  1973. if (opt.m_v >= 3)
  1974.     ndbout << "counted " << (int)count <<  " rows in " << tab.m_name << endl;
  1975.     }
  1976.     // scan all at same time
  1977.     char sql[MAX_SQL], *sqlptr;
  1978.     for (unsigned i = 0; i < tabCount; i++) {
  1979. SQLHANDLE& hStmt = hStmtList[i];
  1980. const Tab& tab = tabList[i];
  1981. if (! tab.optok())
  1982.     continue;
  1983. tab.selectAll(sqlptr = sql);
  1984. test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
  1985.     }
  1986.     unsigned k = 0;
  1987.     while (1) {
  1988. for (unsigned i = 0; i < tabCount; i++) {
  1989.     SQLHANDLE& hStmt = hStmtList[i];
  1990.     const Tab& tab = tabList[i];
  1991.     if (! tab.optok())
  1992. continue;
  1993.     if (k == opt.m_scale * opt.m_threads)
  1994. test.exp(SQL_NO_DATA, 0, 0, true);
  1995.     test.run(HStmt(hStmt), SQLFetch(hStmt));
  1996.     if (k != opt.m_scale * opt.m_threads) {
  1997. chkTuplesFetched(test, hStmt, k + 1);
  1998. test.timerCnt(1);
  1999.     } else {
  2000. chkTuplesFetched(test, hStmt, k);
  2001. test.exp(SQL_NO_DATA, 0, 0, true);
  2002. test.run(HStmt(hStmt), SQLMoreResults(hStmt));
  2003.     }
  2004. }
  2005. if (k == opt.m_scale * opt.m_threads)
  2006.     break;
  2007. k++;
  2008.     }
  2009.     if (opt.m_v >= 3)
  2010. ndbout << "scanned " << opt.m_scale <<  " rows from each table" << endl;
  2011.     freeAll(test, hEnv, hDbc, hStmtList, tabCount);
  2012. }
  2013. // update
  2014. static void
  2015. testUpdatePk(Test& test)
  2016. {
  2017.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  2018.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  2019.     char sql[MAX_SQL], *sqlptr;
  2020.     for (unsigned i = 0; i < tabCount; i++) {
  2021. SQLHANDLE& hStmt = hStmtList[i];
  2022. const Tab& tab = tabList[i];
  2023. if (! tab.optok())
  2024.     continue;
  2025. // prepare
  2026. tab.updatePk(sqlptr = sql);
  2027. if (opt.m_v >= 2)
  2028.     ndbout << "SQL: " << sql << endl;
  2029. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  2030. // bind parameters
  2031. Row row(tab);
  2032. SQLSMALLINT parCount = -1;
  2033. test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
  2034. test.chk(HStmt(hStmt), parCount == tab.m_colCount, "got %d != %d", (int)parCount, (int)tab.m_colCount);
  2035. for (unsigned j = 0; j < tab.m_nkCount; j++) {
  2036.     Fld& fld = row.m_fldList[tab.m_nkIndex[j]];
  2037.     const Col& col = fld.m_col;
  2038.     test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
  2039. }
  2040. for (unsigned j = 0; j < tab.m_pkCount; j++) {
  2041.     Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
  2042.     const Col& col = fld.m_col;
  2043.     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()));
  2044. }
  2045. // bind columns (none)
  2046. SQLSMALLINT colCount = -1;
  2047. test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
  2048. test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount);
  2049. // execute
  2050. for (unsigned k = 0; k < opt.m_scale; k++) {
  2051.     if (k % 5 == 0) {
  2052. unsigned j = 0;
  2053. Fld& fld = row.m_fldList[tab.m_nkIndex[j]];
  2054. const Col& col = fld.m_col;
  2055. test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
  2056.     }
  2057.     row.calcPk(test, k);
  2058.     row.calcNk(test);
  2059.     test.run(HStmt(hStmt), SQLExecute(hStmt));
  2060.     test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_UPDATE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_UPDATE_WHERE);
  2061.     chkRowCount(test, hStmt, 1);
  2062.     // direct update, no read has been necessary
  2063.     chkTuplesFetched(test, hStmt, 0);
  2064. }
  2065. test.timerCnt(opt.m_scale);
  2066. if (opt.m_v >= 3)
  2067.     ndbout << "updated " << opt.m_scale <<  " in " << tab.m_name << endl;
  2068.     }
  2069.     freeAll(test, hEnv, hDbc, hStmtList, tabCount);
  2070. }
  2071. static void
  2072. testUpdateScan(Test& test)
  2073. {
  2074.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  2075.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  2076.     char sql[MAX_SQL], *sqlptr;
  2077.     for (unsigned i = 0; i < tabCount; i++) {
  2078. SQLHANDLE& hStmt = hStmtList[i];
  2079. const Tab& tab = tabList[i];
  2080. if (! tab.optok())
  2081.     continue;
  2082. // prepare
  2083. tab.updateRange(sqlptr = sql);
  2084. if (opt.m_v >= 2)
  2085.     ndbout << "SQL: " << sql << endl;
  2086. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  2087. // bind parameters
  2088. Row row(tab); // for set clause
  2089. Row rowlo(tab); // for pk ranges
  2090. Row rowhi(tab);
  2091. SQLSMALLINT parCount = -1;
  2092. test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
  2093. 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);
  2094. for (unsigned j = 0; j < tab.m_nkCount; j++) {
  2095.     const Col& col = tab.m_colList[tab.m_nkIndex[j]];
  2096.     Fld& fld = row.m_fldList[tab.m_nkIndex[j]];
  2097.     test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
  2098. }
  2099. bool canInterp = true;
  2100. for (unsigned j = 0; j < tab.m_pkCount; j++) {
  2101.     const Col& col = tab.m_colList[tab.m_pkIndex[j]];
  2102.     Fld& fldlo = rowlo.m_fldList[tab.m_pkIndex[j]];
  2103.     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()));
  2104.     Fld& fldhi = rowhi.m_fldList[tab.m_pkIndex[j]];
  2105.     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()));
  2106.     if (col.m_type != Col::Char)
  2107. canInterp = false; // XXX no unsigned yet
  2108. }
  2109. // execute
  2110. row.calcPk(test, 0);
  2111. row.calcNk(test);
  2112. rowlo.calcPk(test, 0);
  2113. rowhi.calcPk(test, test.m_mul); // sucks
  2114. test.run(HStmt(hStmt), SQLExecute(hStmt));
  2115. test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_UPDATE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_UPDATE_WHERE);
  2116. chkRowCount(test, hStmt, opt.m_scale);
  2117. chkTuplesFetched(test, hStmt, canInterp ? opt.m_scale : opt.m_scale * opt.m_threads);
  2118. test.timerCnt(opt.m_scale);
  2119. if (opt.m_v >= 3)
  2120.     ndbout << "updated " << opt.m_scale <<  " in " << tab.m_name << endl;
  2121.     }
  2122.     freeAll(test, hEnv, hDbc, hStmtList, tabCount);
  2123. }
  2124. // verify
  2125. static void
  2126. testVerifyPk(Test& test)
  2127. {
  2128.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  2129.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  2130.     char sql[MAX_SQL], *sqlptr;
  2131.     for (unsigned i = 0; i < tabCount; i++) {
  2132. SQLHANDLE& hStmt = hStmtList[i];
  2133. const Tab& tab = tabList[i];
  2134. if (! tab.optok())
  2135.     continue;
  2136. // prepare
  2137. tab.selectPk(sqlptr = sql);
  2138. if (opt.m_v >= 2)
  2139.     ndbout << "SQL: " << sql << endl;
  2140. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  2141. // use same row for input and output
  2142. Row row(tab);
  2143. // bind parameters
  2144. SQLSMALLINT parCount = -1;
  2145. test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
  2146. test.chk(HStmt(hStmt), parCount == tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_pkCount);
  2147. for (unsigned j = 0; j < tab.m_pkCount; j++) {
  2148.     Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
  2149.     const Col& col = fld.m_col;
  2150.     test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
  2151. }
  2152. // bind columns
  2153. SQLSMALLINT colCount = -1;
  2154. test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
  2155. test.chk(HStmt(hStmt), colCount == tab.m_colCount, "got %d != %d", (int)colCount, (int)tab.m_colCount);
  2156. for (unsigned j = 0; j < tab.m_colCount; j++) {
  2157.     Fld& fld = row.m_fldList[j];
  2158.     const Col& col = fld.m_col;
  2159.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, col.ctype(), fld.caddr(), col.csize(), fld.ind()));
  2160. }
  2161. // row for SQLGetData
  2162. Row rowGet(tab);
  2163. // reference row
  2164. Row rowRef(tab);
  2165. // execute
  2166. for (unsigned k = 0; k < opt.m_scale; k++) {
  2167.     if (k % 5 == 0) {
  2168. // rebind
  2169. unsigned j = 0;
  2170. Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
  2171. const Col& col = fld.m_col;
  2172. test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
  2173.     }
  2174.     row.calcPk(test, k);
  2175.     test.run(HStmt(hStmt), SQLExecute(hStmt));
  2176.     test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_SELECT_CURSOR, "got %d != %d", test.m_functionCode, SQL_DIAG_SELECT_CURSOR);
  2177.     // fetch
  2178.     for (unsigned k2 = 0; ; k2++) {
  2179. if (k2 == 1)
  2180.     test.exp(SQL_NO_DATA, 0, 0, true);
  2181. test.run(HStmt(hStmt), SQLFetch(hStmt));
  2182. chkTuplesFetched(test, hStmt, 1);
  2183. if (k2 == 1)
  2184.     break;
  2185. rowRef.calcPk(test, k);
  2186. test.chk(HStmt(hStmt), row.verifyPk(test, rowRef), "verify row=%d", k);
  2187. if (test.m_const)
  2188.     rowRef.calcPk(test, 0);
  2189. rowRef.calcNk(test);
  2190. test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k);
  2191. // SQLGetData is supported independent of SQLBindCol
  2192. if (opt.m_nogetd)
  2193.     continue;
  2194. for (unsigned j = 0; j < tab.m_colCount; j++) {
  2195.     Fld& fld = rowGet.m_fldList[j];
  2196.     fld.zero();
  2197.     const Col& col = fld.m_col;
  2198.     // test both variants
  2199.     SQLSMALLINT ctype = k % 2 == 0 ? col.ctype() : SQL_ARD_TYPE;
  2200.     if (ctype != Col::CChar)
  2201. test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, fld.caddr(), col.csize(), fld.ind()));
  2202.     else {
  2203. // get in pieces
  2204. unsigned size = col.csize() - 1; // omit null terminator
  2205. char* caddr = (char*)(fld.caddr());
  2206. unsigned off = 0;
  2207. while (off < size) {
  2208.     unsigned m = size / 3; // bytes to get
  2209.     if (m == 0)
  2210. m = 1;
  2211.     if (m > size - off)
  2212. m = size - off;
  2213.     bool getNull = (rowRef.m_fldList[j].m_ind == SQL_NULL_DATA);
  2214.     if (off + m < size && ! getNull)
  2215. test.exp(SQL_SUCCESS_WITH_INFO, "01004", -1, true);
  2216.     // include null terminator in buffer size
  2217.     test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, caddr + off, m + 1, fld.ind()));
  2218.     int ind = *fld.ind();
  2219.     if (getNull) {
  2220. test.chk(HStmt(hStmt), ind == SQL_NULL_DATA, "got %d", ind);
  2221. break;
  2222.     }
  2223.     test.chk(HStmt(hStmt), ind == size - off, "got %d != %u", ind, size - off);
  2224.     off += m;
  2225. }
  2226.     }
  2227. }
  2228. rowRef.calcPk(test, k);
  2229. test.chk(HStmt(hStmt), rowGet.verifyPk(test, rowRef), "verify row=%d", k);
  2230. if (test.m_const)
  2231.     rowRef.calcPk(test, 0);
  2232. rowRef.calcNk(test);
  2233. test.chk(HStmt(hStmt), rowGet.verifyNk(test, rowRef), "verify row=%d", k);
  2234. // SQLGetData again
  2235. for (unsigned j = 0; j < tab.m_colCount; j++) {
  2236.     Fld& fld = rowGet.m_fldList[j];
  2237.     const Col& col = fld.m_col;
  2238.     // test both variants
  2239.     SQLSMALLINT ctype = k % 2 == 0 ? col.ctype() : SQL_ARD_TYPE;
  2240.     // expect no more data
  2241.     test.exp(SQL_NO_DATA, 0, 0, true);
  2242.     test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, fld.caddr(), col.csize(), fld.ind()));
  2243. }
  2244.     }
  2245.     test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  2246. }
  2247. test.timerCnt(opt.m_scale);
  2248. if (opt.m_v >= 3)
  2249.     ndbout << "verified " << opt.m_scale <<  " from " << tab.m_name << endl;
  2250.     }
  2251.     freeAll(test, hEnv, hDbc, hStmtList, tabCount);
  2252. }
  2253. static void
  2254. testVerifyScan(Test& test)
  2255. {
  2256.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  2257.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  2258.     char sql[MAX_SQL], *sqlptr;
  2259.     for (unsigned i = 0; i < tabCount; i++) {
  2260. SQLHANDLE& hStmt = hStmtList[i];
  2261. const Tab& tab = tabList[i];
  2262. if (! tab.optok())
  2263.     continue;
  2264. // prepare
  2265. tab.selectRange(sqlptr = sql, ! opt.m_nosort);
  2266. if (opt.m_v >= 2)
  2267.     ndbout << "SQL: " << sql << endl;
  2268. test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
  2269. // bind parameters
  2270. Row rowlo(tab); // use available PK fields..
  2271. Row rowhi(tab); // since we have no other way for now
  2272. SQLSMALLINT parCount = -1;
  2273. test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
  2274. test.chk(HStmt(hStmt), parCount == 2 * tab.m_pkCount, "got %d != %d", (int)parCount, 2 * (int)tab.m_pkCount);
  2275. for (unsigned j = 0; j < tab.m_pkCount; j++) {
  2276.     const Col& col = tab.m_colList[tab.m_pkIndex[j]];
  2277.     Fld& fldlo = rowlo.m_fldList[tab.m_pkIndex[j]];
  2278.     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()));
  2279.     Fld& fldhi = rowhi.m_fldList[tab.m_pkIndex[j]];
  2280.     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()));
  2281. }
  2282. // bind columns
  2283. Row row(tab);
  2284. SQLSMALLINT colCount = -1;
  2285. test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
  2286. test.chk(HStmt(hStmt), colCount == tab.m_colCount, "got %d != %d", (int)colCount, (int)tab.m_colCount);
  2287. for (unsigned j = 0; j < tab.m_colCount; j++) {
  2288.     Fld& fld = row.m_fldList[j];
  2289.     const Col& col = fld.m_col;
  2290.     test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, col.ctype(), fld.caddr(), col.csize(), fld.ind()));
  2291. }
  2292. // execute
  2293. rowlo.calcPk(test, 0);
  2294. rowhi.calcPk(test, test.m_mul); // sucks
  2295. test.run(HStmt(hStmt), SQLExecute(hStmt));
  2296.     test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_SELECT_CURSOR, "got %d != %d", test.m_functionCode, SQL_DIAG_SELECT_CURSOR);
  2297. // reference row
  2298. Row rowRef(tab);
  2299. // fetch
  2300. unsigned k = 0;
  2301. SQLUINTEGER rowCount1 = (SQLUINTEGER)-1;
  2302. test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowCount1, SQL_IS_POINTER));
  2303. while (1) {
  2304.     unsigned countExp;
  2305.     if (k == opt.m_scale) {
  2306. countExp = k;
  2307. test.exp(SQL_NO_DATA, 0, 0, true);
  2308.     } else {
  2309. countExp = k + 1;
  2310.     }
  2311.     test.run(HStmt(hStmt), SQLFetch(hStmt));
  2312.     // let me count the ways..
  2313.     chkRowCount(test, hStmt, countExp);
  2314.     test.chk(HStmt(hStmt), rowCount1 == countExp, "got %lu != %u", rowCount1, countExp);
  2315.     SQLUINTEGER rowCount2 = (SQLUINTEGER)-1;
  2316.     test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_ROW_NUMBER, &rowCount2, SQL_IS_POINTER, 0));
  2317.     test.chk(HStmt(hStmt), rowCount2 == countExp, "got %lu != %u", rowCount2, countExp);
  2318.     if (k == opt.m_scale)
  2319. break;
  2320.     if (! opt.m_nosort) {
  2321. // expecting k-th row
  2322. rowRef.calcPk(test, k);
  2323. test.chk(HStmt(hStmt), row.verifyPk(test, rowRef), "verify row=%d", k);
  2324. if (test.m_const)
  2325.     rowRef.calcPk(test, 0);
  2326. rowRef.calcNk(test);
  2327. test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k);
  2328.     } else {
  2329. // expecting random row
  2330. rowRef.copy(row);
  2331. test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k);
  2332.     }
  2333.     k++;
  2334. }
  2335. test.timerCnt(opt.m_scale);
  2336. if (opt.m_v >= 3)
  2337.     ndbout << "verified " << opt.m_scale <<  " from " << tab.m_name << endl;
  2338.     }
  2339.     freeAll(test, hEnv, hDbc, hStmtList, tabCount);
  2340. }
  2341. // self-join (scan followed by pk lookups)
  2342. static void
  2343. testJoin(Test& test)
  2344. {
  2345.     SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
  2346.     allocAll(test, hEnv, hDbc, hStmtList, tabCount);
  2347.     char sql[MAX_SQL], *sqlptr;
  2348.     for (unsigned cnt = opt.m_depth; cnt <= opt.m_depth; cnt++) {
  2349. for (unsigned i = 0; i < tabCount; i++) {
  2350.     SQLHANDLE& hStmt = hStmtList[i];
  2351.     Tab& tab = tabList[i];
  2352.     if (! tab.optok())
  2353. continue;
  2354.     tab.selectJoin(sqlptr = sql, cnt);
  2355.     if (opt.m_v >= 2)
  2356. ndbout << "SQL: " << sql << endl;
  2357.     test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
  2358. }
  2359. unsigned k = 0;
  2360. while (1) {
  2361.     for (unsigned i = 0; i < tabCount; i++) {
  2362. SQLHANDLE& hStmt = hStmtList[i];
  2363. const Tab& tab = tabList[i];
  2364. if (! tab.optok())
  2365.     continue;
  2366. if (k == opt.m_scale * opt.m_threads)
  2367.     test.exp(SQL_NO_DATA, 0, 0, true);
  2368. test.run(HStmt(hStmt), SQLFetch(hStmt));
  2369. if (k == opt.m_scale * opt.m_threads) {
  2370.     chkTuplesFetched(test, hStmt, k * opt.m_depth);
  2371.     test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
  2372. } else {
  2373.     chkTuplesFetched(test, hStmt, (k + 1) * opt.m_depth);
  2374.     test.timerCnt(1);
  2375. }
  2376.     }
  2377.     if (k == opt.m_scale * opt.m_threads)
  2378. break;