List:Commits« Previous MessageNext Message »
From:pekka Date:March 31 2007 4:26pm
Subject:bk commit into 5.1 tree (pekka:1.2504)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of pekka. When pekka does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-03-31 18:26:33+02:00, pekka@stripped +8 -0
  ndb - wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/include/ndbapi/NdbBlob.hpp@stripped, 2007-03-31 18:25:51+02:00, pekka@stripped +3 -2
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/include/ndbapi/NdbDictionary.hpp@stripped, 2007-03-31 18:25:51+02:00, pekka@stripped +22 -7
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/src/ndbapi/NdbBlob.cpp@stripped, 2007-03-31 18:25:51+02:00, pekka@stripped +59 -27
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp@stripped, 2007-03-31 18:25:51+02:00, pekka@stripped +7 -3
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp@stripped, 2007-03-31 18:25:52+02:00, pekka@stripped +7 -4
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp@stripped, 2007-03-31 18:25:52+02:00, pekka@stripped +1 -1
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/test/ndbapi/testBlobs.cpp@stripped, 2007-03-31 18:25:52+02:00, pekka@stripped +256 -125
    wl#3717 blobs V2 default partitioning + test program improvements

  storage/ndb/test/ndbapi/test_event_merge.cpp@stripped, 2007-03-31 18:25:52+02:00, pekka@stripped +57 -23
    wl#3717 blobs V2 default partitioning + test program improvements

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	pekka
# Host:	clam.(none)
# Root:	/export/space/pekka/ndb/version/my51-wl3717-a

--- 1.90/storage/ndb/include/ndbapi/NdbDictionary.hpp	2007-03-22 09:15:04 +01:00
+++ 1.91/storage/ndb/include/ndbapi/NdbDictionary.hpp	2007-03-31 18:25:51 +02:00
@@ -353,8 +353,7 @@
 
     /**
      * For blob, get "inline size" i.e. number of initial bytes
-     * to store in table's blob attribute.  This part is normally in
-     * main memory and can be indexed and interpreted.
+     * to store in table's blob attribute.
      */
     int getInlineSize() const;
 
@@ -478,22 +477,38 @@
     void setCharset(CHARSET_INFO* cs);
 
     /**
-     * For blob, get "inline size" i.e. number of initial bytes
+     * For blob, set "inline size" i.e. number of initial bytes
      * to store in table's blob attribute.  This part is normally in
-     * main memory and can be indexed and interpreted.
+     * main memory.  It can not currently be indexed.
      */
     void setInlineSize(int size);
 
     /**
-     * For blob, get "part size" i.e. number of bytes to store in
+     * For blob, set "part size" i.e. number of bytes to store in
      * each tuple of the "blob table".  Can be set to zero to omit parts
      * and to allow only inline bytes ("tinyblob").
      */
     void setPartSize(int size);
 
     /**
-     * For blob, get "stripe size" i.e. number of consecutive
-     * <em>parts</em> to store in each node group.
+     * For blob, set "stripe size" i.e. number of consecutive
+     * <em>parts</em> to store in a fragment, before moving to
+     * another (random) fragment.
+     *
+     * Striping may improve performance for large blobs
+     * since blob part operations are done in parallel.
+     * Optimal stripe size depends on the transport e.g. tcp/ip.
+     *
+     * Example: Given part size 2048 bytes, set stripe size 8.
+     * This assigns i/o in 16k chunks to each fragment.
+     *
+     * Blobs V1 required non-zero stripe size.  Blobs V2
+     * (created in version >= 5.1.x) have following behaviour:
+     *
+     * Default stripe size is zero, which means no striping and
+     * also that blob part data is stored in the same node group
+     * as the primary table row.  This is done by giving blob parts
+     * table same partition key as the primary table.
      */
     void setStripeSize(int size);
 

--- 1.165/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp	2007-03-22 09:15:04 +01:00
+++ 1.166/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp	2007-03-31 18:25:51 +02:00
@@ -205,7 +205,7 @@
   case Blob:
     m_precision = 256;
     m_scale = 8000;
-    m_length = 4;
+    m_length = 0; // default no striping
     m_cs = NULL;
     m_arrayType = NDB_ARRAYTYPE_MEDIUM_VAR;
     m_blobVersion = NDB_BLOB_V2;
@@ -213,7 +213,7 @@
   case Text:
     m_precision = 256;
     m_scale = 8000;
-    m_length = 4;
+    m_length = 0;
     m_cs = default_cs;
     m_arrayType = NDB_ARRAYTYPE_MEDIUM_VAR;
     m_blobVersion = NDB_BLOB_V2;
@@ -2344,7 +2344,11 @@
     if (! c.getBlobType() || c.getPartSize() == 0)
       continue;
     NdbTableImpl bt;
-    NdbBlob::getBlobTable(bt, &t, &c);
+    NdbError error;
+    if (NdbBlob::getBlobTable(bt, &t, &c, error) == -1) {
+      m_error.code = error.code;
+      DBUG_RETURN(-1);
+    }
     NdbDictionary::Column::StorageType 
       d = NdbDictionary::Column::StorageTypeDisk;
     if (orig.m_columns[i]->getStorageType() == d)

--- 1.85/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp	2007-03-28 18:37:06 +02:00
+++ 1.86/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp	2007-03-31 18:25:52 +02:00
@@ -458,7 +458,7 @@
 }
 
 Uint32
-NdbEventOperationImpl::get_blob_part_no()
+NdbEventOperationImpl::get_blob_part_no(bool hasDist)
 {
   assert(theBlobVersion == 1 || theBlobVersion == 2);
   assert(theMainOp != NULL);
@@ -466,10 +466,11 @@
   assert(m_data_item != NULL);
   LinearSectionPtr (&ptr)[3] = m_data_item->ptr;
 
-  uint pos = 0; // PK and DIST to skip
+  uint pos = 0; // PK and possibly DIST to skip
 
   if (unlikely(theBlobVersion == 1)) {
     pos += AttributeHeader(ptr[0].p[0]).getDataSize();
+    assert(hasDist);
     pos += AttributeHeader(ptr[0].p[1]).getDataSize();
   } else {
     uint n = mainTable->m_noOfKeys;
@@ -477,7 +478,8 @@
     for (i = 0; i < n; i++) {
       pos += AttributeHeader(ptr[0].p[i]).getDataSize();
     }
-    pos += AttributeHeader(ptr[0].p[n]).getDataSize();
+    if (hasDist)
+      pos += AttributeHeader(ptr[0].p[n]).getDataSize();
   }
 
   assert(pos < ptr[1].sz);
@@ -494,6 +496,7 @@
                       part, count, blob->theEventBlobVersion));
 
   NdbEventOperationImpl* blob_op = blob->theBlobEventOp;
+  const bool hasDist = (blob->theStripeSize != 0);
 
   EventBufData* main_data = m_data_item;
   DBUG_PRINT_EVENT("info", ("main_data=%p", main_data));
@@ -527,7 +530,7 @@
     int r = blob_op->receive_event();
     assert(r > 0);
     // XXX should be: no = blob->theBlobEventPartValue
-    Uint32 no = blob_op->get_blob_part_no();
+    Uint32 no = blob_op->get_blob_part_no(hasDist);
 
     DBUG_PRINT_EVENT("info", ("part_data=%p part no=%u part sz=%u", data, no, sz));
 

--- 1.36/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp	2007-03-28 18:37:06 +02:00
+++ 1.37/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp	2007-03-31 18:25:52 +02:00
@@ -356,7 +356,7 @@
   NdbRecAttr *getValue(const NdbColumnImpl *, char *aValue, int n);
   NdbBlob *getBlobHandle(const char *colName, int n);
   NdbBlob *getBlobHandle(const NdbColumnImpl *, int n);
-  Uint32 get_blob_part_no();
+  Uint32 get_blob_part_no(bool hasDist);
   int readBlobParts(char* buf, NdbBlob* blob,
                     Uint32 part, Uint32 count, Uint16* lenLoc);
   int receive_event();

--- 1.40/storage/ndb/test/ndbapi/testBlobs.cpp	2007-03-28 18:37:06 +02:00
+++ 1.41/storage/ndb/test/ndbapi/testBlobs.cpp	2007-03-31 18:25:52 +02:00
@@ -24,25 +24,6 @@
 #include <NdbTest.hpp>
 #include <NdbTick.h>
 
-struct Bcol {
-  bool m_nullable;
-  unsigned m_inline;
-  unsigned m_partsize;
-  unsigned m_stripe;
-  char m_btname[200];
-  Bcol(bool a, unsigned b, unsigned c, unsigned d) :
-    m_nullable(a),
-    m_inline(b),
-    m_partsize(c),
-    m_stripe(d)
-    {}
-};
-
-#define TEST_BLOBS_SIZE1        false, 240, 2000, 4
-#define TEST_BLOBS_SIZE2        true, 99, 55, 1
-#define TEST_BLOBS_MIN_SIZE1    false, 8, 8, 4
-#define TEST_BLOBS_MIN_SIZE2    true, 9, 5, 1
-
 struct Opt {
   unsigned m_batch;
   bool m_core;
@@ -64,9 +45,12 @@
   const char* m_x2name;  // ordered index
   unsigned m_pk1off;
   unsigned m_pk2len;
+  unsigned m_pk2totlen;
+  bool m_pk2fixed;
+  bool m_pk2binary;
+  bool m_pk2part;
+  uint m_pk2type;
   bool m_oneblob;
-  Bcol m_blob1;
-  Bcol m_blob2;
   // perf
   const char* m_tnameperf;
   unsigned m_rowsperf;
@@ -94,9 +78,11 @@
     m_x2name("TB1X2"),
     m_pk1off(0x12340000),
     m_pk2len(55),
+    m_pk2totlen(0),
+    m_pk2fixed(false),
+    m_pk2binary(false),
+    m_pk2part(false),
     m_oneblob(false),
-    m_blob1(TEST_BLOBS_SIZE1), // head+inline=256 bytes
-    m_blob2(TEST_BLOBS_SIZE2),
     // perf
     m_tnameperf("TB2"),
     m_rowsperf(10000),
@@ -106,7 +92,7 @@
   }
 };
 
-static const unsigned g_max_pk2len = 256;
+static const unsigned g_max_pk2len = 255;
 
 static void
 printusage()
@@ -130,7 +116,10 @@
     << "  -test xxx   only given tests (see list) [all tests]" << endl
     << "  -version N  blob version 1 or 2 [" << d.m_blob_version << "]" << endl
     << "metadata" << endl
-    << "  -pk2len N   length of PK2 [" << d.m_pk2len << "/" << g_max_pk2len <<"]" << endl
+    << "  -pk2len N   length of PK2, zero omits PK2,PK3  [" << d.m_pk2len << "/" << g_max_pk2len <<"]" << endl
+    << "  -pk2fixed   PK2 is Char [default Varchar]" << endl
+    << "  -pk2binary  PK2 is Binary or Varbinary" << endl
+    << "  -pk2part    partition primary table by PK2" << endl
     << "  -oneblob    only 1 blob attribute [default 2]" << endl
     << "testcases for test/skip" << endl
     << "  k           primary key ops" << endl
@@ -230,6 +219,44 @@
     ndbout << "line " << __LINE__ << " " << x << endl; \
   } while (0)
 
+
+struct Bcol {
+  int m_type;
+  int m_version;
+  bool m_nullable;
+  uint m_inline;
+  uint m_partsize;
+  uint m_stripe;
+  char m_btname[200];
+  Bcol() { memset(this, 0, sizeof(*this)); }
+};
+
+static Bcol g_blob1;
+static Bcol g_blob2;
+
+static void
+initblobs()
+{
+  {
+    Bcol& b = g_blob1;
+    b.m_type = NdbDictionary::Column::Text;
+    b.m_version = g_opt.m_blob_version;
+    b.m_nullable = false;
+    b.m_inline = g_opt.m_min ? 8 : 240;
+    b.m_partsize = g_opt.m_min ? 8 : 2000;
+    b.m_stripe = b.m_version == 1 ? 4 : 0;
+  }
+  {
+    Bcol& b = g_blob2;
+    b.m_type = NdbDictionary::Column::Blob;
+    b.m_version = g_opt.m_blob_version;
+    b.m_nullable = true;
+    b.m_inline = g_opt.m_min ? 9 : 99;
+    b.m_partsize = g_opt.m_min ? 5 : 55;
+    b.m_stripe = 3;
+  }
+}
+
 static int
 dropTable()
 {
@@ -244,6 +271,7 @@
 {
   NdbDictionary::Table tab(g_opt.m_tname);
   tab.setLogging(false);
+  tab.setFragmentType(NdbDictionary::Object::FragAllLarge);
   // col PK1 - Uint32
   { NdbDictionary::Column col("PK1");
     col.setType(NdbDictionary::Column::Unsigned);
@@ -252,43 +280,54 @@
   }
   // col BL1 - Text not-nullable
   { NdbDictionary::Column col("BL1");
-    const Bcol& b = g_opt.m_blob1;
-    col.setType(NdbDictionary::Column::Text);
-    col.setBlobVersion(g_opt.m_blob_version);
+    const Bcol& b = g_blob1;
+    col.setType((NdbDictionary::Column::Type)b.m_type);
+    col.setBlobVersion(b.m_version);
+    col.setNullable(b.m_nullable);
     col.setInlineSize(b.m_inline);
     col.setPartSize(b.m_partsize);
     col.setStripeSize(b.m_stripe);
     tab.addColumn(col);
   }
-  // col PK2 - Char[55]
+  // col PK2 - Char or Varchar, charset binary
   if (g_opt.m_pk2len != 0)
   { NdbDictionary::Column col("PK2");
-    col.setType(NdbDictionary::Column::Char);
+    col.setType((NdbDictionary::Column::Type)g_opt.m_pk2type);
     col.setLength(g_opt.m_pk2len);
     col.setPrimaryKey(true);
+    if (g_opt.m_pk2part)
+      col.setPartitionKey(true);
     tab.addColumn(col);
   }
   // col BL2 - Blob nullable
   if (! g_opt.m_oneblob)
   { NdbDictionary::Column col("BL2");
-    const Bcol& b = g_opt.m_blob2;
-    col.setType(NdbDictionary::Column::Blob);
-    col.setBlobVersion(g_opt.m_blob_version);
-    col.setNullable(true);
+    const Bcol& b = g_blob2;
+    col.setType((NdbDictionary::Column::Type)b.m_type);
+    col.setBlobVersion(b.m_version);
+    col.setNullable(b.m_nullable);
     col.setInlineSize(b.m_inline);
     col.setPartSize(b.m_partsize);
     col.setStripeSize(b.m_stripe);
     tab.addColumn(col);
   }
+  // col PK3 - puts the Var* key PK2 between PK1 and PK3
+  if (g_opt.m_pk2len != 0)
+  { NdbDictionary::Column col("PK3");
+    col.setType(NdbDictionary::Column::Smallunsigned);
+    col.setPrimaryKey(true);
+    tab.addColumn(col);
+  }
   // create table
   CHK(g_dic->createTable(tab) == 0);
-  // unique hash index on PK2
+  // unique hash index on PK2,PK3
   if (g_opt.m_pk2len != 0)
   { NdbDictionary::Index idx(g_opt.m_x1name);
     idx.setType(NdbDictionary::Index::UniqueHashIndex);
     idx.setLogging(false);
     idx.setTable(g_opt.m_tname);
     idx.addColumnName("PK2");
+    idx.addColumnName("PK3");
     CHK(g_dic->createIndex(idx) == 0);
   }
   // ordered index on PK2
@@ -321,6 +360,9 @@
     m_error_code(0)
     {}
   ~Bval() { delete [] m_val; delete [] m_buf; }
+  void alloc() {
+    alloc(m_bcol.m_inline + m_bcol.m_partsize * g_opt.m_parts);
+  }
   void alloc(unsigned buflen) {
     m_buflen = buflen;
     delete [] m_buf;
@@ -362,25 +404,32 @@
 
 struct Tup {
   bool m_exists;        // exists in table
-  Uint32 m_pk1;         // primary keys concatenated like keyinfo
-  char m_pk2[g_max_pk2len + 1];
-  Bval m_blob1;
-  Bval m_blob2;
+  Uint32 m_pk1;         // in V1 primary keys concatenated like keyinfo
+  char *m_pk2;
+  Uint16 m_pk3;
+  Bval m_bval1;
+  Bval m_bval2;
+  Uint32 m_frag;
   Tup() :
     m_exists(false),
-    m_blob1(g_opt.m_blob1),
-    m_blob2(g_opt.m_blob2)
+    m_pk2(new char [g_opt.m_pk2totlen]),
+    m_bval1(g_blob1),
+    m_bval2(g_blob2),
+    m_frag(~(Uint32)0)
     {}
-  ~Tup() { }
+  ~Tup() {
+    delete [] m_pk2;
+    m_pk2 = 0;
+  }
   // alloc buffers of max size
   void alloc() {
-    m_blob1.alloc(g_opt.m_blob1.m_inline + g_opt.m_blob1.m_partsize * g_opt.m_parts);
-    m_blob2.alloc(g_opt.m_blob2.m_inline + g_opt.m_blob2.m_partsize * g_opt.m_parts);
+    m_bval1.alloc();
+    m_bval2.alloc();
   }
   void copyfrom(const Tup& tup) {
     assert(m_pk1 == tup.m_pk1);
-    m_blob1.copyfrom(tup.m_blob1);
-    m_blob2.copyfrom(tup.m_blob2);
+    m_bval1.copyfrom(tup.m_bval1);
+    m_bval2.copyfrom(tup.m_bval2);
   }
 private:
   Tup(const Tup&);
@@ -424,24 +473,39 @@
 static void
 calcBval(Tup& tup, bool keepsize)
 {
-  calcBval(g_opt.m_blob1, tup.m_blob1, keepsize);
+  calcBval(g_blob1, tup.m_bval1, keepsize);
   if (! g_opt.m_oneblob)
-    calcBval(g_opt.m_blob2, tup.m_blob2, keepsize);
+    calcBval(g_blob2, tup.m_bval2, keepsize);
 }
 
 static void
-calcTups(bool keepsize)
+calcTups(bool keys, bool keepsize)
 {
   for (unsigned k = 0; k < g_opt.m_rows; k++) {
     Tup& tup = g_tups[k];
-    tup.m_pk1 = g_opt.m_pk1off + k;
-    for (unsigned i = 0, n = k; i < g_opt.m_pk2len; i++) {
-      if (n != 0) {
-        tup.m_pk2[i] = '0' + n % 10;
-        n = n / 10;
-      } else {
-        tup.m_pk2[i] = 'a' + i % 26;
+    if (keys) {
+      tup.m_pk1 = g_opt.m_pk1off + k;
+      {
+        char* p = tup.m_pk2;
+        uint sz = urandom(g_opt.m_pk2len + 1);
+        if (! g_opt.m_pk2fixed) {
+          *(uchar*)p = sz;
+          p++;
+        }
+        uint i = 0;
+        while (i < sz) {
+          p[i] = 'a' + urandom(26);
+          i++;
+        }
+        while (i < g_opt.m_pk2len) {
+          if (g_opt.m_pk2fixed)
+            p[i] = 0x40;
+          else
+            p[i] = 'A' + urandom(26); //garbage
+          i++;
+        }
       }
+      tup.m_pk3 = (Uint16)k;
     }
     calcBval(tup, keepsize);
   }
@@ -518,9 +582,9 @@
 static int
 setBlobValue(const Tup& tup, int error_code = 0)
 {
-  CHK(setBlobValue(g_bh1, tup.m_blob1, error_code) == 0);
+  CHK(setBlobValue(g_bh1, tup.m_bval1, error_code) == 0);
   if (! g_opt.m_oneblob)
-    CHK(setBlobValue(g_bh2, tup.m_blob2, error_code) == 0);
+    CHK(setBlobValue(g_bh2, tup.m_bval2, error_code) == 0);
   return 0;
 }
 
@@ -536,9 +600,9 @@
 static int
 getBlobValue(const Tup& tup)
 {
-  CHK(getBlobValue(g_bh1, tup.m_blob1) == 0);
+  CHK(getBlobValue(g_bh1, tup.m_bval1) == 0);
   if (! g_opt.m_oneblob)
-    CHK(getBlobValue(g_bh2, tup.m_blob2) == 0);
+    CHK(getBlobValue(g_bh2, tup.m_bval2) == 0);
   return 0;
 }
 
@@ -565,9 +629,9 @@
 static int
 verifyBlobValue(const Tup& tup)
 {
-  CHK(verifyBlobValue(g_bh1, tup.m_blob1) == 0);
+  CHK(verifyBlobValue(g_bh1, tup.m_bval1) == 0);
   if (! g_opt.m_oneblob)
-    CHK(verifyBlobValue(g_bh2, tup.m_blob2) == 0);
+    CHK(verifyBlobValue(g_bh2, tup.m_bval2) == 0);
   return 0;
 }
 
@@ -612,11 +676,11 @@
 static int
 writeBlobData(Tup& tup, int error_code = 0)
 {
-  tup.m_blob1.m_error_code = error_code;
-  CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
+  tup.m_bval1.m_error_code = error_code;
+  CHK(writeBlobData(g_bh1, tup.m_bval1) == 0);
   if (! g_opt.m_oneblob) {
-    tup.m_blob2.m_error_code = error_code;
-    CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
+    tup.m_bval2.m_error_code = error_code;
+    CHK(writeBlobData(g_bh2, tup.m_bval2) == 0);
   }
   return 0;
 }
@@ -660,9 +724,9 @@
 static int
 readBlobData(const Tup& tup)
 {
-  CHK(readBlobData(g_bh1, tup.m_blob1) == 0);
+  CHK(readBlobData(g_bh1, tup.m_bval1) == 0);
   if (! g_opt.m_oneblob)
-    CHK(readBlobData(g_bh2, tup.m_blob2) == 0);
+    CHK(readBlobData(g_bh2, tup.m_bval2) == 0);
   return 0;
 }
 
@@ -691,9 +755,9 @@
 static int
 setBlobWriteHook(Tup& tup, int error_code = 0)
 {
-  CHK(setBlobWriteHook(g_bh1, tup.m_blob1, error_code) == 0);
+  CHK(setBlobWriteHook(g_bh1, tup.m_bval1, error_code) == 0);
   if (! g_opt.m_oneblob)
-    CHK(setBlobWriteHook(g_bh2, tup.m_blob2, error_code) == 0);
+    CHK(setBlobWriteHook(g_bh2, tup.m_bval2, error_code) == 0);
   return 0;
 }
 
@@ -726,53 +790,59 @@
 static int
 setBlobReadHook(Tup& tup)
 {
-  CHK(setBlobReadHook(g_bh1, tup.m_blob1) == 0);
+  CHK(setBlobReadHook(g_bh1, tup.m_bval1) == 0);
   if (! g_opt.m_oneblob)
-    CHK(setBlobReadHook(g_bh2, tup.m_blob2) == 0);
+    CHK(setBlobReadHook(g_bh2, tup.m_bval2) == 0);
   return 0;
 }
 
 // verify blob data
 
 static int
-verifyHeadInline(const Bcol& c, const Bval& v, NdbRecAttr* ra)
+verifyHeadInline(const Bcol& b, const Bval& v, NdbRecAttr* ra)
 {
   if (v.m_val == 0) {
     CHK(ra->isNULL() == 1);
   } else {
     CHK(ra->isNULL() == 0);
     NdbBlob::Head head;
-    NdbBlob::unpackBlobHead(head, ra->aRef(), g_opt.m_blob_version);
+    NdbBlob::unpackBlobHead(head, ra->aRef(), b.m_version);
     CHK(head.length == v.m_len);
     const char* data = ra->aRef() + head.headsize;
-    for (unsigned i = 0; i < head.length && i < c.m_inline; i++)
+    for (unsigned i = 0; i < head.length && i < b.m_inline; i++)
       CHK(data[i] == v.m_val[i]);
   }
   return 0;
 }
 
 static int
-verifyHeadInline(const Tup& tup)
+verifyHeadInline(Tup& tup)
 {
   DBG("verifyHeadInline pk1=" << hex << tup.m_pk1);
   CHK((g_con = g_ndb->startTransaction()) != 0);
   CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
   CHK(g_opr->readTuple() == 0);
   CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-  if (g_opt.m_pk2len != 0)
+  if (g_opt.m_pk2len != 0) {
     CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+    CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+  }
   NdbRecAttr* ra1;
   NdbRecAttr* ra2;
+  NdbRecAttr* ra_frag;
   CHK((ra1 = g_opr->getValue("BL1")) != 0);
   if (! g_opt.m_oneblob)
     CHK((ra2 = g_opr->getValue("BL2")) != 0);
+  CHK((ra_frag = g_opr->getValue(NdbDictionary::Column::FRAGMENT)) != 0);
   if (tup.m_exists) {
     CHK(g_con->execute(Commit, AbortOnError) == 0);
+    tup.m_frag = ra_frag->u_32_value();
+    DBG("fragment id: " << tup.m_frag);
     DBG("verifyHeadInline BL1");
-    CHK(verifyHeadInline(g_opt.m_blob1, tup.m_blob1, ra1) == 0);
+    CHK(verifyHeadInline(g_blob1, tup.m_bval1, ra1) == 0);
     if (! g_opt.m_oneblob) {
       DBG("verifyHeadInline BL2");
-      CHK(verifyHeadInline(g_opt.m_blob2, tup.m_blob2, ra2) == 0);
+      CHK(verifyHeadInline(g_blob2, tup.m_bval2, ra2) == 0);
     }
   } else {
     CHK(g_con->execute(Commit, AbortOnError) == -1 && 
@@ -792,28 +862,34 @@
 }
 
 static int
-verifyBlobTable(const Bcol& b, const Bval& v, Uint32 pk1, bool exists)
+verifyBlobTable(const Bval& v, Uint32 pk1, Uint32 m_frag, bool exists)
 {
+  const Bcol& b = v.m_bcol;
   DBG("verify " << b.m_btname << " pk1=" << hex << pk1);
   NdbRecAttr* ra_pk = 0; // V1
   NdbRecAttr* ra_pk1 = 0; // V2
   NdbRecAttr* ra_pk2 = 0; // V2
+  NdbRecAttr* ra_pk3 = 0; // V2
   NdbRecAttr* ra_part = 0;
   NdbRecAttr* ra_data = 0;
+  NdbRecAttr* ra_frag = 0;
   CHK((g_con = g_ndb->startTransaction()) != 0);
   CHK((g_ops = g_con->getNdbScanOperation(b.m_btname)) != 0);
   CHK(g_ops->readTuples() == 0);
-  if (g_opt.m_blob_version == 1) {
+  if (b.m_version == 1) {
     CHK((ra_pk = g_ops->getValue("PK")) != 0);
     CHK((ra_part = g_ops->getValue("PART")) != 0);
     CHK((ra_data = g_ops->getValue("DATA")) != 0);
   } else {
     CHK((ra_pk1 = g_ops->getValue("PK1")) != 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK((ra_pk2 = g_ops->getValue("PK2")) != 0);
+      CHK((ra_pk3 = g_ops->getValue("PK3")) != 0);
+    }
     CHK((ra_part = g_ops->getValue("NDB$PART")) != 0);
     CHK((ra_data = g_ops->getValue("NDB$DATA")) != 0);
   }
+  CHK((ra_frag = g_ops->getValue(NdbDictionary::Column::FRAGMENT)) != 0);
   CHK(g_con->execute(NoCommit) == 0);
   unsigned partcount;
   if (! exists || v.m_len <= b.m_inline)
@@ -827,7 +903,7 @@
     CHK((ret = g_ops->nextResult()) == 0 || ret == 1);
     if (ret == 1)
       break;
-    if (g_opt.m_blob_version == 1) {
+    if (b.m_version == 1) {
       if (pk1 != ra_pk->u_32_value())
         continue;
     } else {
@@ -844,7 +920,7 @@
     if (m > b.m_partsize)
       m = b.m_partsize;
     const char* data = ra_data->aRef();
-    if (g_opt.m_blob_version == 1)
+    if (b.m_version == 1)
       ;
     else {
       unsigned sz = getvarsize(data);
@@ -857,6 +933,11 @@
         CHK(sz == m);
     }
     CHK(memcmp(data, v.m_val + n, m) == 0);
+    if (b.m_stripe == 0) {
+      Uint32 frag = ra_frag->u_32_value();
+      CHK(frag == frag);
+      DBG("verified frag id " << frag);
+    }
   }
   for (unsigned i = 0; i < partcount; i++)
     CHK(seen[i] == 1);
@@ -870,9 +951,9 @@
 static int
 verifyBlobTable(const Tup& tup)
 {
-  CHK(verifyBlobTable(g_opt.m_blob1, tup.m_blob1, tup.m_pk1, tup.m_exists) == 0);
+  CHK(verifyBlobTable(tup.m_bval1, tup.m_pk1, tup.m_frag, tup.m_exists) == 0);
   if (! g_opt.m_oneblob)
-    CHK(verifyBlobTable(g_opt.m_blob2, tup.m_blob2, tup.m_pk1, tup.m_exists) == 0);
+    CHK(verifyBlobTable(tup.m_bval2, tup.m_pk1, tup.m_frag, tup.m_exists) == 0);
   return 0;
 }
 
@@ -880,7 +961,7 @@
 verifyBlob()
 {
   for (unsigned k = 0; k < g_opt.m_rows; k++) {
-    const Tup& tup = g_tups[k];
+    Tup& tup = g_tups[k];
     DBG("verifyBlob pk1=" << hex << tup.m_pk1);
     CHK(verifyHeadInline(tup) == 0);
     CHK(verifyBlobTable(tup) == 0);
@@ -910,8 +991,10 @@
     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
     CHK(g_opr->insertTuple() == 0);
     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+    }
     CHK(getBlobHandles(g_opr) == 0);
     if (style == 0) {
       CHK(setBlobValue(tup) == 0);
@@ -957,8 +1040,10 @@
     else
       CHK(g_opr->readTuple(NdbOperation::LM_CommittedRead) == 0);
     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+    }
     CHK(getBlobHandles(g_opr) == 0);
     if (style == 0) {
       CHK(getBlobValue(tup) == 0);
@@ -1004,8 +1089,10 @@
         CHK(g_opr->readTuple() == 0);
       }
       CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-      if (g_opt.m_pk2len != 0)
+      if (g_opt.m_pk2len != 0) {
         CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+        CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+      }
       CHK(getBlobHandles(g_opr) == 0);
       if (style == 0) {
         CHK(setBlobValue(tup, error_code) == 0);
@@ -1040,8 +1127,10 @@
     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
     CHK(g_opr->writeTuple() == 0);
     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+    }
     CHK(getBlobHandles(g_opr) == 0);
     if (style == 0) {
       CHK(setBlobValue(tup) == 0);
@@ -1076,8 +1165,10 @@
     CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
     CHK(g_opr->deleteTuple() == 0);
     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+    }
     if (++n == g_opt.m_batch) {
       CHK(g_con->execute(Commit) == 0);
       g_ndb->closeTransaction(g_con);
@@ -1112,6 +1203,7 @@
     else
       CHK(g_opx->readTuple(NdbOperation::LM_CommittedRead) == 0);
     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+    CHK(g_opx->equal("PK3", (char*)&tup.m_pk3) == 0);
     CHK(getBlobHandles(g_opx) == 0);
     if (style == 0) {
       CHK(getBlobValue(tup) == 0);
@@ -1146,6 +1238,7 @@
     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
     CHK(g_opx->updateTuple() == 0);
     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+    CHK(g_opx->equal("PK3", (char*)&tup.m_pk3) == 0);
     CHK(getBlobHandles(g_opx) == 0);
     if (style == 0) {
       CHK(setBlobValue(tup) == 0);
@@ -1175,6 +1268,7 @@
     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
     CHK(g_opx->writeTuple() == 0);
     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+    CHK(g_opx->equal("PK3", (char*)&tup.m_pk3) == 0);
     CHK(getBlobHandles(g_opx) == 0);
     if (style == 0) {
       CHK(setBlobValue(tup) == 0);
@@ -1209,6 +1303,7 @@
     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
     CHK(g_opx->deleteTuple() == 0);
     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+    CHK(g_opx->equal("PK3", (char*)&tup.m_pk3) == 0);
     if (++n == g_opt.m_batch) {
       CHK(g_con->execute(Commit) == 0);
       g_ndb->closeTransaction(g_con);
@@ -1244,8 +1339,10 @@
   else
     CHK(g_ops->readTuples(NdbOperation::LM_CommittedRead) == 0);
   CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
-  if (g_opt.m_pk2len != 0)
+  if (g_opt.m_pk2len != 0) {
     CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+    CHK(g_ops->getValue("PK3", (char*)&tup.m_pk3) != 0);
+  }
   CHK(getBlobHandles(g_ops) == 0);
   if (style == 0) {
     CHK(getBlobValue(tup) == 0);
@@ -1298,8 +1395,10 @@
   }
   CHK(g_ops->readTuples(NdbOperation::LM_Exclusive) == 0);
   CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
-  if (g_opt.m_pk2len != 0)
+  if (g_opt.m_pk2len != 0) {
     CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+    CHK(g_ops->getValue("PK3", (char*)&tup.m_pk3) != 0);
+  }
   CHK(g_con->execute(NoCommit) == 0);
   unsigned rows = 0;
   while (1) {
@@ -1351,8 +1450,10 @@
   }
   CHK(g_ops->readTuples(NdbOperation::LM_Exclusive) == 0);
   CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
-  if (g_opt.m_pk2len != 0)
+  if (g_opt.m_pk2len != 0) {
     CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+    CHK(g_ops->getValue("PK3", (char*)&tup.m_pk3) != 0);
+  }
   CHK(g_con->execute(NoCommit) == 0);
   unsigned rows = 0;
   unsigned n = 0;
@@ -1411,14 +1512,15 @@
   CHK(g_ndb->init(20) == 0);
   CHK(g_ndb->waitUntilReady() == 0);
   g_dic = g_ndb->getDictionary();
+  initblobs();
   g_tups = new Tup [g_opt.m_rows];
   CHK(dropTable() == 0);
   CHK(createTable() == 0);
-  Bcol& b1 = g_opt.m_blob1;
+  Bcol& b1 = g_blob1;
   CHK(NdbBlob::getBlobTableName(b1.m_btname, g_ndb, g_opt.m_tname, "BL1") == 0);
   DBG("BL1: inline=" << b1.m_inline << " part=" << b1.m_partsize << " table=" << b1.m_btname);
   if (! g_opt.m_oneblob) {
-    Bcol& b2 = g_opt.m_blob2;
+    Bcol& b2 = g_blob2;
     CHK(NdbBlob::getBlobTableName(b2.m_btname, g_ndb, g_opt.m_tname, "BL2") == 0);
     DBG("BL2: inline=" << b2.m_inline << " part=" << b2.m_partsize << " table=" << b2.m_btname);
   }
@@ -1444,12 +1546,12 @@
         continue;
       DBG("--- pk ops " << stylename[style] << " ---");
       if (testcase('n')) {
-        calcTups(false);
+        calcTups(true, false);
         CHK(insertPk(style) == 0);
         CHK(verifyBlob() == 0);
         CHK(readPk(style) == 0);
         if (testcase('u')) {
-          calcTups(style);
+          calcTups(false, style);
           CHK(updatePk(style) == 0);
           CHK(verifyBlob() == 0);
           CHK(readPk(style) == 0);
@@ -1458,12 +1560,12 @@
         CHK(verifyBlob() == 0);
       }
       if (testcase('w')) {
-        calcTups(false);
+        calcTups(false, false);
         CHK(writePk(style) == 0);
         CHK(verifyBlob() == 0);
         CHK(readPk(style) == 0);
         if (testcase('u')) {
-          calcTups(style);
+          calcTups(false, style);
           CHK(writePk(style) == 0);
           CHK(verifyBlob() == 0);
           CHK(readPk(style) == 0);
@@ -1478,12 +1580,12 @@
         continue;
       DBG("--- idx ops " << stylename[style] << " ---");
       if (testcase('n')) {
-        calcTups(false);
+        calcTups(true, false);
         CHK(insertPk(style) == 0);
         CHK(verifyBlob() == 0);
         CHK(readIdx(style) == 0);
         if (testcase('u')) {
-          calcTups(style);
+          calcTups(false, style);
           CHK(updateIdx(style) == 0);
           CHK(verifyBlob() == 0);
           CHK(readIdx(style) == 0);
@@ -1492,12 +1594,12 @@
         CHK(verifyBlob() == 0);
       }
       if (testcase('w')) {
-        calcTups(false);
+        calcTups(false, false);
         CHK(writePk(style) == 0);
         CHK(verifyBlob() == 0);
         CHK(readIdx(style) == 0);
         if (testcase('u')) {
-          calcTups(style);
+          calcTups(false, style);
           CHK(writeIdx(style) == 0);
           CHK(verifyBlob() == 0);
           CHK(readIdx(style) == 0);
@@ -1511,7 +1613,7 @@
       if (! testcase('s') || ! testcase(style))
         continue;
       DBG("--- table scan " << stylename[style] << " ---");
-      calcTups(false);
+      calcTups(true, false);
       CHK(insertPk(style) == 0);
       CHK(verifyBlob() == 0);
       CHK(readScan(style, false) == 0);
@@ -1527,7 +1629,7 @@
       if (! testcase('r') || ! testcase(style))
         continue;
       DBG("--- index scan " << stylename[style] << " ---");
-      calcTups(false);
+      calcTups(true, false);
       CHK(insertPk(style) == 0);
       CHK(verifyBlob() == 0);
       CHK(readScan(style, true) == 0);
@@ -1843,7 +1945,7 @@
   unsigned i;
   DBG("bug test 4088 - ndb api hang with mixed ops on index table");
   // insert rows
-  calcTups(false);
+  calcTups(true, false);
   CHK(insertPk(false) == 0);
   // new trans
   CHK((g_con = g_ndb->startTransaction()) != 0);
@@ -1865,8 +1967,8 @@
     CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
     CHK(g_opx->readTuple() == 0);
     CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
-    assert(tup.m_blob1.m_buf != 0);
-    CHK(g_opx->getValue("BL1", (char*)tup.m_blob1.m_buf) != 0);
+    assert(tup.m_bval1.m_buf != 0);
+    CHK(g_opx->getValue("BL1", (char*)tup.m_bval1.m_buf) != 0);
     // execute
     // BUG 4088: gets 1 tckeyconf, 1 tcindxconf, then hangs
     CHK(g_con->execute(Commit) == 0);
@@ -1875,7 +1977,7 @@
       CHK(pktup[i].m_pk1 == tup.m_pk1);
       CHK(memcmp(pktup[i].m_pk2, tup.m_pk2, g_opt.m_pk2len) == 0);
     }
-    CHK(memcmp(tup.m_blob1.m_val, tup.m_blob1.m_buf, 8 + g_opt.m_blob1.m_inline) == 0);
+    CHK(memcmp(tup.m_bval1.m_val, tup.m_bval1.m_buf, 8 + g_blob1.m_inline) == 0);
   }
   return 0;
 }
@@ -1886,7 +1988,7 @@
   DBG("bug test 27018 - middle partial part write clobbers rest of part");
 
   // insert rows
-  calcTups(false);
+  calcTups(true, false);
   CHK(insertPk(false) == 0);
   // new trans
   for (unsigned k= 0; k < g_opt.m_rows; k++)
@@ -1894,25 +1996,27 @@
     Tup& tup= g_tups[k];
 
     /* Update one byte in random position. */
-    Uint32 offset= urandom(tup.m_blob1.m_len + 1);
-    if (offset == tup.m_blob1.m_len) {
+    Uint32 offset= urandom(tup.m_bval1.m_len + 1);
+    if (offset == tup.m_bval1.m_len) {
       // testing write at end is another problem..
       continue;
     }
-    //DBG("len=" << tup.m_blob1.m_len << " offset=" << offset);
+    //DBG("len=" << tup.m_bval1.m_len << " offset=" << offset);
 
     CHK((g_con= g_ndb->startTransaction()) != 0);
     CHK((g_opr= g_con->getNdbOperation(g_opt.m_tname)) != 0);
     CHK(g_opr->updateTuple() == 0);
     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+    }
     CHK(getBlobHandles(g_opr) == 0);
     CHK(g_con->execute(NoCommit) == 0);
 
-    tup.m_blob1.m_buf[0]= 0xff ^ tup.m_blob1.m_val[offset];
+    tup.m_bval1.m_buf[0]= 0xff ^ tup.m_bval1.m_val[offset];
     CHK(g_bh1->setPos(offset) == 0);
-    CHK(g_bh1->writeData(&(tup.m_blob1.m_buf[0]), 1) == 0);
+    CHK(g_bh1->writeData(&(tup.m_bval1.m_buf[0]), 1) == 0);
     CHK(g_con->execute(Commit) == 0);
     g_ndb->closeTransaction(g_con);
 
@@ -1920,17 +2024,19 @@
     CHK((g_opr= g_con->getNdbOperation(g_opt.m_tname)) != 0);
     CHK(g_opr->readTuple() == 0);
     CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
-    if (g_opt.m_pk2len != 0)
+    if (g_opt.m_pk2len != 0) {
       CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+      CHK(g_opr->equal("PK3", (char*)&tup.m_pk3) == 0);
+    }
     CHK(getBlobHandles(g_opr) == 0);
 
-    CHK(g_bh1->getValue(tup.m_blob1.m_buf, tup.m_blob1.m_len) == 0);
+    CHK(g_bh1->getValue(tup.m_bval1.m_buf, tup.m_bval1.m_len) == 0);
     CHK(g_con->execute(Commit) == 0);
 
     Uint64 len= ~0;
-    CHK(g_bh1->getLength(len) == 0 && len == tup.m_blob1.m_len);
-    tup.m_blob1.m_buf[offset]^= 0xff;
-    CHK(memcmp(tup.m_blob1.m_buf, tup.m_blob1.m_val, tup.m_blob1.m_len) == 0);
+    CHK(g_bh1->getLength(len) == 0 && len == tup.m_bval1.m_len);
+    tup.m_bval1.m_buf[offset]^= 0xff;
+    CHK(memcmp(tup.m_bval1.m_buf, tup.m_bval1.m_val, tup.m_bval1.m_len) == 0);
 
     g_ndb->closeTransaction(g_con);
   }
@@ -2012,8 +2118,6 @@
     }
     if (strcmp(arg, "-min") == 0) {
       g_opt.m_min = true;
-      new (&g_opt.m_blob1) Bcol(TEST_BLOBS_MIN_SIZE1);
-      new (&g_opt.m_blob2) Bcol(TEST_BLOBS_MIN_SIZE2);
       continue;
     }
     if (strcmp(arg, "-parts") == 0) {
@@ -2067,6 +2171,18 @@
           continue;
       }
     }
+    if (strcmp(arg, "-pk2fixed") == 0) {
+      g_opt.m_pk2fixed = true;
+      continue;
+    }
+    if (strcmp(arg, "-pk2binary") == 0) {
+      g_opt.m_pk2binary = true;
+      continue;
+    }
+    if (strcmp(arg, "-pk2part") == 0) {
+      g_opt.m_pk2part = true;
+      continue;
+    }
     if (strcmp(arg, "-oneblob") == 0) {
       g_opt.m_oneblob = true;
       continue;
@@ -2108,6 +2224,21 @@
     strcat(b, "i");
     strcat(b, "r");
     g_opt.m_skip = strdup(b);
+  }
+  if (g_opt.m_pk2len != 0) {
+    if (g_opt.m_pk2fixed) {
+      if (g_opt.m_pk2binary)
+        g_opt.m_pk2type = NdbDictionary::Column::Binary;
+      else
+        g_opt.m_pk2type = NdbDictionary::Column::Char;
+      g_opt.m_pk2totlen = g_opt.m_pk2len;
+    } else {
+      if (g_opt.m_pk2binary)
+        g_opt.m_pk2type = NdbDictionary::Column::Varbinary;
+      else
+        g_opt.m_pk2type = NdbDictionary::Column::Varchar;
+      g_opt.m_pk2totlen = 1 + g_opt.m_pk2len;
+    }
   }
   ndbout << cmdline << endl;
   g_ncc = new Ndb_cluster_connection();

--- 1.27/storage/ndb/include/ndbapi/NdbBlob.hpp	2007-03-28 18:37:05 +02:00
+++ 1.28/storage/ndb/include/ndbapi/NdbBlob.hpp	2007-03-31 18:25:51 +02:00
@@ -295,13 +295,13 @@
   int theEventBlobVersion; // -1=normal blob 0=post event 1=pre event
   // define blob table
   static void getBlobTableName(char* btname, const NdbTableImpl* t, const NdbColumnImpl* c);
-  static void getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnImpl* c);
+  static int getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnImpl* c, struct NdbError& error);
   static void getBlobEventName(char* bename, const NdbEventImpl* e, const NdbColumnImpl* c);
   static void getBlobEvent(NdbEventImpl& be, const NdbEventImpl* e, const NdbColumnImpl* c);
   // compute blob table column number for faster access
   enum {
     BtColumnPk = 0,    /* V1 only */
-    BtColumnDist = 1,
+    BtColumnDist = 1,  /* if stripe size != 0 */
     BtColumnPart = 2,
     BtColumnPkid = 3,  /* V2 only */
     BtColumnData = 4
@@ -403,6 +403,7 @@
   int getTableKeyValue(NdbOperation* anOp);
   int setTableKeyValue(NdbOperation* anOp);
   int setAccessKeyValue(NdbOperation* anOp);
+  int setDistKeyValue(NdbOperation* anOp, Uint32 part);
   int setPartKeyValue(NdbOperation* anOp, Uint32 part);
   int setPartPkidValue(NdbOperation* anOp, Uint32 pkid);
   int getPartDataValue(NdbOperation* anOp, char* buf, Uint16* aLenLoc);

--- 1.57/storage/ndb/src/ndbapi/NdbBlob.cpp	2007-03-28 18:37:06 +02:00
+++ 1.58/storage/ndb/src/ndbapi/NdbBlob.cpp	2007-03-31 18:25:51 +02:00
@@ -83,8 +83,8 @@
   DBUG_VOID_RETURN;
 }
 
-void
-NdbBlob::getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnImpl* c)
+int
+NdbBlob::getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnImpl* c, NdbError& error)
 {
   DBUG_ENTER("NdbBlob::getBlobTable");
   const int blobVersion = c->m_blobVersion;
@@ -131,6 +131,14 @@
                       blobVersion,
                       bt.m_primaryTableId, (uint)bt.getFragmentType()));
   if (unlikely(blobVersion == NDB_BLOB_V1)) {
+    /*
+     * Stripe size 0 in V1 does not work as intended.
+     * No point to add support for it now.
+     */
+    if (c->getStripeSize() == 0) {
+      error.code = NdbBlobImpl::ErrTable;
+      DBUG_RETURN(-1);
+    }
     { NdbDictionary::Column bc("PK");
       bc.setType(NdbDictionary::Column::Unsigned);
       assert(t->m_keyLenInWords != 0);
@@ -176,15 +184,22 @@
       uint i;
       for (i = 0; n < noOfKeys; i++) {
         assert(i < columns);
-        NdbColumnImpl* c = t->m_columns[i];
+        const NdbColumnImpl* c = t->getColumn(i);
         assert(c != NULL);
         if (c->m_pk) {
           bt.addColumn(*c);
+          if (c->getDistributionKey()) {
+            // addColumn might usefully return the column added..
+            NdbColumnImpl* bc = bt.getColumn(n);
+            assert(bc != NULL);
+            bc->setDistributionKey(true);
+          }
           n++;
         }
       }
     }
     // in V2 add NDB$ to avoid conflict with table PK
+    if (c->getStripeSize() != 0)
     { NdbDictionary::Column bc("NDB$DIST");
       bc.setType(NdbDictionary::Column::Unsigned);
       bc.setPrimaryKey(true);
@@ -194,12 +209,8 @@
     { NdbDictionary::Column bc("NDB$PART");
       bc.setType(NdbDictionary::Column::Unsigned);
       bc.setPrimaryKey(true);
-      /*
-       * Cannot allow partial distribution key now because
-       * primary table PK may contain unsupported types.
-       * Must be fixed.  All PK types must be supported.
-       */
-      bc.setDistributionKey(true);
+      // wl3717_todo all PK types must be allowed as DK
+      bc.setDistributionKey(false);
       bt.addColumn(bc);
     }
     // in V2 add id sequence for use in blob event code
@@ -228,7 +239,7 @@
       bt.addColumn(bc);
     }
   }
-  DBUG_VOID_RETURN;
+  DBUG_RETURN(0);
 }
 
 int
@@ -756,10 +767,23 @@
 }
 
 int
+NdbBlob::setDistKeyValue(NdbOperation* anOp, Uint32 part)
+{
+  DBUG_ENTER("NdbBlob::setDistKeyValue");
+  if (theStripeSize != 0) {
+    Uint32 dist = getDistKey(part);
+    DBUG_PRINT("info", ("dist=%u", dist));
+    if (anOp->equal(theBtColumnNo[BtColumnDist], dist) == -1)
+      DBUG_RETURN(-1);
+  }
+  DBUG_RETURN(0);
+}
+
+int
 NdbBlob::setPartKeyValue(NdbOperation* anOp, Uint32 part)
 {
   DBUG_ENTER("NdbBlob::setPartKeyValue");
-  DBUG_PRINT("info", ("dist=%u part=%u packkey=", getDistKey(part), part));
+  DBUG_PRINT("info", ("part=%u packkey=", part));
   DBUG_DUMP("info", thePackKeyBuf.data, 4 * thePackKeyBuf.size);
   // TODO use attr ids after compatibility with 4.1.7 not needed
   if (unlikely(theBlobVersion == NDB_BLOB_V1)) {
@@ -772,7 +796,7 @@
     }
   } else {
     if (setTableKeyValue(anOp) == -1 ||
-        anOp->equal(theBtColumnNo[BtColumnDist], getDistKey(part)) == -1 ||
+        setDistKeyValue(anOp, part) == -1 ||
         anOp->equal(theBtColumnNo[BtColumnPart], part) == -1) {
       setErrorCode(anOp);
       DBUG_RETURN(-1);
@@ -1820,6 +1844,7 @@
       buf = thePackKeyBuf.data;
       theBlobEventPkRecAttr = theBlobEventOp->getValue(bc, buf, version);
       //
+      assert(theStripeSize != 0);
       bc = theBlobTable->getColumn(theBtColumnNo[BtColumnDist]);
       buf = (char*)&theBlobEventDistValue;
       theBlobEventDistRecAttr = theBlobEventOp->getValue(bc, buf, version);
@@ -1861,9 +1886,11 @@
           n++;
         }
       }
-      bc = theBlobTable->getColumn(theBtColumnNo[BtColumnDist]);
-      buf = (char*)&theBlobEventDistValue;
-      theBlobEventDistRecAttr = theBlobEventOp->getValue(bc, buf, version);
+      if (theStripeSize != 0) {
+        bc = theBlobTable->getColumn(theBtColumnNo[BtColumnDist]);
+        buf = (char*)&theBlobEventDistValue;
+        theBlobEventDistRecAttr = theBlobEventOp->getValue(bc, buf, version);
+      }
       //
       bc = theBlobTable->getColumn(theBtColumnNo[BtColumnPart]);
       buf = (char*)&theBlobEventPartValue;
@@ -1877,7 +1904,7 @@
       buf = theBlobEventDataBuf.data;
       theBlobEventDataRecAttr = theBlobEventOp->getValue(bc, buf, version);
       if (unlikely(
-            theBlobEventDistRecAttr == NULL ||
+            (theStripeSize != 0 && theBlobEventDistRecAttr == NULL) ||
             theBlobEventPartRecAttr == NULL ||
             theBlobEventPkidRecAttr == NULL ||
             theBlobEventDataRecAttr == NULL
@@ -1896,7 +1923,12 @@
 {
   DBUG_ENTER("prepareColumn");
   NdbDictionary::Column::Type partType = NdbDictionary::Column::Undefined;
+  //
   theBlobVersion = theColumn->m_blobVersion;
+  theInlineSize = theColumn->getInlineSize();
+  thePartSize = theColumn->getPartSize();
+  theStripeSize = theColumn->getStripeSize();
+  //
   if (unlikely(theBlobVersion == NDB_BLOB_V1)) {
     theHeadSize = (NDB_BLOB_V1_HEAD_SIZE << 2);
     theVarsizeBytes = 0;
@@ -1913,6 +1945,8 @@
       setErrorCode(NdbBlobImpl::ErrUsage);
       DBUG_RETURN(-1);
     }
+    // in V1 stripe size is != 0 (except tinyblob)
+    assert(!(thePartSize != 0 && theStripeSize == 0));
     theBtColumnNo[BtColumnPk] = 0;
     theBtColumnNo[BtColumnDist] = 1;
     theBtColumnNo[BtColumnPart] = 2;
@@ -1931,26 +1965,24 @@
       setErrorCode(NdbBlobImpl::ErrUsage);
       DBUG_RETURN(-1);
     }
-    uint noOfKeys = theTable->m_noOfKeys;
-    theBtColumnNo[BtColumnDist] = noOfKeys + 0;
-    theBtColumnNo[BtColumnPart] = noOfKeys + 1;
-    theBtColumnNo[BtColumnPkid] = noOfKeys + 2;
-    theBtColumnNo[BtColumnData] = noOfKeys + 3;
+    uint off = theTable->m_noOfKeys;
+    if (theStripeSize != 0) {
+      theBtColumnNo[BtColumnDist] = off;
+      off += 1;
+    }
+    theBtColumnNo[BtColumnPart] = off + 0;
+    theBtColumnNo[BtColumnPkid] = off + 1;
+    theBtColumnNo[BtColumnData] = off + 2;
   } else {
       setErrorCode(NdbBlobImpl::ErrUsage);
       DBUG_RETURN(-1);
   }
-  // sizes
-  theInlineSize = theColumn->getInlineSize();
-  thePartSize = theColumn->getPartSize();
-  theStripeSize = theColumn->getStripeSize();
   // sanity check
   assert(theColumn->m_attrSize * theColumn->m_arraySize == theHeadSize + theInlineSize);
   if (thePartSize > 0) {
     const NdbTableImpl* bt = NULL;
     const NdbColumnImpl* bc = NULL;
-    if (theStripeSize == 0 ||
-        (bt = theColumn->m_blobTable) == NULL ||
+    if ((bt = theColumn->m_blobTable) == NULL ||
         (bc = bt->getColumn(theBtColumnNo[BtColumnData])) == NULL ||
         bc->getType() != partType ||
         bc->getLength() != (int)thePartSize) {

--- 1.16/storage/ndb/test/ndbapi/test_event_merge.cpp	2007-03-24 17:38:52 +01:00
+++ 1.17/storage/ndb/test/ndbapi/test_event_merge.cpp	2007-03-31 18:25:52 +02:00
@@ -79,6 +79,7 @@
   const char* opstring;
   uint seed;
   int maxtab;
+  my_bool pk2binary;
   my_bool separate_events;
   uint tweak; // whatever's useful
   my_bool use_table;
@@ -105,7 +106,6 @@
 
 static uint g_blobinlinesize = 256;
 static uint g_blobpartsize = 2000;
-static uint g_blobstripesize = 2;
 static const uint g_maxblobsize = 100000;
 
 static NdbEventOperation* g_evt_op = 0;
@@ -209,6 +209,9 @@
   bool nullable;
   uint length;
   uint size;
+  uint inlinesize;
+  uint partsize;
+  uint stripesize;
   bool isblob() const {
     return
       type == NdbDictionary::Column::Text ||
@@ -216,14 +219,29 @@
   }
 };
 
-static const Col g_col[] = {
-  { 0, "pk1", NdbDictionary::Column::Unsigned, true, false, 1, 4 },
-  { 1, "pk2", NdbDictionary::Column::Char, true, false,  g_charlen, g_charlen },
-  { 2, "seq", NdbDictionary::Column::Unsigned,  false, false, 1, 4 },
-  { 3, "cc1", NdbDictionary::Column::Char, false, true, g_charlen, g_charlen },
-  { 4, "tx1", NdbDictionary::Column::Text, false, true, 0, 0 },
-  { 5, "tx2", NdbDictionary::Column::Text, false, true, 0, 0 },
-  { 6, "bl1", NdbDictionary::Column::Blob, false, true, 0, 0 } // tinyblob
+// not const, options may modify
+static Col g_col[] = {
+  { 0, "pk1", NdbDictionary::Column::Unsigned,
+       true, false,
+       1, 4, 0, 0, 0  },
+  { 1, "pk2", NdbDictionary::Column::Char,
+       true, false,
+       g_charlen, g_charlen, 0, 0, 0  },
+  { 2, "seq", NdbDictionary::Column::Unsigned,
+       false, true,
+       1, 4, 0, 0, 0  },
+  { 3, "cc1", NdbDictionary::Column::Char,
+       false, true,
+       g_charlen, g_charlen, 0, 0, 0  },
+  { 4, "tx1", NdbDictionary::Column::Text,
+       false, true,
+       0, 0, g_blobinlinesize, g_blobpartsize, 0 }, // V2 distribution
+  { 5, "tx2", NdbDictionary::Column::Text,
+       false, true,
+       0, 0, g_blobinlinesize, g_blobpartsize, 4 },
+  { 6, "bl1", NdbDictionary::Column::Blob, // tinyblob
+       false, true,
+       0, 0, g_blobinlinesize, 0, 0 }
 };
 
 static const uint g_maxcol = sizeof(g_col)/sizeof(g_col[0]);
@@ -240,7 +258,7 @@
   return n;
 }
 
-static const Col&
+static Col&
 getcol(uint i)
 {
   if (i < ncol())
@@ -249,7 +267,7 @@
   return g_col[0];
 }
 
-static const Col&
+static Col&
 getcol(const char* name)
 {
   uint i;
@@ -305,9 +323,7 @@
     NdbDictionary::Column col(c.name);
     col.setType(c.type);
     col.setPrimaryKey(c.pk);
-    if (! c.pk)
-      col.setNullable(true);
-    col.setLength(c.length);
+    col.setNullable(c.nullable);
     switch (c.type) {
     case NdbDictionary::Column::Unsigned:
       break;
@@ -315,18 +331,21 @@
       col.setLength(c.length);
       col.setCharset(cs);
       break;
+    case NdbDictionary::Column::Binary:
+      col.setLength(c.length);
+      break;
     case NdbDictionary::Column::Text:
       col.setBlobVersion(g_opts.blob_version);
-      col.setInlineSize(g_blobinlinesize);
-      col.setPartSize(g_blobpartsize);
-      col.setStripeSize(g_blobstripesize);
+      col.setInlineSize(c.inlinesize);
+      col.setPartSize(c.partsize);
+      col.setStripeSize(c.stripesize);
       col.setCharset(cs);
       break;
     case NdbDictionary::Column::Blob:
       col.setBlobVersion(g_opts.blob_version);
-      col.setInlineSize(g_blobinlinesize);
-      col.setPartSize(0);
-      col.setStripeSize(0);
+      col.setInlineSize(c.inlinesize);
+      col.setPartSize(c.partsize);
+      col.setStripeSize(c.stripesize);
       break;
     default:
       assert(false);
@@ -509,6 +528,7 @@
         return 1;
       break;
     case NdbDictionary::Column::Char:
+    case NdbDictionary::Column::Binary:
       if (memcmp(d1.ptr[i].ch, d2.ptr[i].ch, c.size) != 0)
         return 1;
       break;
@@ -550,6 +570,7 @@
       out << *d.ptr[i].u32;
       break;
     case NdbDictionary::Column::Char:
+    case NdbDictionary::Column::Binary:
       {
         char buf[g_charlen + 1];
         memcpy(buf, d.ptr[i].ch, g_charlen);
@@ -1314,6 +1335,7 @@
       }
       break;
     case NdbDictionary::Column::Char:
+    case NdbDictionary::Column::Binary:
       {
         char* p = d.ptr[i].ch;
         sprintf(p, "%-*u", g_charlen, pk1);
@@ -1347,6 +1369,7 @@
       }
       break;
     case NdbDictionary::Column::Char:
+    case NdbDictionary::Column::Binary:
       {
         char* p = d.ptr[i].ch;
         uint u = urandom(g_charlen);
@@ -2242,6 +2265,9 @@
   { "blob-version", 1018, "Blob version 1 or 2 (default 2)",
     (gptr*)&g_opts.blob_version, (gptr*)&g_opts.blob_version, 0,
     GET_INT, REQUIRED_ARG, 2, 0, 0, 0, 0, 0 },
+  { "pk2binary", 1019, "Make pk2 binary",
+    (gptr*)&g_opts.pk2binary, (gptr*)&g_opts.pk2binary, 0,
+    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
   { 0, 0, 0,
     0, 0, 0,
     GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
@@ -2256,9 +2282,6 @@
 static int
 checkopts()
 {
-  if (g_opts.blob_version < 1 || g_opts.blob_version > 2) {
-    return -1;
-  }
   if (g_opts.separate_events) {
     g_opts.no_blobs = true;
   }
@@ -2298,6 +2321,17 @@
   if (g_opts.maxpk > g_maxpk ||
       g_opts.maxtab > g_maxtab) {
     return -1;
+  }
+  if (g_opts.blob_version < 1 || g_opts.blob_version > 2) {
+    return -1;
+  }
+  if (g_opts.blob_version == 1 && ! g_opts.no_blobs) {
+    Col& c = getcol("tx1");
+    c.stripesize = 4;
+  }
+  if (g_opts.pk2binary) {
+    Col& c = getcol("pk2");
+    c.type = NdbDictionary::Column::Binary;
   }
   return 0;
 }
Thread
bk commit into 5.1 tree (pekka:1.2504)pekka31 Mar