~tsarev/percona-server/5.5-processlist_rows_stats-sporadic_fails-fix

112 by Laurynas Biveinis
Port the dynamic VARCHAR/HEAP support in MEMORY storage engine from
1
diff -ruN a/include/heap.h b/include/heap.h
2
--- a/include/heap.h	2011-04-11 13:44:02.000000000 +0300
3
+++ b/include/heap.h	2011-06-02 15:28:13.294917001 +0300
4
@@ -33,7 +33,17 @@
5
 #include "my_compare.h"
6
 #include "my_tree.h"
7
 
8
-	/* defines used by heap-funktions */
9
+/* Define index limits to be identical to MyISAM ones for compatibility. */
10
+
11
+#if MAX_INDEXES > HA_MAX_POSSIBLE_KEY
12
+#define HP_MAX_KEY                  HA_MAX_POSSIBLE_KEY /* Max allowed keys */
13
+#else
14
+#define HP_MAX_KEY                  MAX_INDEXES         /* Max allowed keys */
15
+#endif
16
+
17
+#define HP_MAX_KEY_LENGTH           1000            /* Max length in bytes */
18
+
19
+/* defines used by heap-funktions */
20
 
21
 #define HP_MAX_LEVELS	4		/* 128^5 records is enough */
22
 #define HP_PTRS_IN_NOD	128
23
@@ -129,22 +139,58 @@
24
   uint (*get_key_length)(struct st_hp_keydef *keydef, const uchar *key);
25
 } HP_KEYDEF;
26
 
27
-typedef struct st_heap_share
28
+typedef struct st_heap_columndef		/* column information */
29
+{
30
+  int16  type;	  			/* en_fieldtype */
31
+  uint32 length;		  	/* length of field */
32
+  uint32 offset;		  	/* Offset to position in row */
33
+  uint8  null_bit;			/* If column may be 0 */
34
+  uint16 null_pos;			/* position for null marker */
35
+  uint8  length_bytes;			/* length of the size, 1 o 2 bytes */
36
+} HP_COLUMNDEF;
37
+
38
+typedef struct st_heap_dataspace   /* control data for data space */
39
 {
40
   HP_BLOCK block;
41
+  /* Total chunks ever allocated in this dataspace */
42
+  uint chunk_count;
43
+  uint del_chunk_count;         /* Deleted chunks count */
44
+  uchar *del_link;              /* Link to last deleted chunk */
45
+  uint chunk_length;            /* Total length of one chunk */
46
+  /* Length of payload that will be placed into one chunk */
47
+  uint chunk_dataspace_length;
48
+  /* Offset of the status flag relative to the chunk start */
49
+  uint offset_status;
50
+  /* Offset of the linking pointer relative to the chunk start */
51
+  uint offset_link;
52
+  /* Test whether records have variable size and so "next" pointer */
53
+  uint is_variable_size;
54
+  /* Total size allocated within this data space */
55
+  ulonglong total_data_length;
56
+} HP_DATASPACE;
57
+
58
+typedef struct st_heap_share
59
+{
60
   HP_KEYDEF  *keydef;
61
+  HP_COLUMNDEF *column_defs;
62
+  /* Describes "block", which contains actual records */
63
+  HP_DATASPACE recordspace;
64
   ulong min_records,max_records;	/* Params to open */
65
-  ulonglong data_length,index_length,max_table_size;
66
+  ulonglong index_length, max_table_size;
67
   uint key_stat_version;                /* version to indicate insert/delete */
68
-  uint records;				/* records */
69
-  uint blength;				/* records rounded up to 2^n */
70
-  uint deleted;				/* Deleted records in database */
71
-  uint reclength;			/* Length of one record */
72
+  uint records;			/* Actual record (row) count */
73
+  uint blength;			/* used_chunk_count rounded up to 2^n */
74
+  /*
75
+    Length of record's fixed part, which contains keys and always fits into the
76
+    first chunk.
77
+  */
78
+  uint fixed_data_length;
79
+  uint fixed_column_count;  /* Number of columns stored in fixed_data_length */
80
   uint changed;
81
   uint keys,max_key_length;
82
+  uint column_count;
83
   uint currently_disabled_keys;    /* saved value from "keys" when disabled */
84
   uint open_count;
85
-  uchar *del_link;			/* Link to next block with del. rec */
86
   char * name;			/* Name of "memory-file" */
87
   THR_LOCK lock;
88
   mysql_mutex_t intern_lock;            /* Locking for use with _locking */
89
@@ -153,6 +199,7 @@
90
   uint auto_key;
91
   uint auto_key_type;			/* real type of the auto key segment */
92
   ulonglong auto_increment;
93
+  uint blobs;  /* Number of blobs in table */
94
 } HP_SHARE;
95
 
96
 struct st_hp_hash_info;
97
@@ -162,7 +209,7 @@
98
   HP_SHARE *s;
99
   uchar *current_ptr;
100
   struct st_hp_hash_info *current_hash_ptr;
101
-  ulong current_record,next_block;
102
+  ulong current_record;
103
   int lastinx,errkey;
104
   int  mode;				/* Mode of file (READONLY..) */
105
   uint opt_flag,update;
106
@@ -175,6 +222,9 @@
107
   my_bool implicit_emptied;
108
   THR_LOCK_DATA lock;
109
   LIST open_list;
110
+  uchar *blob_buffer;  /* Temporary buffer used to return BLOB values */
111
+  uint blob_size;      /* Current blob_buffer size */
112
+  uint blob_offset;    /* Current offset in blob_buffer */
113
 } HP_INFO;
114
 
115
 
116
@@ -196,6 +246,14 @@
117
     open_count to 1. Is only looked at if not internal_table.
118
   */
119
   my_bool pin_share;
120
+  uint columns;
121
+  HP_COLUMNDEF *columndef;
122
+  uint fixed_key_fieldnr;
123
+  uint fixed_data_size;
124
+  uint keys_memory_size;
125
+  uint max_chunk_size;
126
+  uint is_dynamic;
127
+  uint blobs;
128
 } HP_CREATE_INFO;
129
 
130
 	/* Prototypes for heap-functions */
131
@@ -212,9 +270,8 @@
132
 extern int heap_scan(register HP_INFO *info, uchar *record);
133
 extern int heap_delete(HP_INFO *info,const uchar *buff);
134
 extern int heap_info(HP_INFO *info,HEAPINFO *x,int flag);
135
-extern int heap_create(const char *name,
136
-                       HP_CREATE_INFO *create_info, HP_SHARE **share,
137
-                       my_bool *created_new_share);
138
+extern int heap_create(const char *name, HP_CREATE_INFO *create_info,
139
+                       HP_SHARE **res, my_bool *created_new_share);
140
 extern int heap_delete_table(const char *name);
141
 extern void heap_drop_table(HP_INFO *info);
142
 extern int heap_extra(HP_INFO *info,enum ha_extra_function function);
143
diff -ruN a/mysql-test/r/create.result b/mysql-test/r/create.result
144
--- a/mysql-test/r/create.result	2011-06-02 11:38:04.000000000 +0300
145
+++ b/mysql-test/r/create.result	2011-06-02 15:56:29.224917005 +0300
146
@@ -33,10 +33,7 @@
147
 create table t1 (b char(0) not null, index(b));
148
 ERROR 42000: The used storage engine can't index column 'b'
149
 create table t1 (a int not null,b text) engine=heap;
150
-ERROR 42000: The used table type doesn't support BLOB/TEXT columns
151
 drop table if exists t1;
152
-Warnings:
153
-Note	1051	Unknown table 't1'
154
 create table t1 (ordid int(8) not null auto_increment, ord  varchar(50) not null, primary key (ord,ordid)) engine=heap;
155
 ERROR 42000: Incorrect table definition; there can be only one auto column and it must be defined as a key
156
 create table not_existing_database.test (a int);
157
diff -ruN a/mysql-test/r/ctype_utf8mb4_heap.result b/mysql-test/r/ctype_utf8mb4_heap.result
158
--- a/mysql-test/r/ctype_utf8mb4_heap.result	2011-04-11 13:44:02.000000000 +0300
159
+++ b/mysql-test/r/ctype_utf8mb4_heap.result	2011-06-02 15:31:07.794917003 +0300
160
@@ -1124,6 +1124,8 @@
161
 a varchar(255) NOT NULL default '',
162
 KEY a (a)
163
 ) ENGINE=heap DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
164
+Warnings:
165
+Warning	1071	Specified key was too long; max key length is 1000 bytes
166
 insert into t1 values (_utf8mb4 0xe880bd);
167
 insert into t1 values (_utf8mb4 0x5b);
168
 select hex(a) from t1;
169
@@ -1162,6 +1164,8 @@
170
 Warnings:
171
 Note	1051	Unknown table 't1'
172
 CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=heap DEFAULT CHARSET=utf8mb4;
173
+Warnings:
174
+Warning	1071	Specified key was too long; max key length is 1000 bytes
175
 INSERT INTO t1 VALUES('uuABCDEFGHIGKLMNOPRSTUVWXYZ̀ˆbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
176
 INSERT INTO t1 VALUES('uu');
177
 check table t1;
178
diff -ruN a/mysql-test/t/create.test b/mysql-test/t/create.test
179
--- a/mysql-test/t/create.test	2011-04-11 13:44:01.000000000 +0300
180
+++ b/mysql-test/t/create.test	2011-06-02 15:32:49.614917004 +0300
181
@@ -33,7 +33,7 @@
182
 drop table if exists t1,t2;
183
 --error 1167
184
 create table t1 (b char(0) not null, index(b));
185
---error 1163
186
+# BLOB/TEXT fields are now supported by HEAP
187
 create table t1 (a int not null,b text) engine=heap;
188
 drop table if exists t1;
189
 
190
diff -ruN a/storage/heap/CMakeLists.txt b/storage/heap/CMakeLists.txt
191
--- a/storage/heap/CMakeLists.txt	2011-04-11 13:44:03.000000000 +0300
192
+++ b/storage/heap/CMakeLists.txt	2011-06-02 15:37:43.744917005 +0300
193
@@ -20,6 +20,7 @@
194
 				ha_heap.cc
195
 				hp_delete.c hp_extra.c hp_hash.c hp_info.c hp_open.c hp_panic.c
196
 				hp_rename.c hp_rfirst.c hp_rkey.c hp_rlast.c hp_rnext.c hp_rprev.c
197
+				hp_dspace.c hp_record.c
198
 				hp_rrnd.c hp_rsame.c hp_scan.c hp_static.c hp_update.c hp_write.c)
199
 
200
 MYSQL_ADD_PLUGIN(heap ${HEAP_SOURCES} STORAGE_ENGINE MANDATORY RECOMPILE_FOR_EMBEDDED)
201
diff -ruN a/storage/heap/_check.c b/storage/heap/_check.c
202
--- a/storage/heap/_check.c	2011-04-11 13:44:03.000000000 +0300
203
+++ b/storage/heap/_check.c	2011-06-02 15:37:43.744917005 +0300
204
@@ -43,7 +43,7 @@
205
 {
206
   int error;
207
   uint key;
208
-  ulong records=0, deleted=0, pos, next_block;
209
+  ulong records= 0, deleted= 0, chunk_count= 0, pos, next_block;
210
   HP_SHARE *share=info->s;
211
   HP_INFO save_info= *info;			/* Needed because scan_init */
212
   DBUG_ENTER("heap_check_heap");
213
@@ -64,31 +64,55 @@
214
   {
215
     if (pos < next_block)
216
     {
217
-      info->current_ptr+= share->block.recbuffer;
218
+      info->current_ptr+= share->recordspace.block.recbuffer;
219
     }
220
     else
221
     {
222
-      next_block+= share->block.records_in_block;
223
-      if (next_block >= share->records+share->deleted)
224
+      next_block+= share->recordspace.block.records_in_block;
225
+      if (next_block >= share->recordspace.chunk_count)
226
       {
227
-	next_block= share->records+share->deleted;
228
-	if (pos >= next_block)
229
-	  break;				/* End of file */
230
+        next_block= share->recordspace.chunk_count;
231
+        if (pos >= next_block)
232
+          break;				/* End of file */
233
       }
234
     }
235
     hp_find_record(info,pos);
236
 
237
-    if (!info->current_ptr[share->reclength])
238
+    switch (get_chunk_status(&share->recordspace, info->current_ptr)) {
239
+    case CHUNK_STATUS_DELETED:
240
       deleted++;
241
-    else
242
+      chunk_count++;
243
+      break;
244
+    case CHUNK_STATUS_ACTIVE:
245
       records++;
246
+        chunk_count++;
247
+        break;
248
+    case CHUNK_STATUS_LINKED:
249
+      chunk_count++;
250
+      break;
251
+    default:
252
+      DBUG_PRINT("error",
253
+                 ("Unknown record status: Record: 0x%lx  Status %lu",
254
+                  (long) info->current_ptr,
255
+                  (ulong) get_chunk_status(&share->recordspace,
256
+                                           info->current_ptr)));
257
+        error|= 1;
258
+        break;
259
+    }
260
   }
261
 
262
-  if (records != share->records || deleted != share->deleted)
263
-  {
264
-    DBUG_PRINT("error",("Found rows: %lu (%lu)  deleted %lu (%lu)",
265
-			records, (ulong) share->records,
266
-                        deleted, (ulong) share->deleted));
267
+  /* TODO: verify linked chunks (no orphans, no cycles, no bad links) */
268
+
269
+  if (records != share->records ||
270
+      chunk_count != share->recordspace.chunk_count ||
271
+      deleted != share->recordspace.del_chunk_count)
272
+  {
273
+    DBUG_PRINT("error",
274
+               ("Found rows: %lu (%lu) total chunks %lu (%lu) deleted chunks "
275
+                "%lu (%lu)",
276
+                records, (ulong) share->records,
277
+                chunk_count, (ulong) share->recordspace.chunk_count,
278
+                deleted, (ulong) share->recordspace.del_chunk_count));
279
     error= 1;
280
   }
281
   *info= save_info;
282
@@ -177,7 +201,7 @@
283
     do
284
     {
285
       memcpy(&recpos, key + (*keydef->get_key_length)(keydef,key), sizeof(uchar*));
286
-      key_length= hp_rb_make_key(keydef, info->recbuf, recpos, 0);
287
+      key_length= hp_rb_make_key(keydef, info->recbuf, recpos, 0, TRUE);
288
       if (ha_key_cmp(keydef->seg, (uchar*) info->recbuf, (uchar*) key,
289
 		     key_length, SEARCH_FIND | SEARCH_SAME, not_used))
290
       {
291
diff -ruN a/storage/heap/_rectest.c b/storage/heap/_rectest.c
292
--- a/storage/heap/_rectest.c	2011-04-11 13:44:03.000000000 +0300
293
+++ b/storage/heap/_rectest.c	2011-06-02 15:38:02.774917002 +0300
294
@@ -22,7 +22,9 @@
295
 {
296
   DBUG_ENTER("hp_rectest");
297
 
298
-  if (memcmp(info->current_ptr,old,(size_t) info->s->reclength))
299
+  if (hp_process_record_data_to_chunkset(info->s, old,
300
+                                         info->current_ptr,
301
+                                         1))
302
   {
303
     DBUG_RETURN((my_errno=HA_ERR_RECORD_CHANGED)); /* Record have changed */
304
   }
305
diff -ruN a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc
306
--- a/storage/heap/ha_heap.cc	2011-04-11 13:44:03.000000000 +0300
307
+++ b/storage/heap/ha_heap.cc	2011-06-02 15:37:43.744917005 +0300
308
@@ -113,6 +113,7 @@
309
 
310
     rc= heap_create(name, &create_info, &internal_share, &created_new_share);
311
     my_free(create_info.keydef);
312
+    my_free(create_info.columndef);
313
     if (rc)
314
       goto end;
315
 
316
@@ -194,6 +195,12 @@
317
   {
318
     if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE)
319
       btree_keys.set_bit(i);
320
+    /*
321
+      Reset per-key block size specification so they are not shown
322
+      in SHOW CREATE TABLE.
323
+    */
324
+    table->key_info[i].block_size= 0;
325
+    table->key_info[i].flags&= ~HA_USES_BLOCK_SIZE;
326
   }
327
 }
328
 
329
@@ -427,6 +434,13 @@
330
   return 0;
331
 }
332
 
333
+enum row_type ha_heap::get_row_type() const
334
+{
335
+  if (file->s->recordspace.is_variable_size)
336
+    return ROW_TYPE_DYNAMIC;
337
+
338
+  return ROW_TYPE_FIXED;
339
+}
340
 
341
 int ha_heap::extra(enum ha_extra_function operation)
342
 {
343
@@ -644,23 +658,70 @@
344
 heap_prepare_hp_create_info(TABLE *table_arg, bool internal_table,
345
                             HP_CREATE_INFO *hp_create_info)
346
 {
347
-  uint key, parts, mem_per_row= 0, keys= table_arg->s->keys;
348
+  uint key, parts, mem_per_row_keys= 0, keys= table_arg->s->keys;
349
   uint auto_key= 0, auto_key_type= 0;
350
-  ha_rows max_rows;
351
+  uint fixed_key_fieldnr = 0, fixed_data_size = 0, next_field_pos = 0;
352
+  uint column_idx, column_count= table_arg->s->fields;
353
+  HP_COLUMNDEF *columndef;
354
   HP_KEYDEF *keydef;
355
   HA_KEYSEG *seg;
356
   TABLE_SHARE *share= table_arg->s;
357
   bool found_real_auto_increment= 0;
358
+  uint blobs= 0;
359
 
360
   bzero(hp_create_info, sizeof(*hp_create_info));
361
 
362
+  if (!(columndef= (HP_COLUMNDEF*) my_malloc(column_count *
363
+                                             sizeof(HP_COLUMNDEF),
364
+                                             MYF(MY_WME))))
365
+    return my_errno;
366
+
367
+  for (column_idx= 0; column_idx < column_count; column_idx++)
368
+  {
369
+    Field* field= *(table_arg->field + column_idx);
370
+    HP_COLUMNDEF* column= columndef + column_idx;
371
+    column->type= (uint16) field->type();
372
+    column->length= field->pack_length();
373
+    column->offset= field->offset(table_arg->record[0]);
374
+
375
+    if (field->null_bit)
376
+    {
377
+      column->null_bit= field->null_bit;
378
+      column->null_pos= (uint) (field->null_ptr -
379
+                                (uchar*) table_arg->record[0]);
380
+    }
381
+    else
382
+    {
383
+      column->null_bit= 0;
384
+      column->null_pos= 0;
385
+    }
386
+
387
+    if (field->type() == MYSQL_TYPE_VARCHAR)
388
+    {
389
+      column->length_bytes= (uint8) (((Field_varstring *) field)->length_bytes);
390
+    }
391
+    else if (field->type() == MYSQL_TYPE_BLOB)
392
+    {
393
+      blobs++;
394
+      column->length_bytes= (uint8)
395
+        (((Field_blob *) field)->pack_length_no_ptr());
396
+    }
397
+    else
398
+    {
399
+      column->length_bytes= 0;
400
+    }
401
+  }
402
+
403
   for (key= parts= 0; key < keys; key++)
404
     parts+= table_arg->key_info[key].key_parts;
405
 
406
   if (!(keydef= (HP_KEYDEF*) my_malloc(keys * sizeof(HP_KEYDEF) +
407
 				       parts * sizeof(HA_KEYSEG),
408
 				       MYF(MY_WME))))
409
+  {
410
+    my_free((uchar *) columndef);
411
     return my_errno;
412
+  }
413
   seg= reinterpret_cast<HA_KEYSEG*>(keydef + keys);
414
   for (key= 0; key < keys; key++)
415
   {
416
@@ -676,11 +737,11 @@
417
     case HA_KEY_ALG_UNDEF:
418
     case HA_KEY_ALG_HASH:
419
       keydef[key].algorithm= HA_KEY_ALG_HASH;
420
-      mem_per_row+= sizeof(char*) * 2; // = sizeof(HASH_INFO)
421
+      mem_per_row_keys+= sizeof(char*) * 2; // = sizeof(HASH_INFO)
422
       break;
423
     case HA_KEY_ALG_BTREE:
424
       keydef[key].algorithm= HA_KEY_ALG_BTREE;
425
-      mem_per_row+=sizeof(TREE_ELEMENT)+pos->key_length+sizeof(char*);
426
+      mem_per_row_keys+=sizeof(TREE_ELEMENT)+pos->key_length+sizeof(char*);
427
       break;
428
     default:
429
       DBUG_ASSERT(0); // cannot happen
430
@@ -705,6 +766,16 @@
431
       seg->length=  (uint) key_part->length;
432
       seg->flag=    key_part->key_part_flag;
433
 
434
+      next_field_pos= seg->start;
435
+      if (field->type() == MYSQL_TYPE_VARCHAR)
436
+      {
437
+        Field *orig_field= *(table_arg->field + key_part->field->field_index);
438
+        next_field_pos+= orig_field->pack_length();
439
+      }
440
+      else
441
+      {
442
+        next_field_pos+= seg->length;
443
+      }
444
       if (field->flags & (ENUM_FLAG | SET_FLAG))
445
         seg->charset= &my_charset_bin;
446
       else
447
@@ -730,9 +801,75 @@
448
         auto_key= key+ 1;
449
 	auto_key_type= field->key_type();
450
       }
451
+
452
+      switch (seg->type) {
453
+      case HA_KEYTYPE_SHORT_INT:
454
+      case HA_KEYTYPE_LONG_INT:
455
+      case HA_KEYTYPE_FLOAT:
456
+      case HA_KEYTYPE_DOUBLE:
457
+      case HA_KEYTYPE_USHORT_INT:
458
+      case HA_KEYTYPE_ULONG_INT:
459
+      case HA_KEYTYPE_LONGLONG:
460
+      case HA_KEYTYPE_ULONGLONG:
461
+      case HA_KEYTYPE_INT24:
462
+      case HA_KEYTYPE_UINT24:
463
+      case HA_KEYTYPE_INT8:
464
+        seg->flag|= HA_SWAP_KEY;
465
+        break;
466
+      case HA_KEYTYPE_VARBINARY1:
467
+        /* Case-insensitiveness is handled in coll->hash_sort */
468
+        seg->type= HA_KEYTYPE_VARTEXT1;
469
+        /* fall through */
470
+      case HA_KEYTYPE_VARTEXT1:
471
+        keydef[key].flag|= HA_VAR_LENGTH_KEY;
472
+        /* Save number of bytes used to store length */
473
+        if (seg->flag & HA_BLOB_PART)
474
+          seg->bit_start= field->pack_length() - share->blob_ptr_size;
475
+        else
476
+          seg->bit_start= 1;
477
+        break;
478
+      case HA_KEYTYPE_VARBINARY2:
479
+        /* Case-insensitiveness is handled in coll->hash_sort */
480
+        /* fall_through */
481
+      case HA_KEYTYPE_VARTEXT2:
482
+        keydef[key].flag|= HA_VAR_LENGTH_KEY;
483
+        /* Save number of bytes used to store length */
484
+        if (seg->flag & HA_BLOB_PART)
485
+          seg->bit_start= field->pack_length() - share->blob_ptr_size;
486
+        else
487
+          seg->bit_start= 2;
488
+        /*
489
+          Make future comparison simpler by only having to check for
490
+          one type
491
+        */
492
+        seg->type= HA_KEYTYPE_VARTEXT1;
493
+        break;
494
+      default:
495
+        break;
496
+      }
497
+
498
+      if (next_field_pos > fixed_data_size)
499
+      {
500
+        fixed_data_size= next_field_pos;
501
+      }
502
+
503
+
504
+      if (field->field_index >= fixed_key_fieldnr)
505
+      {
506
+        /*
507
+          Do not use seg->fieldnr as it's not reliable in case of temp tables
508
+        */
509
+        fixed_key_fieldnr= field->field_index + 1;
510
+      }
511
     }
512
   }
513
-  mem_per_row+= MY_ALIGN(share->reclength + 1, sizeof(char*));
514
+
515
+  if (fixed_data_size < share->null_bytes)
516
+  {
517
+    /* Make sure to include null fields regardless of the presense of keys */
518
+    fixed_data_size = share->null_bytes;
519
+  }
520
+
521
   if (table_arg->found_next_number_field)
522
   {
523
     keydef[share->next_number_index].flag|= HA_AUTO_KEY;
524
@@ -743,16 +880,19 @@
525
   hp_create_info->max_table_size=current_thd->variables.max_heap_table_size;
526
   hp_create_info->with_auto_increment= found_real_auto_increment;
527
   hp_create_info->internal_table= internal_table;
528
-
529
-  max_rows= (ha_rows) (hp_create_info->max_table_size / mem_per_row);
530
-  if (share->max_rows && share->max_rows < max_rows)
531
-    max_rows= share->max_rows;
532
-
533
-  hp_create_info->max_records= (ulong) max_rows;
534
+  hp_create_info->max_chunk_size= share->key_block_size;
535
+  hp_create_info->is_dynamic= (share->row_type == ROW_TYPE_DYNAMIC);
536
+  hp_create_info->columns= column_count;
537
+  hp_create_info->columndef= columndef;
538
+  hp_create_info->fixed_key_fieldnr= fixed_key_fieldnr;
539
+  hp_create_info->fixed_data_size= fixed_data_size;
540
+  hp_create_info->max_records= (ulong) share->max_rows;
541
   hp_create_info->min_records= (ulong) share->min_rows;
542
   hp_create_info->keys= share->keys;
543
   hp_create_info->reclength= share->reclength;
544
+  hp_create_info->keys_memory_size= mem_per_row_keys;
545
   hp_create_info->keydef= keydef;
546
+  hp_create_info->blobs= blobs;
547
   return 0;
548
 }
549
 
550
@@ -772,6 +912,7 @@
551
 				  create_info->auto_increment_value - 1 : 0);
552
   error= heap_create(name, &hp_create_info, &internal_share, &created);
553
   my_free(hp_create_info.keydef);
554
+  my_free(hp_create_info.columndef);
555
   DBUG_ASSERT(file == 0);
556
   return (error);
557
 }
558
@@ -782,6 +923,13 @@
559
   table->file->info(HA_STATUS_AUTO);
560
   if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
561
     create_info->auto_increment_value= stats.auto_increment_value;
562
+  if (!(create_info->used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
563
+  {
564
+    if (file->s->recordspace.is_variable_size)
565
+      create_info->key_block_size= file->s->recordspace.chunk_length;
566
+    else
567
+      create_info->key_block_size= 0;
568
+  }
569
 }
570
 
571
 void ha_heap::get_auto_increment(ulonglong offset, ulonglong increment,
572
diff -ruN a/storage/heap/ha_heap.h b/storage/heap/ha_heap.h
573
--- a/storage/heap/ha_heap.h	2011-04-11 13:44:03.000000000 +0300
574
+++ b/storage/heap/ha_heap.h	2011-06-02 15:37:43.744917005 +0300
575
@@ -46,12 +46,11 @@
576
     return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ?
577
             "BTREE" : "HASH");
578
   }
579
-  /* Rows also use a fixed-size format */
580
-  enum row_type get_row_type() const { return ROW_TYPE_FIXED; }
581
+  enum row_type get_row_type() const;
582
   const char **bas_ext() const;
583
   ulonglong table_flags() const
584
   {
585
-    return (HA_FAST_KEY_READ | HA_NO_BLOBS | HA_NULL_IN_KEY |
586
+    return (HA_FAST_KEY_READ | HA_NULL_IN_KEY |
587
             HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
588
             HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED | HA_NO_TRANSACTIONS |
589
             HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
590
@@ -63,8 +62,9 @@
591
             HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
592
   }
593
   const key_map *keys_to_use_for_scanning() { return &btree_keys; }
594
-  uint max_supported_keys()          const { return MAX_KEY; }
595
-  uint max_supported_key_part_length() const { return MAX_KEY_LENGTH; }
596
+  uint max_supported_keys()          const { return HP_MAX_KEY; }
597
+  uint max_supported_key_length() const { return HP_MAX_KEY_LENGTH; }
598
+  uint max_supported_key_part_length() const { return HP_MAX_KEY_LENGTH; }
599
   double scan_time()
600
   { return (double) (stats.records+stats.deleted) / 20.0+10; }
601
   double read_time(uint index, uint ranges, ha_rows rows)
602
diff -ruN a/storage/heap/heapdef.h b/storage/heap/heapdef.h
603
--- a/storage/heap/heapdef.h	2011-04-11 13:44:03.000000000 +0300
604
+++ b/storage/heap/heapdef.h	2011-06-02 15:37:43.754916999 +0300
605
@@ -32,6 +32,13 @@
606
 #define HP_MIN_RECORDS_IN_BLOCK 16
607
 #define HP_MAX_RECORDS_IN_BLOCK 8192
608
 
609
+/* this chunk has been deleted and can be reused */
610
+#define CHUNK_STATUS_DELETED 0
611
+/* this chunk represents the first part of a live record */
612
+#define CHUNK_STATUS_ACTIVE  1
613
+/* this chunk is a continuation from another chunk (part of chunkset) */
614
+#define CHUNK_STATUS_LINKED  2
615
+
616
 	/* Some extern variables */
617
 
618
 extern LIST *heap_open_list,*heap_share_list;
619
@@ -42,7 +49,14 @@
620
 #define hp_find_hash(A,B) ((HASH_INFO*) hp_find_block((A),(B)))
621
 
622
 	/* Find pos for record and update it in info->current_ptr */
623
-#define hp_find_record(info,pos) (info)->current_ptr= hp_find_block(&(info)->s->block,pos)
624
+#define hp_find_record(info,pos) \
625
+  (info)->current_ptr= hp_find_block(&(info)->s->recordspace.block,pos)
626
+
627
+#define get_chunk_status(info,ptr) (ptr[(info)->offset_status])
628
+
629
+#define get_chunk_count(info,rec_length) \
630
+  ((rec_length + (info)->chunk_dataspace_length - 1) / \
631
+   (info)->chunk_dataspace_length)
632
 
633
 typedef struct st_hp_hash_info
634
 {
635
@@ -90,7 +104,7 @@
636
 		      const uchar *key);
637
 extern void hp_make_key(HP_KEYDEF *keydef,uchar *key,const uchar *rec);
638
 extern uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key,
639
-			   const uchar *rec, uchar *recpos);
640
+			   const uchar *rec, uchar *recpos, my_bool packed);
641
 extern uint hp_rb_key_length(HP_KEYDEF *keydef, const uchar *key);
642
 extern uint hp_rb_null_key_length(HP_KEYDEF *keydef, const uchar *key);
643
 extern uint hp_rb_var_key_length(HP_KEYDEF *keydef, const uchar *key);
644
@@ -100,6 +114,23 @@
645
 extern void hp_clear_keys(HP_SHARE *info);
646
 extern uint hp_rb_pack_key(HP_KEYDEF *keydef, uchar *key, const uchar *old,
647
                            key_part_map keypart_map);
