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

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. /*
  14.  * testBlobs
  15.  */
  16. #include <ndb_global.h>
  17. #include <NdbMain.h>
  18. #include <NdbOut.hpp>
  19. #include <NdbTest.hpp>
  20. #include <NdbTick.h>
  21. #include <ndb/src/ndbapi/NdbBlobImpl.hpp>
  22. struct Bcol {
  23.   bool m_nullable;
  24.   unsigned m_inline;
  25.   unsigned m_partsize;
  26.   unsigned m_stripe;
  27.   char m_btname[NdbBlobImpl::BlobTableNameSize];
  28.   Bcol(bool a, unsigned b, unsigned c, unsigned d) :
  29.     m_nullable(a),
  30.     m_inline(b),
  31.     m_partsize(c),
  32.     m_stripe(d)
  33.     {}
  34. };
  35. struct Opt {
  36.   unsigned m_batch;
  37.   bool m_core;
  38.   bool m_dbg;
  39.   bool m_dbgall;
  40.   const char* m_dbug;
  41.   bool m_full;
  42.   unsigned m_loop;
  43.   unsigned m_parts;
  44.   unsigned m_rows;
  45.   unsigned m_seed;
  46.   const char* m_skip;
  47.   const char* m_test;
  48.   // metadata
  49.   const char* m_tname;
  50.   const char* m_x1name;  // hash index
  51.   const char* m_x2name;  // ordered index
  52.   unsigned m_pk1off;
  53.   unsigned m_pk2len;
  54.   bool m_oneblob;
  55.   Bcol m_blob1;
  56.   Bcol m_blob2;
  57.   // perf
  58.   const char* m_tnameperf;
  59.   unsigned m_rowsperf;
  60.   // bugs
  61.   int m_bug;
  62.   int (*m_bugtest)();
  63.   Opt() :
  64.     m_batch(7),
  65.     m_core(false),
  66.     m_dbg(false),
  67.     m_dbgall(false),
  68.     m_dbug(0),
  69.     m_full(false),
  70.     m_loop(1),
  71.     m_parts(10),
  72.     m_rows(100),
  73.     m_seed(0),
  74.     m_skip(0),
  75.     m_test(0),
  76.     // metadata
  77.     m_tname("TBLOB1"),
  78.     m_x1name("TBLOB1X1"),
  79.     m_x2name("TBLOB1X2"),
  80.     m_pk1off(0x12340000),
  81.     m_pk2len(55),
  82.     m_oneblob(false),
  83.     m_blob1(false, 7, 1137, 10),
  84.     m_blob2(true, 99, 55, 1),
  85.     // perf
  86.     m_tnameperf("TBLOB2"),
  87.     m_rowsperf(10000),
  88.     // bugs
  89.     m_bug(0),
  90.     m_bugtest(0) {
  91.   }
  92. };
  93. static const unsigned g_max_pk2len = 256;
  94. static void
  95. printusage()
  96. {
  97.   Opt d;
  98.   ndbout
  99.     << "usage: testBlobs options [default/max]" << endl
  100.     << "  -batch N    number of pk ops in batch [" << d.m_batch << "]" << endl
  101.     << "  -core       dump core on error" << endl
  102.     << "  -dbg        print debug" << endl
  103.     << "  -dbgall     print also NDB API debug (if compiled in)" << endl
  104.     << "  -dbug opt   dbug options" << endl
  105.     << "  -full       read/write only full blob values" << endl
  106.     << "  -loop N     loop N times 0=forever [" << d.m_loop << "]" << endl
  107.     << "  -parts N    max parts in blob value [" << d.m_parts << "]" << endl
  108.     << "  -rows N     number of rows [" << d.m_rows << "]" << endl
  109.     << "  -rowsperf N rows for performace test [" << d.m_rowsperf << "]" << endl
  110.     << "  -seed N     random seed 0=loop number [" << d.m_seed << "]" << endl
  111.     << "  -skip xxx   skip given tests (see list) [no tests]" << endl
  112.     << "  -test xxx   only given tests (see list) [all tests]" << endl
  113.     << "metadata" << endl
  114.     << "  -pk2len N   length of PK2 [" << d.m_pk2len << "/" << g_max_pk2len <<"]" << endl
  115.     << "  -oneblob    only 1 blob attribute [default 2]" << endl
  116.     << "testcases for test/skip" << endl
  117.     << "  k           primary key ops" << endl
  118.     << "  i           hash index ops" << endl
  119.     << "  s           table scans" << endl
  120.     << "  r           ordered index scans" << endl
  121.     << "  p           performance test" << endl
  122.     << "additional flags for test/skip" << endl
  123.     << "  u           update existing blob value" << endl
  124.     << "  n           normal insert and update" << endl
  125.     << "  w           insert and update using writeTuple" << endl
  126.     << "  0           getValue / setValue" << endl
  127.     << "  1           setActiveHook" << endl
  128.     << "  2           readData / writeData" << endl
  129.     << "bug tests (no blob test)" << endl
  130.     << "  -bug 4088   ndb api hang with mixed ops on index table" << endl
  131.     << "  -bug nnnn   delete + write gives 626" << endl
  132.     << "  -bug nnnn   acc crash on delete and long key" << endl
  133.     ;
  134. }
  135. static Opt g_opt;
  136. static bool
  137. testcase(char x)
  138. {
  139.   if (x < 10)
  140.     x += '0';
  141.   return
  142.     (g_opt.m_test == 0 || strchr(g_opt.m_test, x) != 0) &&
  143.     (g_opt.m_skip == 0 || strchr(g_opt.m_skip, x) == 0);
  144. }
  145. static Ndb_cluster_connection* g_ncc = 0;
  146. static Ndb* g_ndb = 0;
  147. static NdbDictionary::Dictionary* g_dic = 0;
  148. static NdbConnection* g_con = 0;
  149. static NdbOperation* g_opr = 0;
  150. static NdbIndexOperation* g_opx = 0;
  151. static NdbScanOperation* g_ops = 0;
  152. static NdbBlob* g_bh1 = 0;
  153. static NdbBlob* g_bh2 = 0;
  154. static bool g_printerror = true;
  155. static unsigned g_loop = 0;
  156. static void
  157. printerror(int line, const char* msg)
  158. {
  159.   ndbout << "line " << line << " FAIL " << msg << endl;
  160.   if (! g_printerror) {
  161.     return;
  162.   }
  163.   if (g_ndb != 0 && g_ndb->getNdbError().code != 0) {
  164.     ndbout << "ndb: " << g_ndb->getNdbError() << endl;
  165.   }
  166.   if (g_dic != 0 && g_dic->getNdbError().code != 0) {
  167.     ndbout << "dic: " << g_dic->getNdbError() << endl;
  168.   }
  169.   if (g_con != 0 && g_con->getNdbError().code != 0) {
  170.     ndbout << "con: " << g_con->getNdbError() << endl;
  171.     if (g_opr != 0 && g_opr->getNdbError().code != 0) {
  172.       ndbout << "opr: table=" << g_opr->getTableName() << " " << g_opr->getNdbError() << endl;
  173.     }
  174.     if (g_opx != 0 && g_opx->getNdbError().code != 0) {
  175.       ndbout << "opx: table=" << g_opx->getTableName() << " " << g_opx->getNdbError() << endl;
  176.     }
  177.     if (g_ops != 0 && g_ops->getNdbError().code != 0) {
  178.       ndbout << "ops: table=" << g_ops->getTableName() << " " << g_ops->getNdbError() << endl;
  179.     }
  180.     NdbOperation* ope = g_con->getNdbErrorOperation();
  181.     if (ope != 0 && ope->getNdbError().code != 0) {
  182.       if (ope != g_opr && ope != g_opx && ope != g_ops)
  183.         ndbout << "ope: table=" << ope->getTableName() << " " << ope->getNdbError() << endl;
  184.     }
  185.   }
  186.   if (g_bh1 != 0 && g_bh1->getNdbError().code != 0) {
  187.     ndbout << "bh1: " << g_bh1->getNdbError() << endl;
  188.   }
  189.   if (g_bh2 != 0 && g_bh2->getNdbError().code != 0) {
  190.     ndbout << "bh2: " << g_bh2->getNdbError() << endl;
  191.   }
  192.   if (g_opt.m_core) {
  193.     abort();
  194.   }
  195.   g_printerror = false;
  196. }
  197. #define CHK(x) 
  198.   do { 
  199.     if (x) break; 
  200.     printerror(__LINE__, #x); return -1; 
  201.   } while (0)
  202. #define DBG(x) 
  203.   do { 
  204.     if (! g_opt.m_dbg) break; 
  205.     ndbout << "line " << __LINE__ << " " << x << endl; 
  206.   } while (0)
  207. static int
  208. dropTable()
  209. {
  210.   NdbDictionary::Table tab(g_opt.m_tname);
  211.   if (g_dic->getTable(g_opt.m_tname) != 0)
  212.     CHK(g_dic->dropTable(tab) == 0);
  213.   return 0;
  214. }
  215. static int
  216. createTable()
  217. {
  218.   NdbDictionary::Table tab(g_opt.m_tname);
  219.   tab.setLogging(false);
  220.   // col PK1 - Uint32
  221.   { NdbDictionary::Column col("PK1");
  222.     col.setType(NdbDictionary::Column::Unsigned);
  223.     col.setPrimaryKey(true);
  224.     tab.addColumn(col);
  225.   }
  226.   // col BL1 - Blob not-nullable
  227.   { NdbDictionary::Column col("BL1");
  228.     const Bcol& b = g_opt.m_blob1;
  229.     col.setType(NdbDictionary::Column::Blob);
  230.     col.setInlineSize(b.m_inline);
  231.     col.setPartSize(b.m_partsize);
  232.     col.setStripeSize(b.m_stripe);
  233.     tab.addColumn(col);
  234.   }
  235.   // col PK2 - Char[55]
  236.   if (g_opt.m_pk2len != 0)
  237.   { NdbDictionary::Column col("PK2");
  238.     col.setType(NdbDictionary::Column::Char);
  239.     col.setLength(g_opt.m_pk2len);
  240.     col.setPrimaryKey(true);
  241.     tab.addColumn(col);
  242.   }
  243.   // col BL2 - Text nullable
  244.   if (! g_opt.m_oneblob)
  245.   { NdbDictionary::Column col("BL2");
  246.     const Bcol& b = g_opt.m_blob2;
  247.     col.setType(NdbDictionary::Column::Text);
  248.     col.setNullable(true);
  249.     col.setInlineSize(b.m_inline);
  250.     col.setPartSize(b.m_partsize);
  251.     col.setStripeSize(b.m_stripe);
  252.     tab.addColumn(col);
  253.   }
  254.   // create table
  255.   CHK(g_dic->createTable(tab) == 0);
  256.   // unique hash index on PK2
  257.   if (g_opt.m_pk2len != 0)
  258.   { NdbDictionary::Index idx(g_opt.m_x1name);
  259.     idx.setType(NdbDictionary::Index::UniqueHashIndex);
  260.     idx.setLogging(false);
  261.     idx.setTable(g_opt.m_tname);
  262.     idx.addColumnName("PK2");
  263.     CHK(g_dic->createIndex(idx) == 0);
  264.   }
  265.   // ordered index on PK2
  266.   if (g_opt.m_pk2len != 0)
  267.   { NdbDictionary::Index idx(g_opt.m_x2name);
  268.     idx.setType(NdbDictionary::Index::OrderedIndex);
  269.     idx.setLogging(false);
  270.     idx.setTable(g_opt.m_tname);
  271.     idx.addColumnName("PK2");
  272.     CHK(g_dic->createIndex(idx) == 0);
  273.   }
  274.   return 0;
  275. }
  276. // tuples
  277. struct Bval {
  278.   char* m_val;
  279.   unsigned m_len;
  280.   char* m_buf;
  281.   unsigned m_buflen;
  282.   Bval() :
  283.     m_val(0),
  284.     m_len(0),
  285.     m_buf(0),   // read/write buffer
  286.     m_buflen(0)
  287.     {}
  288.   ~Bval() { delete [] m_val; delete [] m_buf; }
  289.   void alloc(unsigned buflen) {
  290.     m_buflen = buflen;
  291.     delete [] m_buf;
  292.     m_buf = new char [m_buflen];
  293.     trash();
  294.   }
  295.   void copyfrom(const Bval& v) {
  296.     m_len = v.m_len;
  297.     delete [] m_val;
  298.     if (v.m_val == 0)
  299.       m_val = 0;
  300.     else
  301.       m_val = (char*)memcpy(new char [m_len], v.m_val, m_len);
  302.   }
  303.   void trash() const {
  304.     assert(m_buf != 0);
  305.     memset(m_buf, 'x', m_buflen);
  306.   }
  307. private:
  308.   Bval(const Bval&);
  309.   Bval& operator=(const Bval&);
  310. };
  311. struct Tup {
  312.   bool m_exists;        // exists in table
  313.   Uint32 m_pk1;         // primary keys concatenated like keyinfo
  314.   char m_pk2[g_max_pk2len + 1];
  315.   Bval m_blob1;
  316.   Bval m_blob2;
  317.   Tup() :
  318.     m_exists(false)
  319.     {}
  320.   ~Tup() { }
  321.   // alloc buffers of max size
  322.   void alloc() {
  323.     m_blob1.alloc(g_opt.m_blob1.m_inline + g_opt.m_blob1.m_partsize * g_opt.m_parts);
  324.     m_blob2.alloc(g_opt.m_blob2.m_inline + g_opt.m_blob2.m_partsize * g_opt.m_parts);
  325.   }
  326.   void copyfrom(const Tup& tup) {
  327.     assert(m_pk1 == tup.m_pk1);
  328.     m_blob1.copyfrom(tup.m_blob1);
  329.     m_blob2.copyfrom(tup.m_blob2);
  330.   }
  331. private:
  332.   Tup(const Tup&);
  333.   Tup& operator=(const Tup&);
  334. };
  335. static Tup* g_tups;
  336. static unsigned
  337. urandom(unsigned n)
  338. {
  339.   return n == 0 ? 0 : random() % n;
  340. }
  341. static void
  342. calcBval(const Bcol& b, Bval& v, bool keepsize)
  343. {
  344.   if (b.m_nullable && urandom(10) == 0) {
  345.     v.m_len = 0;
  346.     delete [] v.m_val;
  347.     v.m_val = 0;
  348.     v.m_buf = new char [1];
  349.   } else {
  350.     if (keepsize && v.m_val != 0)
  351.       ;
  352.     else if (urandom(10) == 0)
  353.       v.m_len = urandom(b.m_inline);
  354.     else
  355.       v.m_len = urandom(b.m_inline + g_opt.m_parts * b.m_partsize + 1);
  356.     delete [] v.m_val;
  357.     v.m_val = new char [v.m_len + 1];
  358.     for (unsigned i = 0; i < v.m_len; i++)
  359.       v.m_val[i] = 'a' + urandom(25);
  360.     v.m_val[v.m_len] = 0;
  361.     v.m_buf = new char [v.m_len];
  362.   }
  363.   v.m_buflen = v.m_len;
  364.   v.trash();
  365. }
  366. static void
  367. calcBval(Tup& tup, bool keepsize)
  368. {
  369.   calcBval(g_opt.m_blob1, tup.m_blob1, keepsize);
  370.   if (! g_opt.m_oneblob)
  371.     calcBval(g_opt.m_blob2, tup.m_blob2, keepsize);
  372. }
  373. static void
  374. calcTups(bool keepsize)
  375. {
  376.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  377.     Tup& tup = g_tups[k];
  378.     tup.m_pk1 = g_opt.m_pk1off + k;
  379.     for (unsigned i = 0, n = k; i < g_opt.m_pk2len; i++) {
  380.       if (n != 0) {
  381.         tup.m_pk2[i] = '0' + n % 10;
  382.         n = n / 10;
  383.       } else {
  384.         tup.m_pk2[i] = 'a' + i % 26;
  385.       }
  386.     }
  387.     calcBval(tup, keepsize);
  388.   }
  389. }
  390. // blob handle ops
  391. static int
  392. getBlobHandles(NdbOperation* opr)
  393. {
  394.   CHK((g_bh1 = opr->getBlobHandle("BL1")) != 0);
  395.   if (! g_opt.m_oneblob)
  396.     CHK((g_bh2 = opr->getBlobHandle("BL2")) != 0);
  397.   return 0;
  398. }
  399. static int
  400. getBlobHandles(NdbIndexOperation* opx)
  401. {
  402.   CHK((g_bh1 = opx->getBlobHandle("BL1")) != 0);
  403.   if (! g_opt.m_oneblob)
  404.     CHK((g_bh2 = opx->getBlobHandle("BL2")) != 0);
  405.   return 0;
  406. }
  407. static int
  408. getBlobHandles(NdbScanOperation* ops)
  409. {
  410.   CHK((g_bh1 = ops->getBlobHandle("BL1")) != 0);
  411.   if (! g_opt.m_oneblob)
  412.     CHK((g_bh2 = ops->getBlobHandle("BL2")) != 0);
  413.   return 0;
  414. }
  415. static int
  416. getBlobLength(NdbBlob* h, unsigned& len)
  417. {
  418.   Uint64 len2 = (unsigned)-1;
  419.   CHK(h->getLength(len2) == 0);
  420.   len = (unsigned)len2;
  421.   assert(len == len2);
  422.   bool isNull;
  423.   CHK(h->getNull(isNull) == 0);
  424.   DBG("getBlobLength " << h->getColumn()->getName() << " len=" << len << " null=" << isNull);
  425.   return 0;
  426. }
  427. // setValue / getValue
  428. static int
  429. setBlobValue(NdbBlob* h, const Bval& v)
  430. {
  431.   bool null = (v.m_val == 0);
  432.   bool isNull;
  433.   unsigned len;
  434.   DBG("setValue " <<  h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
  435.   if (null) {
  436.     CHK(h->setNull() == 0);
  437.     isNull = false;
  438.     CHK(h->getNull(isNull) == 0 && isNull == true);
  439.     CHK(getBlobLength(h, len) == 0 && len == 0);
  440.   } else {
  441.     CHK(h->setValue(v.m_val, v.m_len) == 0);
  442.     CHK(h->getNull(isNull) == 0 && isNull == false);
  443.     CHK(getBlobLength(h, len) == 0 && len == v.m_len);
  444.   }
  445.   return 0;
  446. }
  447. static int
  448. setBlobValue(const Tup& tup)
  449. {
  450.   CHK(setBlobValue(g_bh1, tup.m_blob1) == 0);
  451.   if (! g_opt.m_oneblob)
  452.     CHK(setBlobValue(g_bh2, tup.m_blob2) == 0);
  453.   return 0;
  454. }
  455. static int
  456. getBlobValue(NdbBlob* h, const Bval& v)
  457. {
  458.   bool null = (v.m_val == 0);
  459.   DBG("getValue " <<  h->getColumn()->getName() << " buflen=" << v.m_buflen);
  460.   CHK(h->getValue(v.m_buf, v.m_buflen) == 0);
  461.   return 0;
  462. }
  463. static int
  464. getBlobValue(const Tup& tup)
  465. {
  466.   CHK(getBlobValue(g_bh1, tup.m_blob1) == 0);
  467.   if (! g_opt.m_oneblob)
  468.     CHK(getBlobValue(g_bh2, tup.m_blob2) == 0);
  469.   return 0;
  470. }
  471. static int
  472. verifyBlobValue(NdbBlob* h, const Bval& v)
  473. {
  474.   bool null = (v.m_val == 0);
  475.   bool isNull;
  476.   unsigned len;
  477.   if (null) {
  478.     isNull = false;
  479.     CHK(h->getNull(isNull) == 0 && isNull == true);
  480.     CHK(getBlobLength(h, len) == 0 && len == 0);
  481.   } else {
  482.     isNull = true;
  483.     CHK(h->getNull(isNull) == 0 && isNull == false);
  484.     CHK(getBlobLength(h, len) == 0 && len == v.m_len);
  485.     for (unsigned i = 0; i < v.m_len; i++)
  486.       CHK(v.m_val[i] == v.m_buf[i]);
  487.   }
  488.   return 0;
  489. }
  490. static int
  491. verifyBlobValue(const Tup& tup)
  492. {
  493.   CHK(verifyBlobValue(g_bh1, tup.m_blob1) == 0);
  494.   if (! g_opt.m_oneblob)
  495.     CHK(verifyBlobValue(g_bh2, tup.m_blob2) == 0);
  496.   return 0;
  497. }
  498. // readData / writeData
  499. static int
  500. writeBlobData(NdbBlob* h, const Bval& v)
  501. {
  502.   bool null = (v.m_val == 0);
  503.   bool isNull;
  504.   unsigned len;
  505.   DBG("write " <<  h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
  506.   if (null) {
  507.     CHK(h->setNull() == 0);
  508.     isNull = false;
  509.     CHK(h->getNull(isNull) == 0 && isNull == true);
  510.     CHK(getBlobLength(h, len) == 0 && len == 0);
  511.   } else {
  512.     CHK(h->truncate(v.m_len) == 0);
  513.     unsigned n = 0;
  514.     do {
  515.       unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1);
  516.       if (m > v.m_len - n)
  517.         m = v.m_len - n;
  518.       DBG("write pos=" << n << " cnt=" << m);
  519.       CHK(h->writeData(v.m_val + n, m) == 0);
  520.       n += m;
  521.     } while (n < v.m_len);
  522.     assert(n == v.m_len);
  523.     isNull = true;
  524.     CHK(h->getNull(isNull) == 0 && isNull == false);
  525.     CHK(getBlobLength(h, len) == 0 && len == v.m_len);
  526.   }
  527.   return 0;
  528. }
  529. static int
  530. writeBlobData(const Tup& tup)
  531. {
  532.   CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
  533.   if (! g_opt.m_oneblob)
  534.     CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
  535.   return 0;
  536. }
  537. static int
  538. readBlobData(NdbBlob* h, const Bval& v)
  539. {
  540.   bool null = (v.m_val == 0);
  541.   bool isNull;
  542.   unsigned len;
  543.   DBG("read " <<  h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
  544.   if (null) {
  545.     isNull = false;
  546.     CHK(h->getNull(isNull) == 0 && isNull == true);
  547.     CHK(getBlobLength(h, len) == 0 && len == 0);
  548.   } else {
  549.     isNull = true;
  550.     CHK(h->getNull(isNull) == 0 && isNull == false);
  551.     CHK(getBlobLength(h, len) == 0 && len == v.m_len);
  552.     v.trash();
  553.     unsigned n = 0;
  554.     while (n < v.m_len) {
  555.       unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1);
  556.       if (m > v.m_len - n)
  557.         m = v.m_len - n;
  558.       DBG("read pos=" << n << " cnt=" << m);
  559.       const unsigned m2 = m;
  560.       CHK(h->readData(v.m_buf + n, m) == 0);
  561.       CHK(m2 == m);
  562.       n += m;
  563.     }
  564.     assert(n == v.m_len);
  565.     // need to execute to see the data
  566.     CHK(g_con->execute(NoCommit) == 0);
  567.     for (unsigned i = 0; i < v.m_len; i++)
  568.       CHK(v.m_val[i] == v.m_buf[i]);
  569.   }
  570.   return 0;
  571. }
  572. static int
  573. readBlobData(const Tup& tup)
  574. {
  575.   CHK(readBlobData(g_bh1, tup.m_blob1) == 0);
  576.   if (! g_opt.m_oneblob)
  577.     CHK(readBlobData(g_bh2, tup.m_blob2) == 0);
  578.   return 0;
  579. }
  580. // hooks
  581. static NdbBlob::ActiveHook blobWriteHook;
  582. static int
  583. blobWriteHook(NdbBlob* h, void* arg)
  584. {
  585.   DBG("blobWriteHook");
  586.   Bval& v = *(Bval*)arg;
  587.   CHK(writeBlobData(h, v) == 0);
  588.   return 0;
  589. }
  590. static int
  591. setBlobWriteHook(NdbBlob* h, Bval& v)
  592. {
  593.   DBG("setBlobWriteHook");
  594.   CHK(h->setActiveHook(blobWriteHook, &v) == 0);
  595.   return 0;
  596. }
  597. static int
  598. setBlobWriteHook(Tup& tup)
  599. {
  600.   CHK(setBlobWriteHook(g_bh1, tup.m_blob1) == 0);
  601.   if (! g_opt.m_oneblob)
  602.     CHK(setBlobWriteHook(g_bh2, tup.m_blob2) == 0);
  603.   return 0;
  604. }
  605. static NdbBlob::ActiveHook blobReadHook;
  606. // no PK yet to identify tuple so just read the value
  607. static int
  608. blobReadHook(NdbBlob* h, void* arg)
  609. {
  610.   DBG("blobReadHook");
  611.   Bval& v = *(Bval*)arg;
  612.   unsigned len;
  613.   CHK(getBlobLength(h, len) == 0);
  614.   v.alloc(len);
  615.   Uint32 maxlen = 0xffffffff;
  616.   CHK(h->readData(v.m_buf, maxlen) == 0);
  617.   DBG("read " << maxlen << " bytes");
  618.   CHK(len == maxlen);
  619.   return 0;
  620. }
  621. static int
  622. setBlobReadHook(NdbBlob* h, Bval& v)
  623. {
  624.   DBG("setBlobReadHook");
  625.   CHK(h->setActiveHook(blobReadHook, &v) == 0);
  626.   return 0;
  627. }
  628. static int
  629. setBlobReadHook(Tup& tup)
  630. {
  631.   CHK(setBlobReadHook(g_bh1, tup.m_blob1) == 0);
  632.   if (! g_opt.m_oneblob)
  633.     CHK(setBlobReadHook(g_bh2, tup.m_blob2) == 0);
  634.   return 0;
  635. }
  636. // verify blob data
  637. static int
  638. verifyHeadInline(const Bcol& c, const Bval& v, NdbRecAttr* ra)
  639. {
  640.   if (v.m_val == 0) {
  641.     CHK(ra->isNULL() == 1);
  642.   } else {
  643.     CHK(ra->isNULL() == 0);
  644.     const NdbBlob::Head* head = (const NdbBlob::Head*)ra->aRef();
  645.     CHK(head->length == v.m_len);
  646.     const char* data = (const char*)(head + 1);
  647.     for (unsigned i = 0; i < head->length && i < c.m_inline; i++)
  648.       CHK(data[i] == v.m_val[i]);
  649.   }
  650.   return 0;
  651. }
  652. static int
  653. verifyHeadInline(const Tup& tup)
  654. {
  655.   DBG("verifyHeadInline pk1=" << hex << tup.m_pk1);
  656.   CHK((g_con = g_ndb->startTransaction()) != 0);
  657.   CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
  658.   CHK(g_opr->readTuple() == 0);
  659.   CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
  660.   if (g_opt.m_pk2len != 0)
  661.     CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  662.   NdbRecAttr* ra1;
  663.   NdbRecAttr* ra2;
  664.   CHK((ra1 = g_opr->getValue("BL1")) != 0);
  665.   if (! g_opt.m_oneblob)
  666.     CHK((ra2 = g_opr->getValue("BL2")) != 0);
  667.   if (tup.m_exists) {
  668.     CHK(g_con->execute(Commit) == 0);
  669.     DBG("verifyHeadInline BL1");
  670.     CHK(verifyHeadInline(g_opt.m_blob1, tup.m_blob1, ra1) == 0);
  671.     if (! g_opt.m_oneblob) {
  672.       DBG("verifyHeadInline BL2");
  673.       CHK(verifyHeadInline(g_opt.m_blob2, tup.m_blob2, ra2) == 0);
  674.     }
  675.   } else {
  676.     CHK(g_con->execute(Commit) == -1 && g_con->getNdbError().code == 626);
  677.   }
  678.   g_ndb->closeTransaction(g_con);
  679.   g_opr = 0;
  680.   g_con = 0;
  681.   return 0;
  682. }
  683. static int
  684. verifyBlobTable(const Bcol& b, const Bval& v, Uint32 pk1, bool exists)
  685. {
  686.   DBG("verify " << b.m_btname << " pk1=" << hex << pk1);
  687.   NdbRecAttr* ra_pk;
  688.   NdbRecAttr* ra_part;
  689.   NdbRecAttr* ra_data;
  690.   NdbResultSet* rs;
  691.   CHK((g_con = g_ndb->startTransaction()) != 0);
  692.   CHK((g_ops = g_con->getNdbScanOperation(b.m_btname)) != 0);
  693.   CHK((rs = g_ops->readTuples()) != 0);
  694.   CHK((ra_pk = g_ops->getValue("PK")) != 0);
  695.   CHK((ra_part = g_ops->getValue("PART")) != 0);
  696.   CHK((ra_data = g_ops->getValue("DATA")) != 0);
  697.   CHK(g_con->execute(NoCommit) == 0);
  698.   unsigned partcount;
  699.   if (! exists || v.m_len <= b.m_inline)
  700.     partcount = 0;
  701.   else
  702.     partcount = (v.m_len - b.m_inline + b.m_partsize - 1) / b.m_partsize;
  703.   char* seen = new char [partcount];
  704.   memset(seen, 0, partcount);
  705.   while (1) {
  706.     int ret;
  707.     CHK((ret = rs->nextResult()) == 0 || ret == 1);
  708.     if (ret == 1)
  709.       break;
  710.     if (pk1 != ra_pk->u_32_value())
  711.       continue;
  712.     Uint32 part = ra_part->u_32_value();
  713.     DBG("part " << part << " of " << partcount);
  714.     const char* data = ra_data->aRef();
  715.     CHK(part < partcount && ! seen[part]);
  716.     seen[part] = 1;
  717.     unsigned n = b.m_inline + part * b.m_partsize;
  718.     assert(exists && v.m_val != 0 && n < v.m_len);
  719.     unsigned m = v.m_len - n;
  720.     if (m > b.m_partsize)
  721.       m = b.m_partsize;
  722.     CHK(memcmp(data, v.m_val + n, m) == 0);
  723.   }
  724.   for (unsigned i = 0; i < partcount; i++)
  725.     CHK(seen[i] == 1);
  726.   g_ndb->closeTransaction(g_con);
  727.   g_ops = 0;
  728.   g_con = 0;
  729.   return 0;
  730. }
  731. static int
  732. verifyBlobTable(const Tup& tup)
  733. {
  734.   CHK(verifyBlobTable(g_opt.m_blob1, tup.m_blob1, tup.m_pk1, tup.m_exists) == 0);
  735.   if (! g_opt.m_oneblob)
  736.     CHK(verifyBlobTable(g_opt.m_blob2, tup.m_blob2, tup.m_pk1, tup.m_exists) == 0);
  737.   return 0;
  738. }
  739. static int
  740. verifyBlob()
  741. {
  742.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  743.     const Tup& tup = g_tups[k];
  744.     DBG("verifyBlob pk1=" << hex << tup.m_pk1);
  745.     CHK(verifyHeadInline(tup) == 0);
  746.     CHK(verifyBlobTable(tup) == 0);
  747.   }
  748.   return 0;
  749. }
  750. // operations
  751. static const char* stylename[3] = {
  752.   "style=getValue/setValue",
  753.   "style=setActiveHook",
  754.   "style=readData/writeData"
  755. };
  756. // pk ops
  757. static int
  758. insertPk(int style)
  759. {
  760.   DBG("--- insertPk " << stylename[style] << " ---");
  761.   unsigned n = 0;
  762.   CHK((g_con = g_ndb->startTransaction()) != 0);
  763.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  764.     Tup& tup = g_tups[k];
  765.     DBG("insertPk pk1=" << hex << tup.m_pk1);
  766.     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
  767.     CHK(g_opr->insertTuple() == 0);
  768.     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
  769.     if (g_opt.m_pk2len != 0)
  770.       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  771.     CHK(getBlobHandles(g_opr) == 0);
  772.     if (style == 0) {
  773.       CHK(setBlobValue(tup) == 0);
  774.     } else if (style == 1) {
  775.       // non-nullable must be set
  776.       CHK(g_bh1->setValue("", 0) == 0);
  777.       CHK(setBlobWriteHook(tup) == 0);
  778.     } else {
  779.       // non-nullable must be set
  780.       CHK(g_bh1->setValue("", 0) == 0);
  781.       CHK(g_con->execute(NoCommit) == 0);
  782.       CHK(writeBlobData(tup) == 0);
  783.     }
  784.     // just another trap
  785.     if (urandom(10) == 0)
  786.       CHK(g_con->execute(NoCommit) == 0);
  787.     if (++n == g_opt.m_batch) {
  788.       CHK(g_con->execute(Commit) == 0);
  789.       g_ndb->closeTransaction(g_con);
  790.       CHK((g_con = g_ndb->startTransaction()) != 0);
  791.       n = 0;
  792.     }
  793.     g_opr = 0;
  794.     tup.m_exists = true;
  795.   }
  796.   if (n != 0) {
  797.     CHK(g_con->execute(Commit) == 0);
  798.     n = 0;
  799.   }
  800.   g_ndb->closeTransaction(g_con);
  801.   g_con = 0;
  802.   return 0;
  803. }
  804. static int
  805. readPk(int style)
  806. {
  807.   DBG("--- readPk " << stylename[style] << " ---");
  808.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  809.     Tup& tup = g_tups[k];
  810.     DBG("readPk pk1=" << hex << tup.m_pk1);
  811.     CHK((g_con = g_ndb->startTransaction()) != 0);
  812.     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
  813.     CHK(g_opr->readTuple() == 0);
  814.     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
  815.     if (g_opt.m_pk2len != 0)
  816.       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  817.     CHK(getBlobHandles(g_opr) == 0);
  818.     if (style == 0) {
  819.       CHK(getBlobValue(tup) == 0);
  820.     } else if (style == 1) {
  821.       CHK(setBlobReadHook(tup) == 0);
  822.     } else {
  823.       CHK(g_con->execute(NoCommit) == 0);
  824.       CHK(readBlobData(tup) == 0);
  825.     }
  826.     CHK(g_con->execute(Commit) == 0);
  827.     if (style == 0 || style == 1) {
  828.       CHK(verifyBlobValue(tup) == 0);
  829.     }
  830.     g_ndb->closeTransaction(g_con);
  831.     g_opr = 0;
  832.     g_con = 0;
  833.   }
  834.   return 0;
  835. }
  836. static int
  837. updatePk(int style)
  838. {
  839.   DBG("--- updatePk " << stylename[style] << " ---");
  840.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  841.     Tup& tup = g_tups[k];
  842.     DBG("updatePk pk1=" << hex << tup.m_pk1);
  843.     CHK((g_con = g_ndb->startTransaction()) != 0);
  844.     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
  845.     CHK(g_opr->updateTuple() == 0);
  846.     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
  847.     if (g_opt.m_pk2len != 0)
  848.       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  849.     CHK(getBlobHandles(g_opr) == 0);
  850.     if (style == 0) {
  851.       CHK(setBlobValue(tup) == 0);
  852.     } else if (style == 1) {
  853.       CHK(setBlobWriteHook(tup) == 0);
  854.     } else {
  855.       CHK(g_con->execute(NoCommit) == 0);
  856.       CHK(writeBlobData(tup) == 0);
  857.     }
  858.     CHK(g_con->execute(Commit) == 0);
  859.     g_ndb->closeTransaction(g_con);
  860.     g_opr = 0;
  861.     g_con = 0;
  862.     tup.m_exists = true;
  863.   }
  864.   return 0;
  865. }
  866. static int
  867. writePk(int style)
  868. {
  869.   DBG("--- writePk " << stylename[style] << " ---");
  870.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  871.     Tup& tup = g_tups[k];
  872.     DBG("writePk pk1=" << hex << tup.m_pk1);
  873.     CHK((g_con = g_ndb->startTransaction()) != 0);
  874.     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
  875.     CHK(g_opr->writeTuple() == 0);
  876.     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
  877.     if (g_opt.m_pk2len != 0)
  878.       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  879.     CHK(getBlobHandles(g_opr) == 0);
  880.     if (style == 0) {
  881.       CHK(setBlobValue(tup) == 0);
  882.     } else if (style == 1) {
  883.       // non-nullable must be set
  884.       CHK(g_bh1->setValue("", 0) == 0);
  885.       CHK(setBlobWriteHook(tup) == 0);
  886.     } else {
  887.       // non-nullable must be set
  888.       CHK(g_bh1->setValue("", 0) == 0);
  889.       CHK(g_con->execute(NoCommit) == 0);
  890.       CHK(writeBlobData(tup) == 0);
  891.     }
  892.     CHK(g_con->execute(Commit) == 0);
  893.     g_ndb->closeTransaction(g_con);
  894.     g_opr = 0;
  895.     g_con = 0;
  896.     tup.m_exists = true;
  897.   }
  898.   return 0;
  899. }
  900. static int
  901. deletePk()
  902. {
  903.   DBG("--- deletePk ---");
  904.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  905.     Tup& tup = g_tups[k];
  906.     DBG("deletePk pk1=" << hex << tup.m_pk1);
  907.     CHK((g_con = g_ndb->startTransaction()) != 0);
  908.     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
  909.     CHK(g_opr->deleteTuple() == 0);
  910.     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
  911.     if (g_opt.m_pk2len != 0)
  912.       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  913.     CHK(g_con->execute(Commit) == 0);
  914.     g_ndb->closeTransaction(g_con);
  915.     g_opr = 0;
  916.     g_con = 0;
  917.     tup.m_exists = false;
  918.   }
  919.   return 0;
  920. }
  921. // hash index ops
  922. static int
  923. readIdx(int style)
  924. {
  925.   DBG("--- readIdx " << stylename[style] << " ---");
  926.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  927.     Tup& tup = g_tups[k];
  928.     DBG("readIdx pk1=" << hex << tup.m_pk1);
  929.     CHK((g_con = g_ndb->startTransaction()) != 0);
  930.     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
  931.     CHK(g_opx->readTuple() == 0);
  932.     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
  933.     CHK(getBlobHandles(g_opx) == 0);
  934.     if (style == 0) {
  935.       CHK(getBlobValue(tup) == 0);
  936.     } else if (style == 1) {
  937.       CHK(setBlobReadHook(tup) == 0);
  938.     } else {
  939.       CHK(g_con->execute(NoCommit) == 0);
  940.       CHK(readBlobData(tup) == 0);
  941.     }
  942.     CHK(g_con->execute(Commit) == 0);
  943.     if (style == 0 || style == 1) {
  944.       CHK(verifyBlobValue(tup) == 0);
  945.     }
  946.     g_ndb->closeTransaction(g_con);
  947.     g_opx = 0;
  948.     g_con = 0;
  949.   }
  950.   return 0;
  951. }
  952. static int
  953. updateIdx(int style)
  954. {
  955.   DBG("--- updateIdx " << stylename[style] << " ---");
  956.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  957.     Tup& tup = g_tups[k];
  958.     DBG("updateIdx pk1=" << hex << tup.m_pk1);
  959.     CHK((g_con = g_ndb->startTransaction()) != 0);
  960.     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
  961.     CHK(g_opx->updateTuple() == 0);
  962.     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
  963.     CHK(getBlobHandles(g_opx) == 0);
  964.     if (style == 0) {
  965.       CHK(setBlobValue(tup) == 0);
  966.     } else if (style == 1) {
  967.       CHK(setBlobWriteHook(tup) == 0);
  968.     } else {
  969.       CHK(g_con->execute(NoCommit) == 0);
  970.       CHK(writeBlobData(tup) == 0);
  971.     }
  972.     CHK(g_con->execute(Commit) == 0);
  973.     g_ndb->closeTransaction(g_con);
  974.     g_opx = 0;
  975.     g_con = 0;
  976.     tup.m_exists = true;
  977.   }
  978.   return 0;
  979. }
  980. static int
  981. writeIdx(int style)
  982. {
  983.   DBG("--- writeIdx " << stylename[style] << " ---");
  984.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  985.     Tup& tup = g_tups[k];
  986.     DBG("writeIdx pk1=" << hex << tup.m_pk1);
  987.     CHK((g_con = g_ndb->startTransaction()) != 0);
  988.     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
  989.     CHK(g_opx->writeTuple() == 0);
  990.     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
  991.     CHK(getBlobHandles(g_opx) == 0);
  992.     if (style == 0) {
  993.       CHK(setBlobValue(tup) == 0);
  994.     } else if (style == 1) {
  995.       // non-nullable must be set
  996.       CHK(g_bh1->setValue("", 0) == 0);
  997.       CHK(setBlobWriteHook(tup) == 0);
  998.     } else {
  999.       // non-nullable must be set
  1000.       CHK(g_bh1->setValue("", 0) == 0);
  1001.       CHK(g_con->execute(NoCommit) == 0);
  1002.       CHK(writeBlobData(tup) == 0);
  1003.     }
  1004.     CHK(g_con->execute(Commit) == 0);
  1005.     g_ndb->closeTransaction(g_con);
  1006.     g_opx = 0;
  1007.     g_con = 0;
  1008.     tup.m_exists = true;
  1009.   }
  1010.   return 0;
  1011. }
  1012. static int
  1013. deleteIdx()
  1014. {
  1015.   DBG("--- deleteIdx ---");
  1016.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  1017.     Tup& tup = g_tups[k];
  1018.     DBG("deleteIdx pk1=" << hex << tup.m_pk1);
  1019.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1020.     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
  1021.     CHK(g_opx->deleteTuple() == 0);
  1022.     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
  1023.     CHK(g_con->execute(Commit) == 0);
  1024.     g_ndb->closeTransaction(g_con);
  1025.     g_opx = 0;
  1026.     g_con = 0;
  1027.     tup.m_exists = false;
  1028.   }
  1029.   return 0;
  1030. }
  1031. // scan ops table and index
  1032. static int
  1033. readScan(int style, bool idx)
  1034. {
  1035.   DBG("--- " << "readScan" << (idx ? "Idx" : "") << " " << stylename[style] << " ---");
  1036.   Tup tup;
  1037.   tup.alloc();  // allocate buffers
  1038.   NdbResultSet* rs;
  1039.   CHK((g_con = g_ndb->startTransaction()) != 0);
  1040.   if (! idx) {
  1041.     CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
  1042.   } else {
  1043.     CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
  1044.   }
  1045.   CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Read)) != 0);
  1046.   CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
  1047.   if (g_opt.m_pk2len != 0)
  1048.     CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
  1049.   CHK(getBlobHandles(g_ops) == 0);
  1050.   if (style == 0) {
  1051.     CHK(getBlobValue(tup) == 0);
  1052.   } else if (style == 1) {
  1053.     CHK(setBlobReadHook(tup) == 0);
  1054.   }
  1055.   CHK(g_con->execute(NoCommit) == 0);
  1056.   unsigned rows = 0;
  1057.   while (1) {
  1058.     int ret;
  1059.     tup.m_pk1 = (Uint32)-1;
  1060.     memset(tup.m_pk2, 'x', g_opt.m_pk2len);
  1061.     CHK((ret = rs->nextResult(true)) == 0 || ret == 1);
  1062.     if (ret == 1)
  1063.       break;
  1064.     DBG("readScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
  1065.     Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
  1066.     CHK(k < g_opt.m_rows && g_tups[k].m_exists);
  1067.     tup.copyfrom(g_tups[k]);
  1068.     if (style == 0) {
  1069.       CHK(verifyBlobValue(tup) == 0);
  1070.     } else if (style == 1) {
  1071.       // execute ops generated by callbacks, if any
  1072.       CHK(verifyBlobValue(tup) == 0);
  1073.     } else {
  1074.       CHK(readBlobData(tup) == 0);
  1075.     }
  1076.     rows++;
  1077.   }
  1078.   g_ndb->closeTransaction(g_con);
  1079.   g_con = 0;
  1080.   g_ops = 0;
  1081.   CHK(g_opt.m_rows == rows);
  1082.   return 0;
  1083. }
  1084. static int
  1085. updateScan(int style, bool idx)
  1086. {
  1087.   DBG("--- " << "updateScan" << (idx ? "Idx" : "") << " " << stylename[style] << " ---");
  1088.   Tup tup;
  1089.   tup.alloc();  // allocate buffers
  1090.   NdbResultSet* rs;
  1091.   CHK((g_con = g_ndb->startTransaction()) != 0);
  1092.   if (! idx) {
  1093.     CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
  1094.   } else {
  1095.     CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
  1096.   }
  1097.   CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Exclusive)) != 0);
  1098.   CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
  1099.   if (g_opt.m_pk2len != 0)
  1100.     CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
  1101.   CHK(g_con->execute(NoCommit) == 0);
  1102.   unsigned rows = 0;
  1103.   while (1) {
  1104.     int ret;
  1105.     tup.m_pk1 = (Uint32)-1;
  1106.     memset(tup.m_pk2, 'x', g_opt.m_pk2len);
  1107.     CHK((ret = rs->nextResult(true)) == 0 || ret == 1);
  1108.     if (ret == 1)
  1109.       break;
  1110.     DBG("updateScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
  1111.     Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
  1112.     CHK(k < g_opt.m_rows && g_tups[k].m_exists);
  1113.     // calculate new blob values
  1114.     calcBval(g_tups[k], false);
  1115.     tup.copyfrom(g_tups[k]);
  1116.     CHK((g_opr = rs->updateTuple()) != 0);
  1117.     CHK(getBlobHandles(g_opr) == 0);
  1118.     if (style == 0) {
  1119.       CHK(setBlobValue(tup) == 0);
  1120.     } else if (style == 1) {
  1121.       CHK(setBlobWriteHook(tup) == 0);
  1122.     } else {
  1123.       CHK(g_con->execute(NoCommit) == 0);
  1124.       CHK(writeBlobData(tup) == 0);
  1125.     }
  1126.     CHK(g_con->execute(NoCommit) == 0);
  1127.     g_opr = 0;
  1128.     rows++;
  1129.   }
  1130.   CHK(g_con->execute(Commit) == 0);
  1131.   g_ndb->closeTransaction(g_con);
  1132.   g_con = 0;
  1133.   g_ops = 0;
  1134.   CHK(g_opt.m_rows == rows);
  1135.   return 0;
  1136. }
  1137. static int
  1138. deleteScan(bool idx)
  1139. {
  1140.   DBG("--- " << "deleteScan" << (idx ? "Idx" : "") << " ---");
  1141.   Tup tup;
  1142.   NdbResultSet* rs;
  1143.   CHK((g_con = g_ndb->startTransaction()) != 0);
  1144.   if (! idx) {
  1145.     CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
  1146.   } else {
  1147.     CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
  1148.   }
  1149.   CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Exclusive)) != 0);
  1150.   CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
  1151.   if (g_opt.m_pk2len != 0)
  1152.     CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
  1153.   CHK(g_con->execute(NoCommit) == 0);
  1154.   unsigned rows = 0;
  1155.   while (1) {
  1156.     int ret;
  1157.     tup.m_pk1 = (Uint32)-1;
  1158.     memset(tup.m_pk2, 'x', g_opt.m_pk2len);
  1159.     CHK((ret = rs->nextResult()) == 0 || ret == 1);
  1160.     if (ret == 1)
  1161.       break;
  1162.     DBG("deleteScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
  1163.     CHK(rs->deleteTuple() == 0);
  1164.     CHK(g_con->execute(NoCommit) == 0);
  1165.     Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
  1166.     CHK(k < g_opt.m_rows && g_tups[k].m_exists);
  1167.     g_tups[k].m_exists = false;
  1168.     rows++;
  1169.   }
  1170.   CHK(g_con->execute(Commit) == 0);
  1171.   g_ndb->closeTransaction(g_con);
  1172.   g_con = 0;
  1173.   g_ops = 0;
  1174.   CHK(g_opt.m_rows == rows);
  1175.   return 0;
  1176. }
  1177. // main
  1178. // from here on print always
  1179. #undef DBG
  1180. #define DBG(x) 
  1181.   do { 
  1182.     ndbout << "line " << __LINE__ << " " << x << endl; 
  1183.   } while (0)
  1184. static int
  1185. testmain()
  1186. {
  1187.   g_ndb = new Ndb(g_ncc, "TEST_DB");
  1188.   CHK(g_ndb->init() == 0);
  1189.   CHK(g_ndb->waitUntilReady() == 0);
  1190.   g_dic = g_ndb->getDictionary();
  1191.   g_tups = new Tup [g_opt.m_rows];
  1192.   CHK(dropTable() == 0);
  1193.   CHK(createTable() == 0);
  1194.   if (g_opt.m_bugtest != 0) {
  1195.     // test a general bug instead of blobs
  1196.     CHK((*g_opt.m_bugtest)() == 0);
  1197.     return 0;
  1198.   }
  1199.   Bcol& b1 = g_opt.m_blob1;
  1200.   CHK(NdbBlob::getBlobTableName(b1.m_btname, g_ndb, g_opt.m_tname, "BL1") == 0);
  1201.   DBG("BL1: inline=" << b1.m_inline << " part=" << b1.m_partsize << " table=" << b1.m_btname);
  1202.   if (! g_opt.m_oneblob) {
  1203.     Bcol& b2 = g_opt.m_blob2;
  1204.     CHK(NdbBlob::getBlobTableName(b2.m_btname, g_ndb, g_opt.m_tname, "BL2") == 0);
  1205.     DBG("BL2: inline=" << b2.m_inline << " part=" << b2.m_partsize << " table=" << b2.m_btname);
  1206.   }
  1207.   if (g_opt.m_seed != 0)
  1208.     srandom(g_opt.m_seed);
  1209.   for (g_loop = 0; g_opt.m_loop == 0 || g_loop < g_opt.m_loop; g_loop++) {
  1210.     int style;
  1211.     DBG("=== loop " << g_loop << " ===");
  1212.     if (g_opt.m_seed == 0)
  1213.       srandom(g_loop);
  1214.     // pk
  1215.     for (style = 0; style <= 2; style++) {
  1216.       if (! testcase('k') || ! testcase(style))
  1217.         continue;
  1218.       DBG("--- pk ops " << stylename[style] << " ---");
  1219.       if (testcase('n')) {
  1220.         calcTups(false);
  1221.         CHK(insertPk(style) == 0);
  1222.         CHK(verifyBlob() == 0);
  1223.         CHK(readPk(style) == 0);
  1224.         if (testcase('u')) {
  1225.           calcTups(style);
  1226.           CHK(updatePk(style) == 0);
  1227.           CHK(verifyBlob() == 0);
  1228.           CHK(readPk(style) == 0);
  1229.         }
  1230.         CHK(deletePk() == 0);
  1231.         CHK(verifyBlob() == 0);
  1232.       }
  1233.       if (testcase('w')) {
  1234.         calcTups(false);
  1235.         CHK(writePk(style) == 0);
  1236.         CHK(verifyBlob() == 0);
  1237.         CHK(readPk(style) == 0);
  1238.         if (testcase('u')) {
  1239.           calcTups(style);
  1240.           CHK(writePk(style) == 0);
  1241.           CHK(verifyBlob() == 0);
  1242.           CHK(readPk(style) == 0);
  1243.         }
  1244.         CHK(deletePk() == 0);
  1245.         CHK(verifyBlob() == 0);
  1246.       }
  1247.     }
  1248.     // hash index
  1249.     for (style = 0; style <= 2; style++) {
  1250.       if (! testcase('i') || ! testcase(style))
  1251.         continue;
  1252.       DBG("--- idx ops " << stylename[style] << " ---");
  1253.       if (testcase('n')) {
  1254.         calcTups(false);
  1255.         CHK(insertPk(style) == 0);
  1256.         CHK(verifyBlob() == 0);
  1257.         CHK(readIdx(style) == 0);
  1258.         if (testcase('u')) {
  1259.           calcTups(style);
  1260.           CHK(updateIdx(style) == 0);
  1261.           CHK(verifyBlob() == 0);
  1262.           CHK(readIdx(style) == 0);
  1263.         }
  1264.         CHK(deleteIdx() == 0);
  1265.         CHK(verifyBlob() == 0);
  1266.       }
  1267.       if (testcase('w')) {
  1268.         calcTups(false);
  1269.         CHK(writePk(style) == 0);
  1270.         CHK(verifyBlob() == 0);
  1271.         CHK(readIdx(style) == 0);
  1272.         if (testcase('u')) {
  1273.           calcTups(style);
  1274.           CHK(writeIdx(style) == 0);
  1275.           CHK(verifyBlob() == 0);
  1276.           CHK(readIdx(style) == 0);
  1277.         }
  1278.         CHK(deleteIdx() == 0);
  1279.         CHK(verifyBlob() == 0);
  1280.       }
  1281.     }
  1282.     // scan table
  1283.     for (style = 0; style <= 2; style++) {
  1284.       if (! testcase('s') || ! testcase(style))
  1285.         continue;
  1286.       DBG("--- table scan " << stylename[style] << " ---");
  1287.       calcTups(false);
  1288.       CHK(insertPk(style) == 0);
  1289.       CHK(verifyBlob() == 0);
  1290.       CHK(readScan(style, false) == 0);
  1291.       if (testcase('u')) {
  1292.         CHK(updateScan(style, false) == 0);
  1293.         CHK(verifyBlob() == 0);
  1294.       }
  1295.       CHK(deleteScan(false) == 0);
  1296.       CHK(verifyBlob() == 0);
  1297.     }
  1298.     // scan index
  1299.     for (style = 0; style <= 2; style++) {
  1300.       if (! testcase('r') || ! testcase(style))
  1301.         continue;
  1302.       DBG("--- index scan " << stylename[style] << " ---");
  1303.       calcTups(false);
  1304.       CHK(insertPk(style) == 0);
  1305.       CHK(verifyBlob() == 0);
  1306.       CHK(readScan(style, true) == 0);
  1307.       if (testcase('u')) {
  1308.         CHK(updateScan(style, true) == 0);
  1309.         CHK(verifyBlob() == 0);
  1310.       }
  1311.       CHK(deleteScan(true) == 0);
  1312.       CHK(verifyBlob() == 0);
  1313.     }
  1314.   }
  1315.   delete g_ndb;
  1316.   return 0;
  1317. }
  1318. // separate performance test
  1319. struct Tmr {    // stolen from testOIBasic
  1320.   Tmr() {
  1321.     clr();
  1322.   }
  1323.   void clr() {
  1324.     m_on = m_ms = m_cnt = m_time[0] = m_text[0] = 0;
  1325.   }
  1326.   void on() {
  1327.     assert(m_on == 0);
  1328.     m_on = NdbTick_CurrentMillisecond();
  1329.   }
  1330.   void off(unsigned cnt = 0) {
  1331.     NDB_TICKS off = NdbTick_CurrentMillisecond();
  1332.     assert(m_on != 0 && off >= m_on);
  1333.     m_ms += off - m_on;
  1334.     m_cnt += cnt;
  1335.     m_on = 0;
  1336.   }
  1337.   const char* time() {
  1338.     if (m_cnt == 0)
  1339.       sprintf(m_time, "%u ms", m_ms);
  1340.     else
  1341.       sprintf(m_time, "%u ms per %u ( %u ms per 1000 )", m_ms, m_cnt, (1000 * m_ms) / m_cnt);
  1342.     return m_time;
  1343.   }
  1344.   const char* pct (const Tmr& t1) {
  1345.     if (0 < t1.m_ms)
  1346.       sprintf(m_text, "%u pct", (100 * m_ms) / t1.m_ms);
  1347.     else
  1348.       sprintf(m_text, "[cannot measure]");
  1349.     return m_text;
  1350.   }
  1351.   const char* over(const Tmr& t1) {
  1352.     if (0 < t1.m_ms) {
  1353.       if (t1.m_ms <= m_ms)
  1354.         sprintf(m_text, "%u pct", (100 * (m_ms - t1.m_ms)) / t1.m_ms);
  1355.       else
  1356.         sprintf(m_text, "-%u pct", (100 * (t1.m_ms - m_ms)) / t1.m_ms);
  1357.     } else
  1358.       sprintf(m_text, "[cannot measure]");
  1359.     return m_text;
  1360.   }
  1361.   NDB_TICKS m_on;
  1362.   unsigned m_ms;
  1363.   unsigned m_cnt;
  1364.   char m_time[100];
  1365.   char m_text[100];
  1366. };
  1367. static int
  1368. testperf()
  1369. {
  1370.   if (! testcase('p'))
  1371.     return 0;
  1372.   DBG("=== perf test ===");
  1373.   g_bh1 = g_bh2 = 0;
  1374.   g_ndb = new Ndb(g_ncc, "TEST_DB");
  1375.   CHK(g_ndb->init() == 0);
  1376.   CHK(g_ndb->waitUntilReady() == 0);
  1377.   g_dic = g_ndb->getDictionary();
  1378.   NdbDictionary::Table tab(g_opt.m_tnameperf);
  1379.   if (g_dic->getTable(tab.getName()) != 0)
  1380.     CHK(g_dic->dropTable(tab) == 0);
  1381.   // col A - pk
  1382.   { NdbDictionary::Column col("A");
  1383.     col.setType(NdbDictionary::Column::Unsigned);
  1384.     col.setPrimaryKey(true);
  1385.     tab.addColumn(col);
  1386.   }
  1387.   // col B - char 20
  1388.   { NdbDictionary::Column col("B");
  1389.     col.setType(NdbDictionary::Column::Char);
  1390.     col.setLength(20);
  1391.     col.setNullable(true);
  1392.     tab.addColumn(col);
  1393.   }
  1394.   // col C - text
  1395.   { NdbDictionary::Column col("C");
  1396.     col.setType(NdbDictionary::Column::Text);
  1397.     col.setInlineSize(20);
  1398.     col.setPartSize(512);
  1399.     col.setStripeSize(1);
  1400.     col.setNullable(true);
  1401.     tab.addColumn(col);
  1402.   }
  1403.   // create
  1404.   CHK(g_dic->createTable(tab) == 0);
  1405.   Uint32 cA = 0, cB = 1, cC = 2;
  1406.   // timers
  1407.   Tmr t1;
  1408.   Tmr t2;
  1409.   // insert char (one trans)
  1410.   {
  1411.     DBG("--- insert char ---");
  1412.     t1.on();
  1413.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1414.     for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
  1415.       CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
  1416.       CHK(g_opr->insertTuple() == 0);
  1417.       CHK(g_opr->equal(cA, (char*)&k) == 0);
  1418.       CHK(g_opr->setValue(cB, "b") == 0);
  1419.       CHK(g_con->execute(NoCommit) == 0);
  1420.     }
  1421.     t1.off(g_opt.m_rowsperf);
  1422.     CHK(g_con->execute(Rollback) == 0);
  1423.     DBG(t1.time());
  1424.     g_opr = 0;
  1425.     g_con = 0;
  1426.   }
  1427.   // insert text (one trans)
  1428.   {
  1429.     DBG("--- insert text ---");
  1430.     t2.on();
  1431.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1432.     for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
  1433.       CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
  1434.       CHK(g_opr->insertTuple() == 0);
  1435.       CHK(g_opr->equal(cA, (char*)&k) == 0);
  1436.       CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0);
  1437.       CHK((g_bh1->setValue("c", 1) == 0));
  1438.       CHK(g_con->execute(NoCommit) == 0);
  1439.     }
  1440.     t2.off(g_opt.m_rowsperf);
  1441.     CHK(g_con->execute(Rollback) == 0);
  1442.     DBG(t2.time());
  1443.     g_bh1 = 0;
  1444.     g_opr = 0;
  1445.     g_con = 0;
  1446.   }
  1447.   // insert overhead
  1448.   DBG("insert overhead: " << t2.over(t1));
  1449.   t1.clr();
  1450.   t2.clr();
  1451.   // insert
  1452.   {
  1453.     DBG("--- insert for read test ---");
  1454.     unsigned n = 0;
  1455.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1456.     for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
  1457.       CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
  1458.       CHK(g_opr->insertTuple() == 0);
  1459.       CHK(g_opr->equal(cA, (char*)&k) == 0);
  1460.       CHK(g_opr->setValue(cB, "b") == 0);
  1461.       CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0);
  1462.       CHK((g_bh1->setValue("c", 1) == 0));
  1463.       if (++n == g_opt.m_batch) {
  1464.         CHK(g_con->execute(Commit) == 0);
  1465.         g_ndb->closeTransaction(g_con);
  1466.         CHK((g_con = g_ndb->startTransaction()) != 0);
  1467.         n = 0;
  1468.       }
  1469.     }
  1470.     if (n != 0) {
  1471.       CHK(g_con->execute(Commit) == 0);
  1472.       g_ndb->closeTransaction(g_con); g_con = 0;
  1473.       n = 0;
  1474.     }
  1475.     g_bh1 = 0;
  1476.     g_opr = 0;
  1477.   }
  1478.   // pk read char (one trans)
  1479.   {
  1480.     DBG("--- pk read char ---");
  1481.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1482.     Uint32 a;
  1483.     char b[20];
  1484.     t1.on();
  1485.     for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
  1486.       CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
  1487.       CHK(g_opr->readTuple() == 0);
  1488.       CHK(g_opr->equal(cA, (char*)&k) == 0);
  1489.       CHK(g_opr->getValue(cA, (char*)&a) != 0);
  1490.       CHK(g_opr->getValue(cB, b) != 0);
  1491.       a = (Uint32)-1;
  1492.       b[0] = 0;
  1493.       CHK(g_con->execute(NoCommit) == 0);
  1494.       CHK(a == k && strcmp(b, "b") == 0);
  1495.     }
  1496.     CHK(g_con->execute(Commit) == 0);
  1497.     t1.off(g_opt.m_rowsperf);
  1498.     DBG(t1.time());
  1499.     g_opr = 0;
  1500.     g_ndb->closeTransaction(g_con); g_con = 0;
  1501.   }
  1502.   // pk read text (one trans)
  1503.   {
  1504.     DBG("--- pk read text ---");
  1505.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1506.     Uint32 a;
  1507.     char c[20];
  1508.     t2.on();
  1509.     for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
  1510.       CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
  1511.       CHK(g_opr->readTuple() == 0);
  1512.       CHK(g_opr->equal(cA, (char*)&k) == 0);
  1513.       CHK(g_opr->getValue(cA, (char*)&a) != 0);
  1514.       CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0);
  1515.       a = (Uint32)-1;
  1516.       c[0] = 0;
  1517.       CHK(g_con->execute(NoCommit) == 0);
  1518.       Uint32 m = 20;
  1519.       CHK(g_bh1->readData(c, m) == 0);
  1520.       CHK(a == k && m == 1 && strcmp(c, "c") == 0);
  1521.     }
  1522.     CHK(g_con->execute(Commit) == 0);
  1523.     t2.off(g_opt.m_rowsperf);
  1524.     DBG(t2.time());
  1525.     g_ndb->closeTransaction(g_con); g_opr = 0;
  1526.     g_con = 0;
  1527.   }
  1528.   // pk read overhead
  1529.   DBG("pk read overhead: " << t2.over(t1));
  1530.   t1.clr();
  1531.   t2.clr();
  1532.   // scan read char
  1533.   {
  1534.     DBG("--- scan read char ---");
  1535.     NdbResultSet* rs;
  1536.     Uint32 a;
  1537.     char b[20];
  1538.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1539.     CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0);
  1540.     CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Read)) != 0);
  1541.     CHK(g_ops->getValue(cA, (char*)&a) != 0);
  1542.     CHK(g_ops->getValue(cB, b) != 0);
  1543.     CHK(g_con->execute(NoCommit) == 0);
  1544.     unsigned n = 0;
  1545.     t1.on();
  1546.     while (1) {
  1547.       a = (Uint32)-1;
  1548.       b[0] = 0;
  1549.       int ret;
  1550.       CHK((ret = rs->nextResult(true)) == 0 || ret == 1);
  1551.       if (ret == 1)
  1552.         break;
  1553.       CHK(a < g_opt.m_rowsperf && strcmp(b, "b") == 0);
  1554.       n++;
  1555.     }
  1556.     CHK(n == g_opt.m_rowsperf);
  1557.     t1.off(g_opt.m_rowsperf);
  1558.     DBG(t1.time());
  1559.     g_ndb->closeTransaction(g_con); g_ops = 0;
  1560.     g_con = 0;
  1561.   }
  1562.   // scan read text
  1563.   {
  1564.     DBG("--- read text ---");
  1565.     NdbResultSet* rs;
  1566.     Uint32 a;
  1567.     char c[20];
  1568.     CHK((g_con = g_ndb->startTransaction()) != 0);
  1569.     CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0);
  1570.     CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Read)) != 0);
  1571.     CHK(g_ops->getValue(cA, (char*)&a) != 0);
  1572.     CHK((g_bh1 = g_ops->getBlobHandle(cC)) != 0);
  1573.     CHK(g_con->execute(NoCommit) == 0);
  1574.     unsigned n = 0;
  1575.     t2.on();
  1576.     while (1) {
  1577.       a = (Uint32)-1;
  1578.       c[0] = 0;
  1579.       int ret;
  1580.       CHK((ret = rs->nextResult(true)) == 0 || ret == 1);
  1581.       if (ret == 1)
  1582.         break;
  1583.       Uint32 m = 20;
  1584.       CHK(g_bh1->readData(c, m) == 0);
  1585.       CHK(a < g_opt.m_rowsperf && m == 1 && strcmp(c, "c") == 0);
  1586.       n++;
  1587.     }
  1588.     CHK(n == g_opt.m_rowsperf);
  1589.     t2.off(g_opt.m_rowsperf);
  1590.     DBG(t2.time());
  1591.     g_bh1 = 0;
  1592.     g_ops = 0;
  1593.     g_ndb->closeTransaction(g_con); g_con = 0;
  1594.   }
  1595.   // scan read overhead
  1596.   DBG("scan read overhead: " << t2.over(t1));
  1597.   t1.clr();
  1598.   t2.clr();
  1599.   delete g_ndb;
  1600.   return 0;
  1601. }
  1602. // bug tests
  1603. static int
  1604. bugtest_4088()
  1605. {
  1606.   unsigned i;
  1607.   DBG("bug test 4088 - ndb api hang with mixed ops on index table");
  1608.   // insert rows
  1609.   calcTups(false);
  1610.   CHK(insertPk(false) == 0);
  1611.   // new trans
  1612.   CHK((g_con = g_ndb->startTransaction()) != 0);
  1613.   for (unsigned k = 0; k < g_opt.m_rows; k++) {
  1614.     Tup& tup = g_tups[k];
  1615.     // read table pk via index as a table
  1616.     const unsigned pkcnt = 2;
  1617.     Tup pktup[pkcnt];
  1618.     for (i = 0; i < pkcnt; i++) {
  1619.       char name[20];
  1620.       // XXX guess table id
  1621.       sprintf(name, "%d/%s", 4, g_opt.m_x1name);
  1622.       CHK((g_opr = g_con->getNdbOperation(name)) != 0);
  1623.       CHK(g_opr->readTuple() == 0);
  1624.       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
  1625.       CHK(g_opr->getValue("NDB$PK", (char*)&pktup[i].m_pk1) != 0);
  1626.     }
  1627.     // read blob inline via index as an index
  1628.     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
  1629.     CHK(g_opx->readTuple() == 0);
  1630.     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
  1631.     assert(tup.m_blob1.m_buf != 0);
  1632.     CHK(g_opx->getValue("BL1", (char*)tup.m_blob1.m_buf) != 0);
  1633.     // execute
  1634.     // BUG 4088: gets 1 tckeyconf, 1 tcindxconf, then hangs
  1635.     CHK(g_con->execute(Commit) == 0);
  1636.     // verify
  1637.     for (i = 0; i < pkcnt; i++) {
  1638.       CHK(pktup[i].m_pk1 == tup.m_pk1);
  1639.       CHK(memcmp(pktup[i].m_pk2, tup.m_pk2, g_opt.m_pk2len) == 0);
  1640.     }
  1641.     CHK(memcmp(tup.m_blob1.m_val, tup.m_blob1.m_buf, 8 + g_opt.m_blob1.m_inline) == 0);
  1642.   }
  1643.   return 0;
  1644. }
  1645. static int
  1646. bugtest_2222()
  1647. {
  1648.   return 0;
  1649. }
  1650. static int
  1651. bugtest_3333()
  1652. {
  1653.   return 0;
  1654. }
  1655. static struct {
  1656.   int m_bug;
  1657.   int (*m_test)();
  1658. } g_bugtest[] = {
  1659.   { 4088, bugtest_4088 }
  1660. };
  1661. NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
  1662. {
  1663.   ndb_init();
  1664.   while (++argv, --argc > 0) {
  1665.     const char* arg = argv[0];
  1666.     if (strcmp(arg, "-batch") == 0) {
  1667.       if (++argv, --argc > 0) {
  1668. g_opt.m_batch = atoi(argv[0]);
  1669.         continue;
  1670.       }
  1671.     }
  1672.     if (strcmp(arg, "-core") == 0) {
  1673.       g_opt.m_core = true;
  1674.       continue;
  1675.     }
  1676.     if (strcmp(arg, "-dbg") == 0) {
  1677.       g_opt.m_dbg = true;
  1678.       continue;
  1679.     }
  1680.     if (strcmp(arg, "-dbgall") == 0) {
  1681.       g_opt.m_dbg = true;
  1682.       g_opt.m_dbgall = true;
  1683.       putenv(strdup("NDB_BLOB_DEBUG=1"));
  1684.       continue;
  1685.     }
  1686.     if (strcmp(arg, "-dbug") == 0) {
  1687.       if (++argv, --argc > 0) {
  1688.         g_opt.m_dbug = strdup(argv[0]);
  1689. continue;
  1690.       }
  1691.     }
  1692.     if (strcmp(arg, "-full") == 0) {
  1693.       g_opt.m_full = true;
  1694.       continue;
  1695.     }
  1696.     if (strcmp(arg, "-loop") == 0) {
  1697.       if (++argv, --argc > 0) {
  1698. g_opt.m_loop = atoi(argv[0]);
  1699. continue;
  1700.       }
  1701.     }
  1702.     if (strcmp(arg, "-parts") == 0) {
  1703.       if (++argv, --argc > 0) {
  1704. g_opt.m_parts = atoi(argv[0]);
  1705. continue;
  1706.       }
  1707.     }
  1708.     if (strcmp(arg, "-rows") == 0) {
  1709.       if (++argv, --argc > 0) {
  1710. g_opt.m_rows = atoi(argv[0]);
  1711. continue;
  1712.       }
  1713.     }
  1714.     if (strcmp(arg, "-rowsperf") == 0) {
  1715.       if (++argv, --argc > 0) {
  1716. g_opt.m_rowsperf = atoi(argv[0]);
  1717. continue;
  1718.       }
  1719.     }
  1720.     if (strcmp(arg, "-seed") == 0) {
  1721.       if (++argv, --argc > 0) {
  1722. g_opt.m_seed = atoi(argv[0]);
  1723. continue;
  1724.       }
  1725.     }
  1726.     if (strcmp(arg, "-skip") == 0) {
  1727.       if (++argv, --argc > 0) {
  1728.         g_opt.m_skip = strdup(argv[0]);
  1729. continue;
  1730.       }
  1731.     }
  1732.     if (strcmp(arg, "-test") == 0) {
  1733.       if (++argv, --argc > 0) {
  1734.         g_opt.m_test = strdup(argv[0]);
  1735. continue;
  1736.       }
  1737.     }
  1738.     // metadata
  1739.     if (strcmp(arg, "-pk2len") == 0) {
  1740.       if (++argv, --argc > 0) {
  1741. g_opt.m_pk2len = atoi(argv[0]);
  1742.         if (g_opt.m_pk2len <= g_max_pk2len)
  1743.           continue;
  1744.       }
  1745.     }
  1746.     if (strcmp(arg, "-oneblob") == 0) {
  1747.       g_opt.m_oneblob = true;
  1748.       continue;
  1749.     }
  1750.     // bugs
  1751.     if (strcmp(arg, "-bug") == 0) {
  1752.       if (++argv, --argc > 0) {
  1753. g_opt.m_bug = atoi(argv[0]);
  1754.         for (unsigned i = 0; i < sizeof(g_bugtest)/sizeof(g_bugtest[0]); i++) {
  1755.           if (g_opt.m_bug == g_bugtest[i].m_bug) {
  1756.             g_opt.m_bugtest = g_bugtest[i].m_test;
  1757.             break;
  1758.           }
  1759.         }
  1760.         if (g_opt.m_bugtest != 0)
  1761.           continue;
  1762.       }
  1763.     }
  1764.     ndbout << "testOIBasic: unknown option " << arg << endl;
  1765.     printusage();
  1766.     return NDBT_ProgramExit(NDBT_WRONGARGS);
  1767.   }
  1768.   if (g_opt.m_dbug != 0) {
  1769.     DBUG_PUSH(g_opt.m_dbug);
  1770.   }
  1771.   if (g_opt.m_pk2len == 0) {
  1772.     char b[100];
  1773.     b[0] = 0;
  1774.     if (g_opt.m_skip != 0)
  1775.       strcpy(b, g_opt.m_skip);
  1776.     strcat(b, "i");
  1777.     strcat(b, "r");
  1778.     g_opt.m_skip = strdup(b);
  1779.   }
  1780.   g_ncc = new Ndb_cluster_connection();
  1781.   if (g_ncc->connect(30) != 0 || testmain() == -1 || testperf() == -1) {
  1782.     ndbout << "line " << __LINE__ << " FAIL loop=" << g_loop << endl;
  1783.     return NDBT_ProgramExit(NDBT_FAILED);
  1784.   }
  1785.   delete g_ncc;
  1786.   g_ncc = 0;
  1787.   return NDBT_ProgramExit(NDBT_OK);
  1788. }
  1789. // vim: set sw=2 et: