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