648
+extern uint hp_calc_blob_length(uint length, const uchar *pos);
649
+
650
+/* Chunkset management (alloc/free/encode/decode) functions */
651
+extern uchar *hp_allocate_chunkset(HP_DATASPACE *info, uint chunk_count);
652
+extern int hp_reallocate_chunkset(HP_DATASPACE *info, uint chunk_count,
653
+                                  uchar *pos);
654
+extern void hp_free_chunks(HP_DATASPACE *info, uchar *pos);
655
+extern void hp_clear_dataspace(HP_DATASPACE *info);
656
+
657
+extern uint hp_get_encoded_data_length(HP_SHARE *info, const uchar *record,
658
+                                       uint *chunk_count);
659
+extern void hp_copy_record_data_to_chunkset(HP_SHARE *info, const uchar *record,
660
+                                            uchar *pos);
661
+extern int hp_extract_record(HP_INFO *info, uchar *record, const uchar *pos);
662
+extern uint hp_process_record_data_to_chunkset(HP_SHARE *info,
663
+                                               const uchar *record, uchar *pos,
664
+                                               uint is_compare);
665
 
666
 extern mysql_mutex_t THR_LOCK_heap;
667
 
668
diff -ruN a/storage/heap/hp_clear.c b/storage/heap/hp_clear.c
669
--- a/storage/heap/hp_clear.c	2011-04-11 13:44:03.000000000 +0300
670
+++ b/storage/heap/hp_clear.c	2011-06-02 15:37:43.754916999 +0300
671
@@ -30,16 +30,11 @@
672
 {
673
   DBUG_ENTER("hp_clear");
674
 
675
-  if (info->block.levels)
676
-    (void) hp_free_level(&info->block,info->block.levels,info->block.root,
677
-			(uchar*) 0);
678
-  info->block.levels=0;
679
+  hp_clear_dataspace(&info->recordspace);
680
   hp_clear_keys(info);
681
-  info->records= info->deleted= 0;
682
-  info->data_length= 0;
683
+  info->records= 0;
684
   info->blength=1;
685
   info->changed=0;
686
-  info->del_link=0;
687
   DBUG_VOID_RETURN;
688
 }
689
 
690
@@ -157,7 +152,7 @@
691
   int error= 0;
692
   HP_SHARE *share= info->s;
693
 
694
-  if (share->data_length || share->index_length)
695
+  if (share->recordspace.total_data_length || share->index_length)
696
     error= HA_ERR_CRASHED;
697
   else
698
     if (share->currently_disabled_keys)
699
diff -ruN a/storage/heap/hp_close.c b/storage/heap/hp_close.c
700
--- a/storage/heap/hp_close.c	2011-04-11 13:44:03.000000000 +0300
701
+++ b/storage/heap/hp_close.c	2011-06-02 15:37:43.754916999 +0300
702
@@ -46,6 +46,10 @@
703
     heap_open_list=list_delete(heap_open_list,&info->open_list);
704
   if (!--info->s->open_count && info->s->delete_on_close)
705
     hp_free(info->s);				/* Table was deleted */
706
+  if (info->blob_buffer)
707
+  {
708
+    my_free(info->blob_buffer);
709
+  }
710
   my_free(info);
711
   DBUG_RETURN(error);
712
 }
713
diff -ruN a/storage/heap/hp_create.c b/storage/heap/hp_create.c
714
--- a/storage/heap/hp_create.c	2011-04-11 13:44:03.000000000 +0300
715
+++ b/storage/heap/hp_create.c	2011-06-02 15:37:43.754916999 +0300
716
@@ -14,11 +14,21 @@
717
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
718
 
719
 #include "heapdef.h"
720
+#include <mysql_com.h>
721
+#include <mysqld_error.h>
722
 
723
 static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2);
724
-static void init_block(HP_BLOCK *block,uint reclength,ulong min_records,
725
+static void init_block(HP_BLOCK *block,uint chunk_length, ulong min_records,
726
 		       ulong max_records);
727
 
728
+#define FIXED_REC_OVERHEAD (sizeof(uchar))
729
+#define VARIABLE_REC_OVERHEAD (sizeof(uchar **) + sizeof(uchar))
730
+
731
+/* Minimum size that a chunk can take, 12 bytes on 32bit, 24 bytes on 64bit */
732
+#define VARIABLE_MIN_CHUNK_SIZE                                         \
733
+  ((sizeof(uchar **) + VARIABLE_REC_OVERHEAD + sizeof(uchar **) - 1) &  \
734
+   ~(sizeof(uchar **) - 1))
735
+
736
 /* Create a heap table */
737
 
738
 int heap_create(const char *name, HP_CREATE_INFO *create_info,
739
@@ -32,6 +42,7 @@
740
   uint keys= create_info->keys;
741
   ulong min_records= create_info->min_records;
742
   ulong max_records= create_info->max_records;
743
+  ulong max_rows_for_stated_memory;
744
   DBUG_ENTER("heap_create");
745
 
746
   if (!create_info->internal_table)
747
@@ -48,15 +59,147 @@
748
 
749
   if (!share)
750
   {
751
+    uint chunk_dataspace_length, chunk_length, is_variable_size;
752
+    uint fixed_data_length, fixed_column_count;
753
     HP_KEYDEF *keyinfo;
754
     DBUG_PRINT("info",("Initializing new table"));
755
-    
756
+
757
+    if (create_info->max_chunk_size)
758
+    {
759
+      uint configured_chunk_size= create_info->max_chunk_size;
760
+
761
+      /* User requested variable-size records, let's see if they're possible */
762
+
763
+      if (configured_chunk_size < create_info->fixed_data_size)
764
+      {
765
+        /*
766
+          The resulting chunk_size cannot be smaller than fixed data part
767
+          at the start of the first chunk which allows faster copying
768
+          with a single memcpy().
769
+        */
770
+        my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "key_block_size");
771
+        goto err;
772
+      }
773
+
774
+      if (reclength > configured_chunk_size + VARIABLE_REC_OVERHEAD ||
775
+	  create_info->blobs > 0)
776
+      {
777
+        /*
778
+          Allow variable size only if we're saving some space, i.e.
779
+          if a fixed-size record would take more space than variable-size
780
+          one plus the variable-size overhead.
781
+          There has to be at least one field after indexed fields.
782
+          Note that NULL bits are already included in key_part_size.
783
+        */
784
+        is_variable_size= 1;
785
+        chunk_dataspace_length= configured_chunk_size;
786
+      }
787
+      else
788
+      {
789
+        /* max_chunk_size is near the full reclength, let's use fixed size */
790
+        is_variable_size= 0;
791
+        chunk_dataspace_length= reclength;
792
+      }
793
+    }
794
+    else if ((create_info->is_dynamic && reclength >
795
+              256 + VARIABLE_REC_OVERHEAD)
796
+             || create_info->blobs > 0)
797
+    {
798
+      /*
799
+        User asked for dynamic records - use 256 as the chunk size, if that
800
+        will may save some memory. Otherwise revert to fixed size format.
801
+      */
802
+      if ((create_info->fixed_data_size + VARIABLE_REC_OVERHEAD) > 256)
803
+        chunk_dataspace_length= create_info->fixed_data_size;
804
+      else
805
+        chunk_dataspace_length= 256 - VARIABLE_REC_OVERHEAD;
806
+
807
+      is_variable_size= 1;
808
+    }
809
+    else
810
+    {
811
+      /*
812
+        If max_chunk_size is not specified, put the whole record in one chunk
813
+      */
814
+      is_variable_size= 0;
815
+      chunk_dataspace_length= reclength;
816
+    }
817
+
818
+    if (is_variable_size)
819
+    {
820
+      /* Check whether we have any variable size records past key data */
821
+      uint has_variable_fields= 0;
822
+
823
+      fixed_data_length= create_info->fixed_data_size;
824
+      fixed_column_count= create_info->fixed_key_fieldnr;
825
+
826
+      for (i= create_info->fixed_key_fieldnr; i < create_info->columns; i++)
827
+      {
828
+        HP_COLUMNDEF *column= create_info->columndef + i;
829
+	if ((column->type == MYSQL_TYPE_VARCHAR &&
830
+	     (column->length - column->length_bytes) >= 32) ||
831
+	    column->type == MYSQL_TYPE_BLOB)
832
+        {
833
+            /*
834
+              The field has to be either blob or >= 5.0.3 true VARCHAR
835
+              and have substantial length.
836
+              TODO: do we want to calculate minimum length?
837
+            */
838
+            has_variable_fields= 1;
839
+            break;
840
+        }
841
+
842
+        if (has_variable_fields)
843
+        {
844
+          break;
845
+        }
846
+
847
+        if ((column->offset + column->length) <= chunk_dataspace_length)
848
+        {
849
+          /* Still no variable-size columns, add one fixed-length */
850
+          fixed_column_count= i + 1;
851
+          fixed_data_length= column->offset + column->length;
852
+        }
853
+      }
854
+
855
+      if (!has_variable_fields && create_info->blobs == 0)
856
+      {
857
+        /*
858
+          There is no need to use variable-size records without variable-size
859
+          columns.
860
+          Reset sizes if it's not variable size anymore.
861
+        */
862
+        is_variable_size= 0;
863
+        chunk_dataspace_length= reclength;
864
+        fixed_data_length= reclength;
865
+        fixed_column_count= create_info->columns;
866
+      }
867
+    }
868
+    else
869
+    {
870
+      fixed_data_length= reclength;
871
+      fixed_column_count= create_info->columns;
872
+    }
873
+
874
     /*
875
-      We have to store sometimes uchar* del_link in records,
876
-      so the record length should be at least sizeof(uchar*)
877
+      We store uchar* del_link inside the data area of deleted records,
878
+      so the data length should be at least sizeof(uchar*)
879
     */
880
-    set_if_bigger(reclength, sizeof (uchar*));
881
-    
882
+    set_if_bigger(chunk_dataspace_length, sizeof (uchar **));
883
+
884
+    if (is_variable_size)
885
+    {
886
+      chunk_length= chunk_dataspace_length + VARIABLE_REC_OVERHEAD;
887
+    }
888
+    else
889
+    {
890
+      chunk_length= chunk_dataspace_length + FIXED_REC_OVERHEAD;
891
+    }
892
+
893
+    /* Align chunk length to the next pointer */
894
+    chunk_length= (uint) (chunk_length + sizeof(uchar **) - 1) &
895
+      ~(sizeof(uchar **) - 1);
896
+
897
     for (i= key_segs= max_length= 0, keyinfo= keydef; i < keys; i++, keyinfo++)
898
     {
899
       bzero((char*) &keyinfo->block,sizeof(keyinfo->block));
900
@@ -73,42 +216,11 @@
901
 	    keyinfo->rb_tree.size_of_element++;
902
 	}
903
 	switch (keyinfo->seg[j].type) {
904
-	case HA_KEYTYPE_SHORT_INT:
905
-	case HA_KEYTYPE_LONG_INT:
906
-	case HA_KEYTYPE_FLOAT:
907
-	case HA_KEYTYPE_DOUBLE:
908
-	case HA_KEYTYPE_USHORT_INT:
909
-	case HA_KEYTYPE_ULONG_INT:
910
-	case HA_KEYTYPE_LONGLONG:
911
-	case HA_KEYTYPE_ULONGLONG:
912
-	case HA_KEYTYPE_INT24:
913
-	case HA_KEYTYPE_UINT24:
914
-	case HA_KEYTYPE_INT8:
915
-	  keyinfo->seg[j].flag|= HA_SWAP_KEY;
916
-          break;
917
         case HA_KEYTYPE_VARBINARY1:
918
-          /* Case-insensitiveness is handled in coll->hash_sort */
919
-          keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
920
-          /* fall_through */
921
         case HA_KEYTYPE_VARTEXT1:
922
-          keyinfo->flag|= HA_VAR_LENGTH_KEY;
923
-          length+= 2;
924
-          /* Save number of bytes used to store length */
925
-          keyinfo->seg[j].bit_start= 1;
926
-          break;
927
         case HA_KEYTYPE_VARBINARY2:
928
-          /* Case-insensitiveness is handled in coll->hash_sort */
929
-          /* fall_through */
930
         case HA_KEYTYPE_VARTEXT2:
931
-          keyinfo->flag|= HA_VAR_LENGTH_KEY;
932
           length+= 2;
933
-          /* Save number of bytes used to store length */
934
-          keyinfo->seg[j].bit_start= 2;
935
-          /*
936
-            Make future comparison simpler by only having to check for
937
-            one type
938
-          */
939
-          keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
940
           break;
941
 	default:
942
 	  break;
943
@@ -133,13 +245,34 @@
944
     }
945
     if (!(share= (HP_SHARE*) my_malloc((uint) sizeof(HP_SHARE)+
946
 				       keys*sizeof(HP_KEYDEF)+
947
+                                       (create_info->columns *
948
+                                        sizeof(HP_COLUMNDEF)) +
949
 				       key_segs*sizeof(HA_KEYSEG),
950
 				       MYF(MY_ZEROFILL))))
951
       goto err;
952
-    share->keydef= (HP_KEYDEF*) (share + 1);
953
+
954
+    /*
955
+      Max_records is used for estimating block sizes and for enforcement.
956
+      Calculate the very maximum number of rows (if everything was one chunk)
957
+      and then take either that value or configured max_records (pick smallest
958
+      one).
959
+    */
960
+    max_rows_for_stated_memory= (ha_rows) (create_info->max_table_size /
961
+                                           (create_info->keys_memory_size +
962
+                                            chunk_length));
963
+    max_records = ((max_records && max_records < max_rows_for_stated_memory) ?
964
+                   max_records : max_rows_for_stated_memory);
965
+
966
+    share->column_defs= (HP_COLUMNDEF*) (share + 1);
967
+    memcpy(share->column_defs, create_info->columndef,
968
+           (size_t) (sizeof(create_info->columndef[0]) *
969
+                     create_info->columns));
970
+
971
+    share->keydef= (HP_KEYDEF*) (share->column_defs + create_info->columns);
972
     share->key_stat_version= 1;
973
     keyseg= (HA_KEYSEG*) (share->keydef + keys);
974
-    init_block(&share->block, reclength + 1, min_records, max_records);
975
+    init_block(&share->recordspace.block, chunk_length, min_records,
976
+               max_records);
977
 	/* Fix keys */
978
     memcpy(share->keydef, keydef, (size_t) (sizeof(keydef[0]) * keys));
979
     for (i= 0, keyinfo= share->keydef; i < keys; i++, keyinfo++)
980
@@ -177,15 +310,35 @@
981
     share->min_records= min_records;
982
     share->max_records= max_records;
983
     share->max_table_size= create_info->max_table_size;
984
-    share->data_length= share->index_length= 0;
985
-    share->reclength= reclength;
986
+    share->index_length= 0;
987
     share->blength= 1;
988
     share->keys= keys;
989
     share->max_key_length= max_length;
990
+    share->column_count= create_info->columns;
991
     share->changed= 0;
992
     share->auto_key= create_info->auto_key;
993
     share->auto_key_type= create_info->auto_key_type;
994
     share->auto_increment= create_info->auto_increment;
995
+
996
+    share->fixed_data_length= fixed_data_length;
997
+    share->fixed_column_count= fixed_column_count;
998
+    share->blobs= create_info->blobs;
999
+
1000
+    share->recordspace.chunk_length= chunk_length;
1001
+    share->recordspace.chunk_dataspace_length= chunk_dataspace_length;
1002
+    share->recordspace.is_variable_size= is_variable_size;
1003
+    share->recordspace.total_data_length= 0;
1004
+
1005
+    if (is_variable_size) {
1006
+      share->recordspace.offset_link= chunk_dataspace_length;
1007
+      share->recordspace.offset_status= share->recordspace.offset_link +
1008
+        sizeof(uchar **);
1009
+    } else {
1010
+      /* Make it likely to fail if anyone uses this offset */
1011
+      share->recordspace.offset_link= 1 << 22;
1012
+      share->recordspace.offset_status= chunk_dataspace_length;
1013
+    }
1014
+
1015
     /* Must be allocated separately for rename to work */
1016
     if (!(share->name= my_strdup(name,MYF(0))))
1017
     {
1018
@@ -227,7 +380,7 @@
1019
 		    param->search_flag, not_used);
1020
 }
1021
 
1022
-static void init_block(HP_BLOCK *block, uint reclength, ulong min_records,
1023
+static void init_block(HP_BLOCK *block, uint chunk_length, ulong min_records,
1024
 		       ulong max_records)
1025
 {
1026
   uint i,recbuffer,records_in_block;
1027
@@ -235,7 +388,12 @@
1028
   max_records= max(min_records,max_records);
1029
   if (!max_records)
1030
     max_records= 1000;			/* As good as quess as anything */
1031
-  recbuffer= (uint) (reclength + sizeof(uchar**) - 1) & ~(sizeof(uchar**) - 1);
1032
+  /*
1033
+    We want to start each chunk at 8 bytes boundary, round recbuffer to the
1034
+    next 8.
1035
+  */
1036
+  recbuffer= (uint) (chunk_length + sizeof(uchar**) - 1) &
1037
+    ~(sizeof(uchar**) - 1);
1038
   records_in_block= max_records / 10;
1039
   if (records_in_block < 10 && max_records)
1040
     records_in_block= 10;
1041
diff -ruN a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c
1042
--- a/storage/heap/hp_delete.c	2011-04-11 13:44:03.000000000 +0300
1043
+++ b/storage/heap/hp_delete.c	2011-06-02 15:37:43.754916999 +0300
1044
@@ -22,6 +22,8 @@
1045
   uchar *pos;
1046
   HP_SHARE *share=info->s;
1047
   HP_KEYDEF *keydef, *end, *p_lastinx;
1048
+  uint rec_length, chunk_count;
1049
+
1050
   DBUG_ENTER("heap_delete");
1051
   DBUG_PRINT("enter",("info: 0x%lx  record: 0x%lx", (long) info, (long) record));
1052
 
1053
@@ -31,6 +33,8 @@
1054
     DBUG_RETURN(my_errno);			/* Record changed */
1055
   share->changed=1;
1056
 
1057
+  rec_length = hp_get_encoded_data_length(share, record, &chunk_count);
1058
+
1059
   if ( --(share->records) < share->blength >> 1) share->blength>>=1;
1060
   pos=info->current_ptr;
1061
 
1062
@@ -43,10 +47,7 @@
1063
   }
1064
 
1065
   info->update=HA_STATE_DELETED;
1066
-  *((uchar**) pos)=share->del_link;
1067
-  share->del_link=pos;
1068
-  pos[share->reclength]=0;		/* Record deleted */
1069
-  share->deleted++;
1070
+  hp_free_chunks(&share->recordspace, pos);
1071
   info->current_hash_ptr=0;
1072
 #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
1073
   DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
1074
@@ -75,7 +76,8 @@
1075
     info->last_pos= NULL; /* For heap_rnext/heap_rprev */
1076
 
1077
   custom_arg.keyseg= keyinfo->seg;
1078
-  custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
1079
+  custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos,
1080
+                                        FALSE);
1081
   custom_arg.search_flag= SEARCH_SAME;
1082
   old_allocated= keyinfo->rb_tree.allocated;
1083
   res= tree_delete(&keyinfo->rb_tree, info->recbuf, custom_arg.key_length,
1084
@@ -112,6 +114,7 @@
1085
   blength=share->blength;
1086
   if (share->records+1 == blength)
1087
     blength+= blength;
1088
+
1089
   lastpos=hp_find_hash(&keyinfo->block,share->records);
1090
   last_ptr=0;
1091
 
1092
diff -ruN /dev/null b/storage/heap/hp_dspace.c
1093
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
1094
+++ b/storage/heap/hp_dspace.c	2011-06-02 15:40:14.104917001 +0300
1095
@@ -0,0 +1,440 @@
1096
+/* Copyright (C) 2000-2002 MySQL AB
1097
+   Copyright (C) 2008 eBay, Inc
1098
+
1099
+   This program is free software; you can redistribute it and/or modify
1100
+   it under the terms of the GNU General Public License as published by
1101
+   the Free Software Foundation; version 2 of the License.
1102
+
1103
+   This program is distributed in the hope that it will be useful,
1104
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
1105
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1106
+   GNU General Public License for more details.
1107
+
1108
+   You should have received a copy of the GNU General Public License
1109
+   along with this program; if not, write to the Free Software
1110
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
1111
+
1112
+/*
1113
+  Implements various base dataspace-related functions - allocate, free, clear
1114
+*/
1115
+
1116
+#include "heapdef.h"
1117
+
1118
+
1119
+/*
1120
+  MySQL Heap tables keep data in arrays of fixed-size chunks.
1121
+  These chunks are organized into two groups of HP_BLOCK structures:
1122
+    - group1 contains indexes, with one HP_BLOCK per key
1123
+      (part of HP_KEYDEF)
1124
+    - group2 contains record data, with single HP_BLOCK
1125
+      for all records, referenced by HP_SHARE.recordspace.block
1126
+
1127
+  While columns used in index are usually small, other columns
1128
+  in the table may need to accomodate larger data. Typically,
1129
+  larger data is placed into VARCHAR or BLOB columns. With actual
1130
+  sizes varying, Heap Engine has to support variable-sized records
1131
+  in memory. Heap Engine implements the concept of dataspace
1132
+  (HP_DATASPACE), which incorporates HP_BLOCK for the record data,
1133
+  and adds more information for managing variable-sized records.
1134
+
1135
+  Variable-size records are stored in multiple "chunks",
1136
+  which means that a single record of data (database "row") can
1137
+  consist of multiple chunks organized into one "set". HP_BLOCK
1138
+  contains chunks. In variable-size format, one record
1139
+  is represented as one or many chunks, depending on the actual
1140
+  data, while in fixed-size mode, one record is always represented
1141
+  as one chunk. The index structures would always point to the first
1142
+  chunk in the chunkset.
1143
+
1144
+  At the time of table creation, Heap Engine attempts to find out if
1145
+  variable-size records are desired. A user can request
1146
+  variable-size records by providing either row_type=dynamic or
1147
+  key_block_size=NNN table create option. Heap Engine will check
1148
+  whether key_block_size provides enough space in the first chunk
1149
+  to keep all null bits and columns that are used in indexes.
1150
+  If key_block_size is too small, table creation will be aborted
1151
+  with an error. Heap Engine will revert to fixed-size allocation
1152
+  mode if key_block_size provides no memory benefits (if the
1153
+  fixed-size record would always be shorter then the first chunk
1154
+  in the chunkset with the specified key_block_size).
1155
+
1156
+  In order to improve index search performance, Heap Engine needs
1157
+  to keep all null flags and all columns used as keys inside
1158
+  the first chunk of a chunkset. In particular, this means that
1159
+  all columns used as keys should be defined first in the table
1160
+  creation SQL. The length of data used by null bits and key columns
1161
+  is stored as fixed_data_length inside HP_SHARE. fixed_data_length
1162
+  will extend past last key column if more fixed-length fields can
1163
+  fit into the first chunk.
1164
+
1165
+  Variable-size records are necessary only in the presence of
1166
+  variable-size columns. Heap Engine will be looking for BLOB
1167
+  columns or VARCHAR columns, which declare length of 32 or more. If
1168
+  no such columns are found, table will be switched to fixed-size
1169
+  format. You should always try to put such columns at the end of
1170
+  the table definition.
1171
+
1172
+  Whenever data is being inserted or updated in the table
1173
+  Heap Engine will calculate how many chunks are necessary.
1174
+  For insert operations, Heap Engine allocates new chunkset in
1175
+  the recordspace. For update operations it will modify length of
1176
+  the existing chunkset, unlinking unnecessary chunks at the end,
1177
+  or allocating and adding more if larger length is necessary.
1178
+
1179
+  When writing data to chunks or copying data back to record,
1180
+  fixed-size columns are copied in their full format. VARCHARs and
1181
+  BLOBs are copied based on their actual length. Any NULL values
1182
+  after fixed_data_length are skipped.
1183
+
1184
+  The allocation and contents of the actual chunks varies between
1185
+  fixed and variable-size modes. Total chunk length is always
1186
+  aligned to the next sizeof(uchar*). Here is the format of
1187
+  fixed-size chunk:
1188
+      uchar[] - sizeof=chunk_dataspace_length, but at least
1189
+                sizeof(uchar*) bytes. Keeps actual data or pointer
1190
+                to the next deleted chunk.
1191
+                chunk_dataspace_length equals to full record length
1192
+      uchar   - status field (1 means "in use", 0 means "deleted")
1193
+
1194
+  Variable-size chunk uses different format:
1195
+      uchar[] - sizeof=chunk_dataspace_length, but at least
1196
+                sizeof(uchar*) bytes. Keeps actual data or pointer
1197
+                to the next deleted chunk.
1198
+                chunk_dataspace_length is set according to table
1199
+                setup (key_block_size)
1200
+      uchar*  - pointer to the next chunk in this chunkset,
1201
+                or NULL for the last chunk
1202
+      uchar   - status field (1 means "first", 0 means "deleted",
1203
+                2 means "linked")
1204
+
1205
+  When allocating a new chunkset of N chunks, Heap Engine will try
1206
+  to allocate chunks one-by-one, linking them as they become
1207
+  allocated. Allocation of a single chunk will attempt to reuse
1208
+  a deleted (freed) chunk. If no free chunks are available,
1209
+  it will attempt to allocate a new area inside HP_BLOCK.
1210
+  Freeing chunks will place them at the front of free list
1211
+  referenced by del_link in HP_DATASPACE. The newly freed chunk
1212
+  will contain reference to the previously freed chunk in its first
1213
+  sizeof(uchar*) of the payload space.
1214
+
1215
+  Here is open issues:
1216
+    -  It is not very nice to require people to keep key columns
1217
+       at the beginning of the table creation SQL. There are three
1218
+       proposed resolutions:
1219
+       a. Leave it as is. It's a reasonable limitation
1220
+       b. Add new HA_KEEP_KEY_COLUMNS_TO_FRONT flag to handler.h and
1221
+          make table.cpp align columns when it creates the table
1222
+       c. Make HeapEngine reorder columns in the chunk data, so that
1223
+          key columns go first. Add parallel HA_KEYSEG structures
1224
+          to distinguish positions in record vs. positions in
1225
+          the first chunk. Copy all data field-by-field rather than
1226
+          using single memcpy unless DBA kept key columns to
1227
+          the beginning.
1228
+    -  heap_check_heap needs verify linked chunks, looking for
1229
+       issues such as orphans, cycles, and bad links. However,
1230
+       Heap Engine today does not do similar things even for
1231
+       free list.
1232
+    -  In a more sophisticated implementation, some space can
1233
+       be saved even with all fixed-size columns if many of them
1234
+       have NULL value, as long as these columns are not used
1235
+       in indexes
1236
+    -  In variable-size format status should be moved to lower
1237
+       bits of the "next" pointer. Pointer is always aligned
1238
+       to sizeof(byte*), which is at least 4, leaving 2 lower
1239
+       bits free. This will save 8 bytes per chunk
1240
+       on 64-bit platform.
1241
+    -  As we do not want to modify FRM format or to add new SQL
1242
+       keywords, KEY_BLOCK_SIZE option of "CREATE TABLE" is reused
1243
+       to specify block size for Heap Engine tables.
1244
+    -  since all key columns must fit in the first chunk, having keys
1245
+       on BLOB columns is currently impossible. This limitation is
1246
+       relatively easiy to remove in future.
1247
+*/
1248
+
1249
+static uchar *hp_allocate_one_chunk(HP_DATASPACE *info);
1250
+
1251
+
1252
+/**
1253
+  Clear a dataspace
1254
+
1255
+  Frees memory and zeros-out any relevant counters in the dataspace
1256
+
1257
+  @param  info  the dataspace to clear
1258
+*/
1259
+
1260
+void hp_clear_dataspace(HP_DATASPACE *info)
1261
+{
1262
+  if (info->block.levels)
1263
+  {
1264
+    hp_free_level(&info->block,info->block.levels,info->block.root,
1265
+                  (uchar *) 0);
1266
+  }
1267
+  info->block.levels= 0;
1268
+  info->del_chunk_count= info->chunk_count= 0;
1269
+  info->del_link= 0;
1270
+  info->total_data_length= 0;
1271
+}
1272
+
1273
+
1274
+/**
1275
+  Allocate or reallocate a chunkset in the dataspace
1276
+
1277
+  Attempts to allocate a new chunkset or change the size of an existing chunkset
1278
+
1279
+  @param  info            the hosting dataspace
1280
+  @param  chunk_count     the number of chunks that we expect as the result
1281
+  @param  existing_set    non-null value asks function to resize existing
1282
+                          chunkset, return value would point to this set
1283
+
1284
+  @return  Pointer to the first chunk in the new or updated chunkset, or NULL
1285
+           if unsuccessful
1286
+*/
1287
+
1288
+static uchar *hp_allocate_variable_chunkset(HP_DATASPACE *info,
1289
+                                           uint chunk_count,
1290
+                                           uchar *existing_set)
1291
+{
1292
+  int alloc_count= chunk_count, i;
1293
+  uchar *first_chunk= 0, *curr_chunk= 0, *prev_chunk= 0;
1294
+  uchar  *last_existing_chunk= 0;
1295
+
1296
+  DBUG_ASSERT(alloc_count);
1297
+
1298
+  if (existing_set)
1299
+  {
1300
+    first_chunk= existing_set;
1301
+
1302
+    curr_chunk= existing_set;
1303
+    while (curr_chunk && alloc_count)
1304
+    {
1305
+      prev_chunk= curr_chunk;
1306
+      curr_chunk= *((uchar **) (curr_chunk + info->offset_link));
1307
+      alloc_count--;
1308
+    }
1309
+
1310
+    if (!alloc_count)
1311
+    {
1312
+      if (curr_chunk)
1313
+      {
1314
+        /*
1315
+          We came through all chunks and there is more left, let's truncate the
1316
+          list.
1317
+        */
1318
+        *((uchar **) (prev_chunk + info->offset_link))= NULL;
1319
+        hp_free_chunks(info, curr_chunk);
1320
+      }
1321
+
1322
+      return first_chunk;
1323
+    }
1324
+
1325
+    last_existing_chunk= prev_chunk;
1326
+  }
1327
+
1328
+  /*
1329
+    We can reach this point only if we're allocating new chunkset or more chunks
1330
+    in existing set.
1331
+  */
1332
+
1333
+  for (i= 0; i < alloc_count; i++)
1334
+  {
1335
+    curr_chunk= hp_allocate_one_chunk(info);
1336
+    if (!curr_chunk)
1337
+    {
1338
+      /* no space in the current block */
1339
+
1340
+      if (last_existing_chunk)
1341
+      {
1342
+        /* Truncate whatever was added at the end of the existing chunkset */
1343
+        prev_chunk= last_existing_chunk;
1344
+        curr_chunk= *((uchar **)(prev_chunk + info->offset_link));
1345
+        *((uchar **)(prev_chunk + info->offset_link))= NULL;
1346
+        hp_free_chunks(info, curr_chunk);
1347
+      }
1348
+      else if (first_chunk)
1349
+      {
1350
+        /* free any chunks previously allocated */
1351
+        hp_free_chunks(info, first_chunk);
1352
+      }
1353
+
1354
+      return NULL;
1355
+    }
1356
+
1357
+    /* mark as if this chunk is last in the chunkset */
1358
+    *((uchar **) (curr_chunk + info->offset_link))= 0;
1359
+
1360
+    if (prev_chunk)
1361
+    {
1362
+      /* tie them into a linked list */
1363
+      *((uchar **) (prev_chunk + info->offset_link))= curr_chunk;
1364
+      /* Record linked from active */
1365
+      curr_chunk[info->offset_status]= CHUNK_STATUS_LINKED;
1366
+    }
1367
+    else
1368
+    {
1369
+      /* Record active */
1370
+      curr_chunk[info->offset_status]= CHUNK_STATUS_ACTIVE;
1371
+    }
1372
+
1373
+    if (!first_chunk)
1374
+    {
1375
+      first_chunk= curr_chunk;
1376
+    }
1377
+
1378
+    prev_chunk= curr_chunk;
1379
+}
1380
+
1381
+  return first_chunk;
1382
+}
1383
+
1384
+
1385
+/**
1386
+  Allocate a new chunkset in the dataspace
1387
+
1388
+  Attempts to allocate a new chunkset
1389
+
1390
+  @param  info            the hosting dataspace
1391
+  @param  chunk_count     the number of chunks that we expect as the result
1392
+
1393
+  @return  Pointer to the first chunk in the new or updated chunkset, or NULL if
1394
+           unsuccessful
1395
+*/
1396
+
1397
+uchar *hp_allocate_chunkset(HP_DATASPACE *info, uint chunk_count)
1398
+{
1399
+  uchar *result;
1400
+
1401
+  DBUG_ENTER("hp_allocate_chunks");
1402
+
1403
+  if (info->is_variable_size)
1404
+  {
1405
+    result = hp_allocate_variable_chunkset(info, chunk_count, NULL);
1406
+  }
1407
+  else
1408
+  {
1409
+    result= hp_allocate_one_chunk(info);
1410
+    if (result)
1411
+    {
1412
+      result[info->offset_status]= CHUNK_STATUS_ACTIVE;
1413
+    }
1414
+
1415
+    DBUG_RETURN(result);
1416
+  }
1417
+
1418
+  DBUG_RETURN(result);
1419
+}
1420
+
1421
+
1422
+/**
1423
+  Reallocate an existing chunkset in the dataspace
1424
+
1425
+  Attempts to change the size of an existing chunkset
1426
+
1427
+  @param  info            the hosting dataspace
1428
+  @param  chunk_count     the number of chunks that we expect as the result
1429
+  @param  pos             pointer to the existing chunkset
1430
+
1431
+  @return  Error code or zero if successful
1432
+*/
1433
+
1434
+int hp_reallocate_chunkset(HP_DATASPACE *info, uint chunk_count, uchar *pos)
1435
+{
1436
+  DBUG_ENTER("hp_reallocate_chunks");
1437
+
1438
+  if (!info->is_variable_size)
1439
+  {
1440
+    /* Update should never change chunk_count in fixed-size mode */
1441
+    my_errno= HA_ERR_WRONG_COMMAND;
1442
+    return my_errno;
1443
+  }
1444
+
1445
+  /* Reallocate never moves the first chunk */
1446
+  if (!hp_allocate_variable_chunkset(info, chunk_count, pos))
1447
+    DBUG_RETURN(my_errno);
1448
+
1449
+  DBUG_RETURN(0);
1450
+}
1451
+
1452
+
1453
+/**
1454
+  Allocate a single chunk in the dataspace
1455
+
1456
+  Attempts to allocate a new chunk or reuse one from deleted list
1457
+
1458
+  @param  info            the hosting dataspace
1459
+
1460
+  @return  Pointer to the chunk, or NULL if unsuccessful
1461
+*/
1462
+
1463
+static uchar *hp_allocate_one_chunk(HP_DATASPACE *info)
1464
+{
1465
+  uchar *curr_chunk;
1466
+  size_t length;
1467
+  ulong block_pos;
1468
+
1469
+  if (info->del_link)
1470
+  {
1471
+    curr_chunk= info->del_link;
1472
+    info->del_link= *((uchar **) curr_chunk);
1473
+    info->del_chunk_count--;
1474
+
1475
+    DBUG_PRINT("hp_allocate_one_chunk",
1476
+               ("Used old position: 0x%lx",(long) curr_chunk));
1477
+    return curr_chunk;
1478
+  }
1479
+
1480
+  block_pos= (info->chunk_count % info->block.records_in_block);
1481
+  if (!block_pos)
1482
+  {
1483
+    if (hp_get_new_block(&info->block, &length))
1484
+    {
1485
+      /* no space in the current block */
1486
+      return NULL;
1487
+    }
1488
+
1489
+    info->total_data_length+= length;
1490
+  }
1491
+
1492
+  info->chunk_count++;
1493
+  curr_chunk= ((uchar *) info->block.level_info[0].last_blocks +
1494
+               block_pos * info->block.recbuffer);
1495
+
1496
+  DBUG_PRINT("hp_allocate_one_chunk",
1497
+             ("Used new position: 0x%lx", (long) curr_chunk));
1498
+
1499
+  return curr_chunk;
1500
+}
1501
+
1502
+
1503
+/**
1504
+  Free a list of chunks
1505
+
1506
+  Reclaims all chunks linked by the pointer,
1507
+  which could be the whole chunkset or a part of an existing chunkset
1508
+
1509
+  @param  info            the hosting dataspace
1510
+  @param  pos             pointer to the head of the chunkset
1511
+*/
1512
+
1513
+void hp_free_chunks(HP_DATASPACE *info, uchar *pos)
1514
+{
1515
+  uchar *curr_chunk= pos;
1516
+
1517
+  while (curr_chunk)
1518
+  {
1519
+    info->del_chunk_count++;
1520
+    *((uchar **) curr_chunk)= info->del_link;
1521
+    info->del_link= curr_chunk;
1522
+
1523
+    curr_chunk[info->offset_status]= CHUNK_STATUS_DELETED;
1524
+
1525
+    DBUG_PRINT("hp_free_chunks",("Freed position: 0x%lx", (long) curr_chunk));
1526
+
1527
+    if (!info->is_variable_size)
1528
+    {
1529
+      break;
1530
+    }
1531
+
1532
+    /* Delete next chunk in this chunkset */
1533
+    curr_chunk= *((uchar **)(curr_chunk + info->offset_link));
1534
+  }
1535
+}
1536
diff -ruN a/storage/heap/hp_extra.c b/storage/heap/hp_extra.c
1537
--- a/storage/heap/hp_extra.c	2011-04-11 13:44:03.000000000 +0300
1538
+++ b/storage/heap/hp_extra.c	2011-06-02 15:37:57.814917006 +0300
1539
@@ -56,7 +56,6 @@
1540
   info->current_record= (ulong) ~0L;
1541
   info->current_hash_ptr=0;
1542
   info->update=0;
1543
-  info->next_block=0;
1544
   return 0;
1545
 }
1546
 
1547
diff -ruN a/storage/heap/hp_hash.c b/storage/heap/hp_hash.c
1548
--- a/storage/heap/hp_hash.c	2011-04-11 13:44:03.000000000 +0300
1549
+++ b/storage/heap/hp_hash.c	2011-06-02 15:37:57.824916999 +0300
1550
@@ -336,16 +336,26 @@
1551
     {
1552
       CHARSET_INFO *cs= seg->charset;
1553
       uint pack_length= seg->bit_start;
1554
-      uint length= (pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos));
1555
+      uint length= hp_calc_blob_length(pack_length, pos);
1556
+
1557
+      if (seg->flag & HA_BLOB_PART)
1558
+      {
1559
+        memcpy(&pos, pos + pack_length, sizeof(char *));
1560
+      }
1561
+      else
1562
+      {
1563
+        pos+= pack_length;
1564
+      }
1565
+
1566
       if (cs->mbmaxlen > 1)
1567
       {
1568
         uint char_length;
1569
-        char_length= my_charpos(cs, pos + pack_length,
1570
-                                pos + pack_length + length,
1571
+        char_length= my_charpos(cs, pos,
1572
+                                pos + length,
1573
                                 seg->length/cs->mbmaxlen);
1574
         set_if_smaller(length, char_length);
1575
       }
1576
-      cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2);
1577
+      cs->coll->hash_sort(cs, pos, length, &nr, &nr2);
1578
     }
1579
     else
1580
     {
1581
@@ -545,18 +555,18 @@
1582
       uint char_length1, char_length2;
1583
       uint pack_length= seg->bit_start;
1584
       CHARSET_INFO *cs= seg->charset;
1585
-      if (pack_length == 1)
1586
-      {
1587
-        char_length1= (uint) *(uchar*) pos1++;
1588
-        char_length2= (uint) *(uchar*) pos2++;
1589
-      }
1590
-      else
1591
+
1592
+      char_length1= hp_calc_blob_length(pack_length, pos1);
1593
+      char_length2= hp_calc_blob_length(pack_length, pos2);
1594
+      pos1+= pack_length;
1595
+      pos2+= pack_length;
1596
+
1597
+      if (seg->flag & HA_BLOB_PART)
1598
       {
1599
-        char_length1= uint2korr(pos1);
1600
-        char_length2= uint2korr(pos2);
1601
-        pos1+= 2;
1602
-        pos2+= 2;
1603
+        memcpy(&pos1, pos1, sizeof(char *));
1604
+        memcpy(&pos2, pos2, sizeof(char *));
1605
       }
1606
+
1607
       if (cs->mbmaxlen > 1)
1608
       {
1609
         uint safe_length1= char_length1;
1610
@@ -668,6 +678,34 @@
1611
 }
1612
 
1613
 
1614
+/**
1615
+   Returns a BLOB length stored in the specified number of bytes at the
1616
+   specified location.
1617
+
1618
+   @param length       the number of bytes used to store length
1619
+   @param pos          pointer to length bytes
1620
+
1621
+   @return  Length of BLOB data.
1622
+*/
1623
+
1624
+uint hp_calc_blob_length(uint bytes, const uchar *pos)
1625
+{
1626
+  switch (bytes) {
1627
+  case 1:
1628
+    return (uint) *pos;
1629
+  case 2:
1630
+    return uint2korr(pos);
1631
+  case 3:
1632
+    return uint3korr(pos);
1633
+  case 4:
1634
+    return uint4korr(pos);
1635
+  default:
1636
+    break;
1637
+  }
1638
+
1639
+  return 0; /* Impossible */
1640
+}
1641
+
1642
 	/* Copy a key from a record to a keybuffer */
1643
 
1644
 void hp_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec)
1645
@@ -678,18 +716,37 @@
1646
   {
1647
     CHARSET_INFO *cs= seg->charset;
1648
     uint char_length= seg->length;
1649
-    uchar *pos= (uchar*) rec + seg->start;
1650
+    const uchar *pos= rec + seg->start;
1651
     if (seg->null_bit)
1652
       *key++= test(rec[seg->null_pos] & seg->null_bit);
1653
-    if (cs->mbmaxlen > 1)
1654
+
1655
+    if (seg->flag & HA_BLOB_PART)
1656
     {
1657
-      char_length= my_charpos(cs, pos, pos + seg->length,
1658
-                              char_length / cs->mbmaxlen);
1659
-      set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */
1660
-    }
1661
-    if (seg->type == HA_KEYTYPE_VARTEXT1)
1662
-      char_length+= seg->bit_start;             /* Copy also length */
1663
-    memcpy(key,rec+seg->start,(size_t) char_length);
1664
+      uint tmp_length= hp_calc_blob_length(seg->bit_start, pos);
1665
+      uint length= min(seg->length, tmp_length);
1666
+
1667
+      memcpy(&pos, rec + seg->bit_start, sizeof(char *));
1668
+      if (cs->mbmaxlen > 1)
1669
+      {
1670
+        char_length= my_charpos(cs, pos, pos + seg->length,
1671
+                                char_length / cs->mbmaxlen);
1672
+        set_if_smaller(char_length, length); /* QQ: ok to remove? */
1673
+      }
1674
+      store_key_length_inc(key, char_length);
1675
+    }
1676
+    else
1677
+    {
1678
+      if (cs->mbmaxlen > 1)
1679
+      {
1680
+        char_length= my_charpos(cs, pos, pos + seg->length,
1681
+                                char_length / cs->mbmaxlen);
1682
+        set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */
1683
+      }
1684
+      if (seg->type == HA_KEYTYPE_VARTEXT1)
1685
+        char_length+= seg->bit_start;             /* Copy also length */
1686
+    }
1687
+
1688
+    memcpy(key, pos, (size_t) char_length);
1689
     key+= char_length;
1690
   }
1691
 }
1692
@@ -702,8 +759,8 @@
1693
   } while(0)
1694
 
1695
 
1696
-uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key, 
1697
-		    const uchar *rec, uchar *recpos)
1698
+uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key,
1699
+                    const uchar *rec, uchar *recpos, my_bool packed)
1700
 {
1701
   uchar *start_key= key;
1702
   HA_KEYSEG *seg, *endseg;
1703
@@ -772,6 +829,29 @@
1704
       key+= char_length;
1705
       continue;
1706
     }
1707
+    else if (seg->flag & HA_BLOB_PART)
1708
+    {
1709
+      uchar *pos=      (uchar*) rec + seg->start;
1710
+      uint tmp_length= hp_calc_blob_length(seg->bit_start, pos);
1711
+      uint length= min(seg->length, tmp_length);
1712
+      CHARSET_INFO *cs= seg->charset;
1713
+      char_length= seg->length / cs->mbmaxlen;
1714
+
1715
+      /* check_one_rb_key() calls hp_rb_make_key() for already packed records */
1716
+      if (!packed)
1717
+      {
1718
+        memcpy(&pos, pos + seg->bit_start, sizeof(char *));
1719
+      }
1720
+      else
1721
+      {
1722
+        pos+= seg->bit_start;
1723
+      }
1724
+      FIX_LENGTH(cs, pos, length, char_length);
1725
+      store_key_length_inc(key, char_length);
1726
+      memcpy(key, pos, (size_t) char_length);
1727
+      key+= char_length;
1728
+      continue;
1729
+    }
1730
 
1731
     char_length= seg->length;
1732
     if (seg->charset->mbmaxlen > 1)
1733
diff -ruN a/storage/heap/hp_info.c b/storage/heap/hp_info.c
1734
--- a/storage/heap/hp_info.c	2011-04-11 13:44:03.000000000 +0300
1735
+++ b/storage/heap/hp_info.c	2011-06-02 15:37:57.824916999 +0300
1736
@@ -47,9 +47,22 @@
1737
 {
1738
   DBUG_ENTER("heap_info");
1739
   x->records         = info->s->records;
1740
-  x->deleted         = info->s->deleted;
1741
-  x->reclength       = info->s->reclength;
1742
-  x->data_length     = info->s->data_length;
1743
+  x->deleted         = info->s->recordspace.del_chunk_count;
1744
+
1745
+  if (info->s->recordspace.is_variable_size)
1746
+  {
1747
+    if (info->s->records)
1748
+      x->reclength   = (uint) (info->s->recordspace.total_data_length /
1749
+                               (ulonglong) info->s->records);
1750
+    else
1751
+      x->reclength   = info->s->recordspace.chunk_length;
1752
+  }
1753
+  else
1754
+  {
1755
+    x->reclength     = info->s->recordspace.chunk_dataspace_length;
1756
+  }
1757
+
1758
+  x->data_length     = info->s->recordspace.total_data_length;
1759
   x->index_length    = info->s->index_length;
1760
   x->max_records     = info->s->max_records;
1761
   x->errkey          = info->errkey;
1762
diff -ruN a/storage/heap/hp_open.c b/storage/heap/hp_open.c
1763
--- a/storage/heap/hp_open.c	2011-04-11 13:44:03.000000000 +0300
1764
+++ b/storage/heap/hp_open.c	2011-06-02 15:37:57.824916999 +0300
1765
@@ -47,9 +47,9 @@
1766
 #ifndef DBUG_OFF
1767
   info->opt_flag= READ_CHECK_USED;		/* Check when changing */
1768
 #endif
1769
-  DBUG_PRINT("exit",("heap: 0x%lx  reclength: %d  records_in_block: %d",
1770
-		     (long) info, share->reclength,
1771
-                     share->block.records_in_block));
1772
+  DBUG_PRINT("exit",("heap: 0x%lx  chunk_length: %d  records_in_block: %d",
1773
+                     (long) info, share->recordspace.chunk_length,
1774
+                     share->recordspace.block.records_in_block));
1775
   DBUG_RETURN(info);
1776
 }
1777
 
1778
diff -ruN /dev/null b/storage/heap/hp_record.c
1779
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
1780
+++ b/storage/heap/hp_record.c	2011-06-02 15:40:21.634916999 +0300
1781
@@ -0,0 +1,498 @@
1782
+/* Copyright (C) 2000-2002 MySQL AB
1783
+   Copyright (C) 2008 eBay, Inc
1784
+
1785
+   This program is free software; you can redistribute it and/or modify
1786
+   it under the terms of the GNU General Public License as published by
1787
+   the Free Software Foundation; version 2 of the License.
1788
+
1789
+   This program is distributed in the hope that it will be useful,
1790
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
1791
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1792
+   GNU General Public License for more details.
1793
+
1794
+   You should have received a copy of the GNU General Public License
1795
+   along with this program; if not, write to the Free Software
1796
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
1797
+
1798
+/*
1799
+  Implements various base record-related functions, such as encode and decode
1800
+  into chunks.
1801
+*/
1802
+
1803
+#include "heapdef.h"
1804
+#include <mysql_com.h>
1805
+
1806
+/**
1807
+  Calculate size of the record for the purpose of storing in chunks
1808
+
1809
+  Walk through the fields of the record and calculates the exact space
1810
+  needed in chunks as well the the total chunk count
1811
+
1812
+  @param       info         the hosting table
1813
+  @param       record       the record in standard unpacked format
1814
+  @param[out]  chunk_count  the number of chunks needed for this record
1815
+
1816
+  @return The size of the required storage in bytes
1817
+*/
1818
+
1819
+uint hp_get_encoded_data_length(HP_SHARE *info, const uchar *record,
1820
+                                uint *chunk_count)
1821
+{
1822
+  uint i, dst_offset;
1823
+
1824
+  dst_offset= info->fixed_data_length;
1825
+
1826
+  if (!info->recordspace.is_variable_size)
1827
+  {
1828
+    /* Nothing more to copy */
1829
+    *chunk_count= 1;
1830
+    return dst_offset;
1831
+  }
1832
+
1833
+  for (i= info->fixed_column_count; i < info->column_count; i++)
1834
+  {
1835
+    uint src_offset, length;
1836
+
1837
+    HP_COLUMNDEF *column= info->column_defs + i;
1838
+
1839
+    if (column->null_bit)
1840
+    {
1841
+      if (record[column->null_pos] & column->null_bit)
1842
+      {
1843
+        /* Skip all NULL values */
1844
+        continue;
1845
+      }
1846
+    }
1847
+
1848
+    src_offset= column->offset;
1849
+    if (column->type == MYSQL_TYPE_VARCHAR)
1850
+    {
1851
+      uint pack_length;
1852
+
1853
+      /* >= 5.0.3 true VARCHAR */
1854
+
1855
+      pack_length= column->length_bytes;
1856
+      length= pack_length + (pack_length == 1 ?
1857
+                             (uint) *(uchar *) (record + src_offset) :
1858
+                             uint2korr(record + src_offset));
1859
+    }
1860
+    else if (column->type == MYSQL_TYPE_BLOB)
1861
+    {
1862
+      uint pack_length= column->length_bytes;
1863
+
1864
+      length= pack_length + hp_calc_blob_length(pack_length,
1865
+                                                record + src_offset);
1866
+    }
1867
+    else
1868
+    {
1869
+      length= column->length;
1870
+    }
1871
+
1872
+    dst_offset+= length;
1873
+  }
1874
+
1875
+  *chunk_count= get_chunk_count(&info->recordspace, dst_offset);
1876
+
1877
+  return dst_offset;
1878
+}
1879
+
1880
+
1881
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
1882
+static void dump_chunk(HP_SHARE *info, const uchar *curr_chunk)
1883
+{
1884
+  uint i;
1885
+  fprintf(stdout, "Chunk dump at 0x%lx: ", (long) curr_chunk);
1886
+  for (i= 0; i < info->recordspace.chunk_dataspace_length; i++)
1887
+  {
1888
+    uint b= *((uchar *)(curr_chunk + i));
1889
+    if (b < 0x10)
1890
+    {
1891
+      fprintf(stdout, "0");
1892
+    }
1893
+    fprintf(stdout, "%lx ", (long) b);
1894
+  }
1895
+  fprintf(stdout, ". Next = 0x%lx, Status = %d\n",
1896
+          (long) (*((uchar **) (curr_chunk + info->recordspace.offset_link))),
1897
+          (uint) (*((uchar *) (curr_chunk + info->recordspace.offset_status))));
1898
+}
1899
+#endif
1900
+
1901
+/**
1902
+  Stores data from packed field into the preallocated chunkset,
1903
+  or performs data comparison
1904
+
1905
+  @param  info         the hosting table
1906
+  @param  data         the field data in packed format
1907
+  @param  length       the field data length
1908
+  @param  pos_ptr      the target chunkset
1909
+  @param  off_ptr      the pointer to the offset within the current chunkset
1910
+  @param  is_compare   flag indicating whether we should compare data or store
1911
+                       it
1912
+
1913
+  @return  Status of comparison
1914
+    @retval  non-zero  if comparison found data differences
1915
+    @retval  zero      otherwise
1916
+*/
1917
+
1918
+static inline uint
1919
+hp_process_field_data_to_chunkset(HP_SHARE *info, const uchar *data,
1920
+                                  uint length, uchar **pos_ptr, uint *off_ptr,
1921
+                                  uint is_compare)
1922
+{
1923
+  uint to_copy;
1924
+  uchar *curr_chunk= *pos_ptr;
1925
+  uint dst_offset= *off_ptr;
1926
+  uint rc= 1;
1927
+
1928
+  while (length > 0)
1929
+  {
1930
+
1931
+    to_copy= info->recordspace.chunk_dataspace_length - dst_offset;
1932
+    if (to_copy == 0)
1933
+    {
1934
+      /* Jump to the next chunk */
1935
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
1936
+      dump_chunk(info, curr_chunk);
1937
+#endif
1938
+      curr_chunk= *((uchar **) (curr_chunk + info->recordspace.offset_link));
1939
+      dst_offset= 0;
1940
+      continue;
1941
+    }
1942
+
1943
+    to_copy= min(length, to_copy);
1944
+
1945
+    if (is_compare)
1946
+    {
1947
+      if (memcmp(curr_chunk + dst_offset, data, (size_t) to_copy))
1948
+      {
1949
+        goto end;
1950
+      }
1951
+    }
1952
+    else
1953
+    {
1954
+      memcpy(curr_chunk + dst_offset, data, (size_t) to_copy);
1955
+    }
1956
+
1957
+    data+= to_copy;
1958
+    dst_offset+= to_copy;
1959
+    length-= to_copy;
1960
+  }
1961
+
1962
+  rc= 0;
1963
+
1964
+end:
1965
+  *pos_ptr= curr_chunk;
1966
+  *off_ptr= dst_offset;
1967
+
1968
+  return rc;
1969
+}
1970
+
1971
+/**
1972
+  Encodes or compares record
1973
+
1974
+  Copies data from original unpacked record into the preallocated chunkset,
1975
+  or performs data comparison
1976
+
1977
+  @param  info         the hosting table
1978
+  @param  record       the record in standard unpacked format
1979
+  @param  pos          the target chunkset
1980
+  @param  is_compare   flag indicating whether we should compare data or store
1981
+                       it
1982
+
1983
+  @return  Status of comparison
1984
+    @retval  non-zero  if comparison fond data differences
1985
+    @retval  zero      otherwise
1986
+*/
1987
+
1988
+uint hp_process_record_data_to_chunkset(HP_SHARE *info, const uchar *record,
1989
+                                        uchar *pos, uint is_compare)
1990
+{
1991
+  uint i, dst_offset;
1992
+  uchar *curr_chunk= pos;
1993
+
1994
+  if (is_compare)
1995
+  {
1996
+    if (memcmp(curr_chunk, record, (size_t) info->fixed_data_length))
1997
+    {
1998
+      return 1;
1999
+    }
2000
+  }
2001
+  else
2002
+  {
2003
+    memcpy(curr_chunk, record, (size_t) info->fixed_data_length);
2004
+  }
2005
+
2006
+  if (!info->recordspace.is_variable_size)
2007
+  {
2008
+    /* Nothing more to copy */
2009
+    return 0;
2010
+  }
2011
+
2012
+  dst_offset= info->fixed_data_length;
2013
+
2014
+  for (i= info->fixed_column_count; i < info->column_count; i++)
2015
+  {
2016
+    uint length;
2017
+    const uchar *data;
2018
+
2019
+    HP_COLUMNDEF *column= info->column_defs + i;
2020
+
2021
+    if (column->null_bit)
2022
+    {
2023
+      if (record[column->null_pos] & column->null_bit)
2024
+      {
2025
+        /* Skip all NULL values */
2026
+        continue;
2027
+      }
2028
+    }
2029
+
2030
+    data= record + column->offset;
2031
+    if (column->type == MYSQL_TYPE_VARCHAR)
2032
+    {
2033
+      uint pack_length;
2034
+
2035
+      /* >= 5.0.3 true VARCHAR */
2036
+
2037
+      /* Make sure to copy length indicator and actuals string bytes */
2038
+      pack_length= column->length_bytes;
2039
+      length= pack_length + (pack_length == 1 ? (uint) *data : uint2korr(data));
2040
+    }
2041
+    else if (column->type == MYSQL_TYPE_BLOB)
2042
+    {
2043
+      uint pack_length;
2044
+
2045
+      pack_length= column->length_bytes;
2046
+      /* Just want to store the length, so not interested in the return code */
2047
+      (void) hp_process_field_data_to_chunkset(info, data, pack_length,
2048
+                                               &curr_chunk, &dst_offset, 0);
2049
+      length= hp_calc_blob_length(pack_length, data);
2050
+      memcpy(&data, data + pack_length, sizeof(char *));
2051
+    }
2052
+    else
2053
+    {
2054
+      length= column->length;
2055
+    }
2056
+
2057
+    if (hp_process_field_data_to_chunkset(info, data, length, &curr_chunk,
2058
+                                          &dst_offset, is_compare))
2059
+    {
2060
+      return 1;
2061
+    }
2062
+  }
2063
+
2064
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2065
+  dump_chunk(info, curr_chunk);
2066
+#endif
2067
+
2068
+  return 0;
2069
+}
2070
+
2071
+
2072
+/**
2073
+  Stores record in the heap table chunks
2074
+
2075
+  Copies data from original unpacked record into the preallocated chunkset
2076
+
2077
+  @param  info         the hosting table
2078
+  @param  record       the record in standard unpacked format
2079
+  @param  pos          the target chunkset
2080
+*/
2081
+
2082
+void hp_copy_record_data_to_chunkset(HP_SHARE *info, const uchar *record,
2083
+                                     uchar *pos)
2084
+{
2085
+  DBUG_ENTER("hp_copy_record_data_to_chunks");
2086
+
2087
+  hp_process_record_data_to_chunkset(info, record, pos, 0);
2088
+
2089
+  DBUG_VOID_RETURN;
2090
+}
2091
+
2092
+
2093
+/*
2094
+  Macro to switch curr_chunk to the next chunk in the chunkset and reset
2095
+  src_offset.
2096
+*/
2097
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2098
+#define SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset)     \
2099
+  {                                                                     \
2100
+    curr_chunk= *((uchar**) (curr_chunk + share->recordspace.offset_link)); \
2101
+    src_offset= 0;                                                      \
2102
+    dump_chunk(share, curr_chunk);                                       \
2103
+  }
2104
+#else
2105
+#define SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset)     \
2106
+  {                                                                     \
2107
+    curr_chunk= *((uchar**) (curr_chunk + share->recordspace.offset_link)); \
2108
+    src_offset= 0;                                                      \
2109
+  }
2110
+#endif
2111
+
2112
+/**
2113
+  Copies record data from storage to unpacked record format
2114
+
2115
+  Copies data from chunkset into its original unpacked record
2116
+
2117
+  @param       info         the hosting table
2118
+  @param[out]  record       the target record in standard unpacked format
2119
+  @param       pos          the source chunkset
2120
+
2121
+  @return                   Status of conversion
2122
+    @retval   0             success
2123
+    @retval   1             out of memory
2124
+*/
2125
+
2126
+int hp_extract_record(HP_INFO *info, uchar *record, const uchar *pos)
2127
+{
2128
+  uint i, src_offset;
2129
+  const uchar *curr_chunk= pos;
2130
+  HP_SHARE *share= info->s;
2131
+  uint *rec_offsets;
2132
+  uint *buf_offsets;
2133
+  uint nblobs= 0;
2134
+  uint init_offset= share->blobs * sizeof(uint) * 2;
2135
+
2136
+  DBUG_ENTER("hp_extract_record");
2137
+
2138
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2139
+  if (share->recordspace.is_variable_size)
2140
+  {
2141
+    dump_chunk(share, curr_chunk);
2142
+  }
2143
+#endif
2144
+
2145
+  memcpy(record, curr_chunk, (size_t) share->fixed_data_length);
2146
+
2147
+  if (!share->recordspace.is_variable_size)
2148
+  {
2149
+    /* Nothing more to copy */
2150
+    DBUG_RETURN(0);
2151
+  }
2152
+
2153
+  /* Reserve space for rec_offsets and buf_offsets.*/
2154
+  info->blob_offset= init_offset;
2155
+  src_offset= share->fixed_data_length;
2156
+
2157
+  for (i= share->fixed_column_count; i < share->column_count; i++)
2158
+  {
2159
+    uint length, is_null= 0;
2160
+    uchar *to;
2161
+
2162
+    HP_COLUMNDEF *column= share->column_defs + i;
2163
+
2164
+    if (column->null_bit)
2165
+    {
2166
+      if (record[column->null_pos] & column->null_bit)
2167
+      {
2168
+        is_null= 1;
2169
+      }
2170
+    }
2171
+
2172
+    if (is_null)
2173
+    {
2174
+      /* TODO: is memset really needed? */
2175
+      memset(record + column->offset, 0, column->length);
2176
+      continue;
2177
+    }
2178
+
2179
+    to= record + column->offset;
2180
+    if (column->type == MYSQL_TYPE_VARCHAR || column->type == MYSQL_TYPE_BLOB)
2181
+    {
2182
+      uint pack_length, i;
2183
+      uchar *tmp= to;
2184
+
2185
+      pack_length= column->length_bytes;
2186
+
2187
+      for (i= 0; i < pack_length; i++)
2188
+      {
2189
+        if (src_offset == share->recordspace.chunk_dataspace_length)
2190
+        {
2191
+          SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset);
2192
+        }
2193
+        *to++= curr_chunk[src_offset++];
2194
+      }
2195
+      /*
2196
+        We copy byte-by-byte and then use hp_calc_blob_length to combine bytes
2197
+        in the right order.
2198
+      */
2199
+      length= hp_calc_blob_length(pack_length, tmp);
2200
+
2201
+      if (column->type == MYSQL_TYPE_BLOB && length == 0)
2202
+      {
2203
+        /*
2204
+          Store a zero pointer for zero-length BLOBs because the server
2205
+          relies on that (see Field_blob::val_*().
2206
+        */
2207
+        *(uchar **) to= 0;
2208
+      }
2209
+      else if (column->type == MYSQL_TYPE_BLOB && length > 0)
2210
+      {
2211
+        uint newsize= info->blob_offset + length;
2212
+
2213
+        DBUG_ASSERT(share->blobs > 0);
2214
+        /*
2215
+          Make sure we have enough space in blob_buffer and store the pointer
2216
+          to this blob in record.
2217
+        */
2218
+        if (info->blob_size < newsize)
2219
+        {
2220
+          uchar *ptr;
2221
+          ptr= my_realloc(info->blob_buffer, newsize, MYF(MY_ALLOW_ZERO_PTR));
2222
+          if (ptr == NULL)
2223
+          {
2224
+            DBUG_RETURN(1);
2225
+          }
2226
+
2227
+          if (info->blob_buffer == NULL)
2228
+          {
2229
+            memset(ptr, 0, init_offset);
2230
+          }
2231
+          info->blob_buffer= ptr;
2232
+          info->blob_size= newsize;
2233
+        }
2234
+
2235
+        rec_offsets= (uint *) info->blob_buffer;
2236
+        buf_offsets= rec_offsets + share->blobs;
2237
+
2238
+        rec_offsets[nblobs]= (uint) (to - record);
2239
+        buf_offsets[nblobs]= info->blob_offset;
2240
+        nblobs++;
2241
+
2242
+        /* Change 'to' so blob data is copied into blob_buffer */
2243
+        to= info->blob_buffer + info->blob_offset;
2244
+        info->blob_offset= newsize;
2245
+      }
2246
+    }
2247
+    else
2248
+    {
2249
+      length= column->length;
2250
+    }
2251
+
2252
+    while (length > 0)
2253
+    {
2254
+      uint to_copy;
2255
+
2256
+      to_copy= share->recordspace.chunk_dataspace_length - src_offset;
2257
+      if (to_copy == 0)
2258
+      {
2259
+        SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset);
2260
+        to_copy= share->recordspace.chunk_dataspace_length;
2261
+      }
2262
+
2263
+      to_copy= min(length, to_copy);
2264
+
2265
+      memcpy(to, curr_chunk + src_offset, (size_t) to_copy);
2266
+      src_offset+= to_copy;
2267
+      to+= to_copy;
2268
+      length-= to_copy;
2269
+    }
2270
+  }
2271
+
2272
+  /* Store pointers to blob data in record */
2273
+  for (i= 0; i < nblobs; i++)
2274
+  {
2275
+    *(uchar **) (record + rec_offsets[i]) = info->blob_buffer + buf_offsets[i];
2276
+  }
2277
+
2278
+  DBUG_RETURN(0);
2279
+}
2280
diff -ruN a/storage/heap/hp_rfirst.c b/storage/heap/hp_rfirst.c
2281
--- a/storage/heap/hp_rfirst.c	2011-04-11 13:44:03.000000000 +0300
2282
+++ b/storage/heap/hp_rfirst.c	2011-06-02 15:38:02.764917001 +0300
2283
@@ -34,7 +34,10 @@
2284
       memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos), 
2285
 	     sizeof(uchar*));
2286
       info->current_ptr = pos;
2287
-      memcpy(record, pos, (size_t)share->reclength);
2288
+      if (hp_extract_record(info, record, pos))
2289
+      {
2290
+        DBUG_RETURN(my_errno);
2291
+      }
2292
       /*
2293
         If we're performing index_first on a table that was taken from
2294
         table cache, info->lastkey_len is initialized to previous query.
2295
diff -ruN a/storage/heap/hp_rkey.c b/storage/heap/hp_rkey.c
2296
--- a/storage/heap/hp_rkey.c	2011-04-11 13:44:03.000000000 +0300
2297
+++ b/storage/heap/hp_rkey.c	2011-06-02 15:38:02.764917001 +0300
2298
@@ -66,7 +66,10 @@
2299
     if (!(keyinfo->flag & HA_NOSAME))
2300
       memcpy(info->lastkey, key, (size_t) keyinfo->length);
2301
   }
2302
-  memcpy(record, pos, (size_t) share->reclength);
2303
+  if (hp_extract_record(info, record, pos))
2304
+  {
2305
+    DBUG_RETURN(my_errno);
2306
+  }
2307
   info->update= HA_STATE_AKTIV;
2308
   DBUG_RETURN(0);
2309
 }
2310
diff -ruN a/storage/heap/hp_rlast.c b/storage/heap/hp_rlast.c
2311
--- a/storage/heap/hp_rlast.c	2011-04-11 13:44:03.000000000 +0300
2312
+++ b/storage/heap/hp_rlast.c	2011-06-02 15:38:02.764917001 +0300
2313
@@ -35,7 +35,10 @@
2314
       memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos), 
2315
 	     sizeof(uchar*));
2316
       info->current_ptr = pos;
2317
-      memcpy(record, pos, (size_t)share->reclength);
2318
+      if (hp_extract_record(info, record, pos))
2319
+      {
2320
+        DBUG_RETURN(my_errno);
2321
+      }
2322
       info->update = HA_STATE_AKTIV;
2323
     }
2324
     else
2325
diff -ruN a/storage/heap/hp_rnext.c b/storage/heap/hp_rnext.c
2326
--- a/storage/heap/hp_rnext.c	2011-04-11 13:44:03.000000000 +0300
2327
+++ b/storage/heap/hp_rnext.c	2011-06-02 15:38:02.764917001 +0300
2328
@@ -109,7 +109,10 @@
2329
       my_errno=HA_ERR_END_OF_FILE;
2330
     DBUG_RETURN(my_errno);
2331
   }
2332
-  memcpy(record,pos,(size_t) share->reclength);
2333
+  if (hp_extract_record(info, record, pos))
2334
+  {
2335
+    DBUG_RETURN(my_errno);
2336
+  }
2337
   info->update=HA_STATE_AKTIV | HA_STATE_NEXT_FOUND;
2338
   DBUG_RETURN(0);
2339
 }
2340
diff -ruN a/storage/heap/hp_rprev.c b/storage/heap/hp_rprev.c
2341
--- a/storage/heap/hp_rprev.c	2011-04-11 13:44:03.000000000 +0300
2342
+++ b/storage/heap/hp_rprev.c	2011-06-02 15:38:02.764917001 +0300
2343
@@ -77,7 +77,10 @@
2344
       my_errno=HA_ERR_END_OF_FILE;
2345
     DBUG_RETURN(my_errno);
2346
   }
2347
-  memcpy(record,pos,(size_t) share->reclength);
2348
+  if (hp_extract_record(info, record, pos))
2349
+  {
2350
+    DBUG_RETURN(my_errno);
2351
+  }
2352
   info->update=HA_STATE_AKTIV | HA_STATE_PREV_FOUND;
2353
   DBUG_RETURN(0);
2354
 }
2355
diff -ruN a/storage/heap/hp_rrnd.c b/storage/heap/hp_rrnd.c
2356
--- a/storage/heap/hp_rrnd.c	2011-04-11 13:44:03.000000000 +0300
2357
+++ b/storage/heap/hp_rrnd.c	2011-06-02 15:38:02.764917001 +0300
2358
@@ -36,13 +36,18 @@
2359
     info->update= 0;
2360
     DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2361
   }
2362
-  if (!info->current_ptr[share->reclength])
2363
+  if (get_chunk_status(&share->recordspace, info->current_ptr) !=
2364
+      CHUNK_STATUS_ACTIVE)
2365
   {
2366
+    /* treat deleted and linked chunks as deleted */
2367
     info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
2368
     DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
2369
   }
2370
   info->update=HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND | HA_STATE_AKTIV;
2371
-  memcpy(record,info->current_ptr,(size_t) share->reclength);
2372
+  if (hp_extract_record(info, record, info->current_ptr))
2373
+  {
2374
+    DBUG_RETURN(my_errno);
2375
+  }
2376
   DBUG_PRINT("exit", ("found record at 0x%lx", (long) info->current_ptr));
2377
   info->current_hash_ptr=0;			/* Can't use rnext */
2378
   DBUG_RETURN(0);
2379
@@ -70,17 +75,17 @@
2380
   {
2381
     pos= ++info->current_record;
2382
     if (pos % share->block.records_in_block &&	/* Quick next record */
2383
-	pos < share->records+share->deleted &&
2384
-	(info->update & HA_STATE_PREV_FOUND))
2385
+        pos < share->used_chunk_count + share->deleted_chunk_count &&
2386
+        (info->update & HA_STATE_PREV_FOUND))
2387
     {
2388
-      info->current_ptr+=share->block.recbuffer;
2389
+      info->current_ptr+= share->block.recbufferlen;
2390
       goto end;
2391
     }
2392
   }
2393
   else
2394
     info->current_record=pos;
2395
 
2396
-  if (pos >= share->records+share->deleted)
2397
+  if (pos >= share->used_chunk_count + share->deleted_chunk_count)
2398
   {
2399
     info->update= 0;
2400
     DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2401
@@ -90,13 +95,17 @@
2402
   hp_find_record(info, pos);
2403
 
2404
 end:
2405
-  if (!info->current_ptr[share->reclength])
2406
+  if (GET_CHUNK_STATUS(info, info->current_ptr) != CHUNK_STATUS_ACTIVE)
2407
   {
2408
+    /* treat deleted and linked chunks as deleted */
2409
     info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
2410
     DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
2411
   }
2412
   info->update=HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND | HA_STATE_AKTIV;
2413
-  memcpy(record,info->current_ptr,(size_t) share->reclength);
2414
+  if (hp_extract_record(info, record, info->current_ptr))
2415
+  {
2416
+    DBUG_RETURN(my_errno);
2417
+  }
2418
   DBUG_PRINT("exit",("found record at 0x%lx",info->current_ptr));
2419
   info->current_hash_ptr=0;			/* Can't use rnext */
2420
   DBUG_RETURN(0);
2421
diff -ruN a/storage/heap/hp_rsame.c b/storage/heap/hp_rsame.c
2422
--- a/storage/heap/hp_rsame.c	2011-04-11 13:44:03.000000000 +0300
2423
+++ b/storage/heap/hp_rsame.c	2011-06-02 15:38:02.764917001 +0300
2424
@@ -31,7 +31,8 @@
2425
   DBUG_ENTER("heap_rsame");
2426
 
2427
   test_active(info);
2428
-  if (info->current_ptr[share->reclength])
2429
+  if (get_chunk_status(&share->recordspace, info->current_ptr) ==
2430
+      CHUNK_STATUS_ACTIVE)
2431
   {
2432
     if (inx < -1 || inx >= (int) share->keys)
2433
     {
2434
@@ -47,9 +48,15 @@
2435
 	DBUG_RETURN(my_errno);
2436
       }
2437
     }
2438
-    memcpy(record,info->current_ptr,(size_t) share->reclength);
2439
+    if (hp_extract_record(info, record, info->current_ptr))
2440
+    {
2441
+      DBUG_RETURN(my_errno);
2442
+    }
2443
     DBUG_RETURN(0);
2444
   }
2445
+
2446
+  /* treat deleted and linked chunks as deleted */
2447
+
2448
   info->update=0;
2449
 
2450
   DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
2451
diff -ruN a/storage/heap/hp_scan.c b/storage/heap/hp_scan.c
2452
--- a/storage/heap/hp_scan.c	2011-04-11 13:44:03.000000000 +0300
2453
+++ b/storage/heap/hp_scan.c	2011-06-02 15:38:02.774917002 +0300
2454
@@ -30,7 +30,6 @@
2455
   info->lastinx= -1;
2456
   info->current_record= (ulong) ~0L;		/* No current record */
2457
   info->update=0;
2458
-  info->next_block=0;
2459
   DBUG_RETURN(0);
2460
 }
2461
 
2462
@@ -41,32 +40,26 @@
2463
   DBUG_ENTER("heap_scan");
2464
 
2465
   pos= ++info->current_record;
2466
-  if (pos < info->next_block)
2467
+  if (pos >= share->recordspace.chunk_count)
2468
   {
2469
-    info->current_ptr+=share->block.recbuffer;
2470
+    info->update= 0;
2471
+    DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2472
   }
2473
-  else
2474
-  {
2475
-    info->next_block+=share->block.records_in_block;
2476
-    if (info->next_block >= share->records+share->deleted)
2477
-    {
2478
-      info->next_block= share->records+share->deleted;
2479
-      if (pos >= info->next_block)
2480
-      {
2481
-	info->update= 0;
2482
-	DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2483
-      }
2484
-    }
2485
-    hp_find_record(info, pos);
2486
-  }
2487
-  if (!info->current_ptr[share->reclength])
2488
+
2489
+  hp_find_record(info, pos);
2490
+
2491
+  if (get_chunk_status(&share->recordspace, info->current_ptr) !=
2492
+      CHUNK_STATUS_ACTIVE)
2493
   {
2494
-    DBUG_PRINT("warning",("Found deleted record"));
2495
+    DBUG_PRINT("warning",("Found deleted record or secondary chunk"));
2496
     info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
2497
     DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
2498
   }
2499
   info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND | HA_STATE_AKTIV;
2500
-  memcpy(record,info->current_ptr,(size_t) share->reclength);
2501
+  if (hp_extract_record(info, record, info->current_ptr))
2502
+  {
2503
+    DBUG_RETURN(my_errno);
2504
+  }
2505
   info->current_hash_ptr=0;			/* Can't use read_next */
2506
   DBUG_RETURN(0);
2507
 } /* heap_scan */
2508
diff -ruN a/storage/heap/hp_test1.c b/storage/heap/hp_test1.c
2509
--- a/storage/heap/hp_test1.c	2011-04-11 13:44:03.000000000 +0300
2510
+++ b/storage/heap/hp_test1.c	2011-06-02 15:38:02.774917002 +0300
2511
@@ -22,6 +22,7 @@
2512
 #include <my_global.h>
2513
 #include <my_sys.h>
2514
 #include <m_string.h>
2515
+#include <mysql_com.h>
2516
 #include "heap.h"
2517
 
2518
 static int get_options(int argc, char *argv[]);
2519
@@ -35,6 +36,7 @@
2520
   uchar record[128],key[32];
2521
   const char *filename;
2522
   HP_KEYDEF keyinfo[10];
2523
+  HP_COLUMNDEF columndef[2];
2524
   HA_KEYSEG keyseg[4];
2525
   HP_CREATE_INFO hp_create_info;
2526
   HP_SHARE *tmp_share;
2527
@@ -51,6 +53,10 @@
2528
   hp_create_info.reclength= 30;
2529
   hp_create_info.max_records= (ulong) flag*100000L;
2530
   hp_create_info.min_records= 10UL;
2531
+  hp_create_info.columns= 2;
2532
+  hp_create_info.columndef= columndef;
2533
+  hp_create_info.fixed_key_fieldnr= 30;
2534
+  hp_create_info.fixed_data_size= sizeof(char*) * 2;
2535
 
2536
   keyinfo[0].keysegs=1;
2537
   keyinfo[0].seg=keyseg;
2538
@@ -62,11 +68,20 @@
2539
   keyinfo[0].seg[0].null_bit= 0;
2540
   keyinfo[0].flag = HA_NOSAME;
2541
 
2542
+  memset(columndef, 0, 2 * sizeof(HP_COLUMNDEF));
2543
+  columndef[0].type= MYSQL_TYPE_STRING;
2544
+  columndef[0].offset= 1;
2545
+  columndef[0].length= 6;
2546
+  columndef[1].type= MYSQL_TYPE_STRING;
2547
+  columndef[1].offset= 7;
2548
+  columndef[1].length= 23;
2549
+
2550
   deleted=0;
2551
   bzero((uchar*) flags,sizeof(flags));
2552
 
2553
   printf("- Creating heap-file\n");
2554
-  if (heap_create(filename, &hp_create_info, &tmp_share, &unused) ||
2555
+  if (heap_create(filename, &hp_create_info,
2556
+                  &tmp_share, &unused) ||
2557
       !(file= heap_open(filename, 2)))
2558
     goto err;
2559
   printf("- Writing records:s\n");
2560
diff -ruN a/storage/heap/hp_test2.c b/storage/heap/hp_test2.c
2561
--- a/storage/heap/hp_test2.c	2011-04-11 13:44:03.000000000 +0300
2562
+++ b/storage/heap/hp_test2.c	2011-06-02 15:38:02.774917002 +0300
2563
@@ -18,6 +18,7 @@
2564
 
2565
 #include "heapdef.h"		/* Because of hp_find_block */
2566
 #include <signal.h>
2567
+#include <mysql_com.h>
2568
 
2569
 #define MAX_RECORDS 100000
2570
 #define MAX_KEYS 4
2571
@@ -44,6 +45,7 @@
2572
   register uint i,j;
2573
   uint ant,n1,n2,n3;
2574
   uint write_count,update,opt_delete,check2,dupp_keys,found_key;
2575
+  uint mem_per_keys;
2576
   int error;
2577
   ulong pos;
2578
   unsigned long key_check;
2579
@@ -53,6 +55,7 @@
2580
   HP_SHARE *tmp_share;
2581
   HP_KEYDEF keyinfo[MAX_KEYS];
2582
   HA_KEYSEG keyseg[MAX_KEYS*5];
2583
+  HP_COLUMNDEF columndef[4];
2584
   HEAP_PTR UNINIT_VAR(position);
2585
   HP_CREATE_INFO hp_create_info;
2586
   CHARSET_INFO *cs= &my_charset_latin1;
2587
@@ -65,12 +68,16 @@
2588
   get_options(argc,argv);
2589
 
2590
   bzero(&hp_create_info, sizeof(hp_create_info));
2591
-  hp_create_info.max_table_size= 1024L*1024L;
2592
+  hp_create_info.max_table_size= 1024L*1024L*1024L;
2593
   hp_create_info.keys= keys;
2594
   hp_create_info.keydef= keyinfo;
2595
   hp_create_info.reclength= reclength;
2596
   hp_create_info.max_records= (ulong) flag*100000L;
2597
   hp_create_info.min_records= (ulong) recant/2;
2598
+  hp_create_info.columns= 4;
2599
+  hp_create_info.columndef= columndef;
2600
+  hp_create_info.fixed_key_fieldnr= 4;
2601
+  hp_create_info.fixed_data_size= 39;
2602
 
2603
   write_count=update=opt_delete=0;
2604
   key_check=0;
2605
@@ -118,11 +125,30 @@
2606
   keyinfo[3].seg[0].null_pos=38;
2607
   keyinfo[3].seg[0].charset=cs;
2608
 
2609
+  memset(columndef, 0, 4 * sizeof(HP_COLUMNDEF));
2610
+  columndef[0].type= MYSQL_TYPE_STRING;
2611
+  columndef[0].offset= 0;
2612
+  columndef[0].length= 6;
2613
+  columndef[1].type= MYSQL_TYPE_STRING;
2614
+  columndef[1].offset= 7;
2615
+  columndef[1].length= 6;
2616
+  columndef[2].type= MYSQL_TYPE_STRING;
2617
+  columndef[2].offset= 12;
2618
+  columndef[2].length= 8;
2619
+  columndef[3].type= MYSQL_TYPE_TINY;
2620
+  columndef[3].offset= 37;
2621
+  columndef[3].length= 1;
2622
+  columndef[3].null_bit= 1;
2623
+  columndef[3].null_pos= 38;
2624
+
2625
+  mem_per_keys= (sizeof(char*) * 2) * 4;
2626
+
2627
   bzero((char*) key1,sizeof(key1));
2628
   bzero((char*) key3,sizeof(key3));
2629
 
2630
   printf("- Creating heap-file\n");
2631
-  if (heap_create(filename, &hp_create_info, &tmp_share, &unused) ||
2632
+  if (heap_create(filename, &hp_create_info,
2633
+                  &tmp_share, &unused) ||
2634
       !(file= heap_open(filename, 2)))
2635
     goto err;
2636
   signal(SIGINT,endprog);
2637
diff -ruN a/storage/heap/hp_write.c b/storage/heap/hp_write.c
2638
--- a/storage/heap/hp_write.c	2011-04-11 13:44:03.000000000 +0300
2639
+++ b/storage/heap/hp_write.c	2011-06-02 15:38:02.774917002 +0300
2640
@@ -25,7 +25,6 @@
2641
 #define HIGHFIND 4
2642
 #define HIGHUSED 8
2643
 
2644
-static uchar *next_free_record_pos(HP_SHARE *info);
2645
 static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
2646
 				     ulong records);
2647
 
2648
@@ -34,6 +33,8 @@
2649
   HP_KEYDEF *keydef, *end;
2650
   uchar *pos;
2651
   HP_SHARE *share=info->s;
2652
+  uint rec_length, chunk_count;
2653
+
2654
   DBUG_ENTER("heap_write");
2655
 #ifndef DBUG_OFF
2656
   if (info->mode & O_RDONLY)
2657
@@ -41,7 +42,18 @@
2658
     DBUG_RETURN(my_errno=EACCES);
2659
   }
2660
 #endif
2661
-  if (!(pos=next_free_record_pos(share)))
2662
+
2663
+  if ((share->records >= share->max_records && share->max_records) ||
2664
+      (share->recordspace.total_data_length + share->index_length >=
2665
+       share->max_table_size))
2666
+  {
2667
+    my_errno= HA_ERR_RECORD_FILE_FULL;
2668
+    DBUG_RETURN(my_errno);
2669
+  }
2670
+
2671
+  rec_length= hp_get_encoded_data_length(share, record, &chunk_count);
2672
+
2673
+  if (!(pos= hp_allocate_chunkset(&share->recordspace, chunk_count)))
2674
     DBUG_RETURN(my_errno);
2675
   share->changed=1;
2676
 
2677
@@ -52,8 +64,8 @@
2678
       goto err;
2679
   }
2680
 
2681
-  memcpy(pos,record,(size_t) share->reclength);
2682
-  pos[share->reclength]=1;		/* Mark record as not deleted */
2683
+  hp_copy_record_data_to_chunkset(share, record, pos);
2684
+
2685
   if (++share->records == share->blength)
2686
     share->blength+= share->blength;
2687
   info->current_ptr=pos;
2688
@@ -87,10 +99,7 @@
2689
     keydef--;
2690
   } 
2691
 
2692
-  share->deleted++;
2693
-  *((uchar**) pos)=share->del_link;
2694
-  share->del_link=pos;
2695
-  pos[share->reclength]=0;			/* Record deleted */
2696
+  hp_free_chunks(&share->recordspace, pos);
2697
 
2698
   DBUG_RETURN(my_errno);
2699
 } /* heap_write */
2700
@@ -106,7 +115,8 @@
2701
   uint old_allocated;
2702
 
2703
   custom_arg.keyseg= keyinfo->seg;
2704
-  custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
2705
+  custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos,
2706
+                                        FALSE);
2707
   if (keyinfo->flag & HA_NOSAME)
2708
   {
2709
     custom_arg.search_flag= SEARCH_FIND | SEARCH_UPDATE;
2710
@@ -128,42 +138,6 @@
2711
   return 0;
2712
 }
2713
 
2714
-	/* Find where to place new record */
2715
-
2716
-static uchar *next_free_record_pos(HP_SHARE *info)
2717
-{
2718
-  int block_pos;
2719
-  uchar *pos;
2720
-  size_t length;
2721
-  DBUG_ENTER("next_free_record_pos");
2722
-
2723
-  if (info->del_link)
2724
-  {
2725
-    pos=info->del_link;
2726
-    info->del_link= *((uchar**) pos);
2727
-    info->deleted--;
2728
-    DBUG_PRINT("exit",("Used old position: 0x%lx",(long) pos));
2729
-    DBUG_RETURN(pos);
2730
-  }
2731
-  if (!(block_pos=(info->records % info->block.records_in_block)))
2732
-  {
2733
-    if ((info->records > info->max_records && info->max_records) ||
2734
-        (info->data_length + info->index_length >= info->max_table_size))
2735
-    {
2736
-      my_errno=HA_ERR_RECORD_FILE_FULL;
2737
-      DBUG_RETURN(NULL);
2738
-    }
2739
-    if (hp_get_new_block(&info->block,&length))
2740
-      DBUG_RETURN(NULL);
2741
-    info->data_length+=length;
2742
-  }
2743
-  DBUG_PRINT("exit",("Used new position: 0x%lx",
2744
-		     (long) ((uchar*) info->block.level_info[0].last_blocks+
2745
-                             block_pos * info->block.recbuffer)));
2746
-  DBUG_RETURN((uchar*) info->block.level_info[0].last_blocks+
2747
-	      block_pos*info->block.recbuffer);
2748
-}
2749
-
2750
 
2751
 /*
2752
   Write a hash-key to the hash-index
2753
diff -ruN a/storage/heap/hp_update.c b/storage/heap/hp_update.c
2754
--- a/storage/heap/hp_update.c	2011-04-11 13:44:03.000000000 +0300
2755
+++ b/storage/heap/hp_update.c	2011-05-26 09:07:47.605561000 +0300
2756
@@ -17,43 +17,66 @@
2757
 
2758
 #include "heapdef.h"
2759
 
2760
-int heap_update(HP_INFO *info, const uchar *old, const uchar *heap_new)
2761
+int heap_update(HP_INFO *info, const uchar *old_record, const uchar *new_record)
2762
 {
2763
   HP_KEYDEF *keydef, *end, *p_lastinx;
2764
   uchar *pos;
2765
   my_bool auto_key_changed= 0;
2766
   HP_SHARE *share= info->s;
2767
+  uint old_length, new_length;
2768
+  uint old_chunk_count, new_chunk_count;
2769
+
2770
   DBUG_ENTER("heap_update");
2771
 
2772
   test_active(info);
2773
   pos=info->current_ptr;
2774
 
2775
-  if (info->opt_flag & READ_CHECK_USED && hp_rectest(info,old))
2776
+  if (info->opt_flag & READ_CHECK_USED && hp_rectest(info, old_record))
2777
     DBUG_RETURN(my_errno);				/* Record changed */
2778
+
2779
+  old_length = hp_get_encoded_data_length(share, old_record, &old_chunk_count);
2780
+  new_length = hp_get_encoded_data_length(share, new_record, &new_chunk_count);
2781
+
2782
+  if (new_chunk_count > old_chunk_count)
2783
+  {
2784
+    /* extend the old chunkset size as necessary, but do not shrink yet */
2785
+    if (hp_reallocate_chunkset(&share->recordspace, new_chunk_count, pos))
2786
+    {
2787
+      DBUG_RETURN(my_errno); /* Out of memory or table space */
2788
+    }
2789
+  }
2790
+
2791
   if (--(share->records) < share->blength >> 1) share->blength>>= 1;
2792
   share->changed=1;
2793
 
2794
   p_lastinx= share->keydef + info->lastinx;
2795
   for (keydef= share->keydef, end= keydef + share->keys; keydef < end; keydef++)
2796
   {
2797
-    if (hp_rec_key_cmp(keydef, old, heap_new, 0))
2798
+    if (hp_rec_key_cmp(keydef, old_record, new_record, 0))
2799
     {
2800
-      if ((*keydef->delete_key)(info, keydef, old, pos, keydef == p_lastinx) ||
2801
-          (*keydef->write_key)(info, keydef, heap_new, pos))
2802
+      if ((*keydef->delete_key)(info, keydef, old_record, pos,
2803
+                                keydef == p_lastinx) ||
2804
+          (*keydef->write_key)(info, keydef, new_record, pos))
2805
         goto err;
2806
       if (share->auto_key == (uint) (keydef - share->keydef + 1))
2807
         auto_key_changed= 1;
2808
     }
2809
   }
2810
 
2811
-  memcpy(pos,heap_new,(size_t) share->reclength);
2812
+  hp_copy_record_data_to_chunkset(share, new_record, pos);
2813
   if (++(share->records) == share->blength) share->blength+= share->blength;
2814
 
2815
+  if (new_chunk_count < old_chunk_count)
2816
+  {
2817
+    /* Shrink the chunkset to its new size */
2818
+    hp_reallocate_chunkset(&share->recordspace, new_chunk_count, pos);
2819
+  }
2820
+
2821
 #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2822
   DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
2823
 #endif
2824
   if (auto_key_changed)
2825
-    heap_update_auto_increment(info, heap_new);
2826
+    heap_update_auto_increment(info, new_record);
2827
   DBUG_RETURN(0);
2828
 
2829
  err:
2830
@@ -63,7 +86,7 @@
2831
     if (keydef->algorithm == HA_KEY_ALG_BTREE)
2832
     {
2833
       /* we don't need to delete non-inserted key from rb-tree */
2834
-      if ((*keydef->write_key)(info, keydef, old, pos))
2835
+      if ((*keydef->write_key)(info, keydef, old_record, pos))
2836
       {
2837
         if (++(share->records) == share->blength)
2838
 	  share->blength+= share->blength;
2839
@@ -73,10 +96,10 @@
2840
     }
2841
     while (keydef >= share->keydef)
2842
     {
2843
-      if (hp_rec_key_cmp(keydef, old, heap_new, 0))
2844
+      if (hp_rec_key_cmp(keydef, old_record, new_record, 0))
2845
       {
2846
-	if ((*keydef->delete_key)(info, keydef, heap_new, pos, 0) ||
2847
-	    (*keydef->write_key)(info, keydef, old, pos))
2848
+        if ((*keydef->delete_key)(info, keydef, new_record, pos, 0) ||
2849
+            (*keydef->write_key)(info, keydef, old_record, pos))
2850
 	  break;
2851
       }
2852
       keydef--;
2853
@@ -84,5 +107,12 @@
2854
   }
2855
   if (++(share->records) == share->blength)
2856
     share->blength+= share->blength;
2857
+
2858
+  if (new_chunk_count > old_chunk_count)
2859
+  {
2860
+    /* Shrink the chunkset to its original size */
2861
+    hp_reallocate_chunkset(&share->recordspace, old_chunk_count, pos);
2862
+  }
2863
+
2864
   DBUG_RETURN(my_errno);
2865
 } /* heap_update */