4 #include "my_compare.h"
7 - /* defines used by heap-funktions */
8 +/* Define index limits to be identical to MyISAM ones for compatibility. */
10 +#if MAX_INDEXES > HA_MAX_POSSIBLE_KEY
11 +#define HP_MAX_KEY HA_MAX_POSSIBLE_KEY /* Max allowed keys */
13 +#define HP_MAX_KEY MAX_INDEXES /* Max allowed keys */
16 +#define HP_MAX_KEY_LENGTH 1000 /* Max length in bytes */
18 +/* defines used by heap-funktions */
20 #define HP_MAX_LEVELS 4 /* 128^5 records is enough */
21 #define HP_PTRS_IN_NOD 128
23 uint (*get_key_length)(struct st_hp_keydef *keydef, const uchar *key);
26 -typedef struct st_heap_share
27 +typedef struct st_heap_columndef /* column information */
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 */
37 +typedef struct st_heap_dataspace /* control data for data space */
40 + /* Total chunks ever allocated in this dataspace */
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 */
49 + /* Offset of the linking pointer relative to the chunk start */
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;
57 +typedef struct st_heap_share
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 */
74 + Length of record's fixed part, which contains keys and always fits into the
77 + uint fixed_data_length;
78 + uint fixed_column_count; /* Number of columns stored in fixed_data_length */
80 uint keys,max_key_length;
82 uint currently_disabled_keys; /* saved value from "keys" when disabled */
84 - uchar *del_link; /* Link to next block with del. rec */
85 char * name; /* Name of "memory-file" */
87 mysql_mutex_t intern_lock; /* Locking for use with _locking */
90 uint auto_key_type; /* real type of the auto key segment */
91 ulonglong auto_increment;
92 + uint blobs; /* Number of blobs in table */
95 struct st_hp_hash_info;
99 struct st_hp_hash_info *current_hash_ptr;
100 - ulong current_record,next_block;
101 + ulong current_record;
103 int mode; /* Mode of file (READONLY..) */
104 uint opt_flag,update;
106 my_bool implicit_emptied;
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 */
116 open_count to 1. Is only looked at if not internal_table.
120 + HP_COLUMNDEF *columndef;
121 + uint fixed_key_fieldnr;
122 + uint fixed_data_size;
123 + uint keys_memory_size;
124 + uint max_chunk_size;
129 /* Prototypes for heap-functions */
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
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;
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 '',
160 ) ENGINE=heap DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
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 @@
168 Note 1051 Unknown table 't1'
169 CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=heap DEFAULT CHARSET=utf8mb4;
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');
175 --- a/mysql-test/t/create.test
176 +++ b/mysql-test/t/create.test
178 drop table if exists t1,t2;
180 create table t1 (b char(0) not null, index(b));
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;
186 --- a/storage/heap/CMakeLists.txt
187 +++ b/storage/heap/CMakeLists.txt
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)
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
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");
209 if (pos < next_block)
211 - info->current_ptr+= share->block.recbuffer;
212 + info->current_ptr+= share->recordspace.block.recbuffer;
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)
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 */
229 hp_find_record(info,pos);
231 - if (!info->current_ptr[share->reclength])
232 + switch (get_chunk_status(&share->recordspace, info->current_ptr)) {
233 + case CHUNK_STATUS_DELETED:
238 + case CHUNK_STATUS_ACTIVE:
242 + case CHUNK_STATUS_LINKED:
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)));
256 - if (records != share->records || deleted != share->deleted)
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) */
263 + if (records != share->records ||
264 + chunk_count != share->recordspace.chunk_count ||
265 + deleted != share->recordspace.del_chunk_count)
267 + DBUG_PRINT("error",
268 + ("Found rows: %lu (%lu) total chunks %lu (%lu) deleted chunks "
270 + records, (ulong) share->records,
271 + chunk_count, (ulong) share->recordspace.chunk_count,
272 + deleted, (ulong) share->recordspace.del_chunk_count));
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))
285 --- a/storage/heap/_rectest.c
286 +++ b/storage/heap/_rectest.c
289 DBUG_ENTER("hp_rectest");
291 - if (memcmp(info->current_ptr,old,(size_t) info->s->reclength))
292 + if (hp_process_record_data_to_chunkset(info->s, old,
296 DBUG_RETURN((my_errno=HA_ERR_RECORD_CHANGED)); /* Record have changed */
298 --- a/storage/heap/ha_heap.cc
299 +++ b/storage/heap/ha_heap.cc
302 rc= heap_create(name, &create_info, &internal_share, &created_new_share);
303 my_free(create_info.keydef);
304 + my_free(create_info.columndef);
310 if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE)
311 btree_keys.set_bit(i);
313 + Reset per-key block size specification so they are not shown
314 + in SHOW CREATE TABLE.
316 + table->key_info[i].block_size= 0;
317 + table->key_info[i].flags&= ~HA_USES_BLOCK_SIZE;
325 +enum row_type ha_heap::get_row_type() const
327 + if (file->s->recordspace.is_variable_size)
328 + return ROW_TYPE_DYNAMIC;
330 + return ROW_TYPE_FIXED;
333 int ha_heap::extra(enum ha_extra_function operation)
335 @@ -645,23 +659,70 @@
336 heap_prepare_hp_create_info(TABLE *table_arg, bool internal_table,
337 HP_CREATE_INFO *hp_create_info)
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;
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;
348 TABLE_SHARE *share= table_arg->s;
349 bool found_real_auto_increment= 0;
352 bzero(hp_create_info, sizeof(*hp_create_info));
354 + if (!(columndef= (HP_COLUMNDEF*) my_malloc(column_count *
355 + sizeof(HP_COLUMNDEF),
359 + for (column_idx= 0; column_idx < column_count; column_idx++)
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]);
367 + if (field->null_bit)
369 + column->null_bit= field->null_bit;
370 + column->null_pos= (uint) (field->null_ptr -
371 + (uchar*) table_arg->record[0]);
375 + column->null_bit= 0;
376 + column->null_pos= 0;
379 + if (field->type() == MYSQL_TYPE_VARCHAR)
381 + column->length_bytes= (uint8) (((Field_varstring *) field)->length_bytes);
383 + else if (field->type() == MYSQL_TYPE_BLOB)
386 + column->length_bytes= (uint8)
387 + (((Field_blob *) field)->pack_length_no_ptr());
391 + column->length_bytes= 0;
395 for (key= parts= 0; key < keys; key++)
396 parts+= table_arg->key_info[key].key_parts;
398 if (!(keydef= (HP_KEYDEF*) my_malloc(keys * sizeof(HP_KEYDEF) +
399 parts * sizeof(HA_KEYSEG),
402 + my_free((uchar *) columndef);
405 seg= reinterpret_cast<HA_KEYSEG*>(keydef + keys);
406 for (key= 0; key < keys; key++)
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)
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*);
421 DBUG_ASSERT(0); // cannot happen
423 seg->length= (uint) key_part->length;
424 seg->flag= key_part->key_part_flag;
426 + next_field_pos= seg->start;
427 + if (field->type() == MYSQL_TYPE_VARCHAR)
429 + Field *orig_field= *(table_arg->field + key_part->field->field_index);
430 + next_field_pos+= orig_field->pack_length();
434 + next_field_pos+= seg->length;
436 if (field->flags & (ENUM_FLAG | SET_FLAG))
437 seg->charset= &my_charset_bin;
441 auto_key_type= field->key_type();
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;
458 + case HA_KEYTYPE_VARBINARY1:
459 + /* Case-insensitiveness is handled in coll->hash_sort */
460 + seg->type= HA_KEYTYPE_VARTEXT1;
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;
470 + case HA_KEYTYPE_VARBINARY2:
471 + /* Case-insensitiveness is handled in coll->hash_sort */
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;
481 + Make future comparison simpler by only having to check for
484 + seg->type= HA_KEYTYPE_VARTEXT1;
490 + if (next_field_pos > fixed_data_size)
492 + fixed_data_size= next_field_pos;
496 + if (field->field_index >= fixed_key_fieldnr)
499 + Do not use seg->fieldnr as it's not reliable in case of temp tables
501 + fixed_key_fieldnr= field->field_index + 1;
505 - mem_per_row+= MY_ALIGN(share->reclength + 1, sizeof(char*));
507 + if (fixed_data_size < share->null_bytes)
509 + /* Make sure to include null fields regardless of the presense of keys */
510 + fixed_data_size = share->null_bytes;
513 if (table_arg->found_next_number_field)
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;
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;
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;
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);
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))
556 + if (file->s->recordspace.is_variable_size)
557 + create_info->key_block_size= file->s->recordspace.chunk_length;
559 + create_info->key_block_size= 0;
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
567 return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ?
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
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);
582 HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
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; }
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
596 #define HP_MIN_RECORDS_IN_BLOCK 16
597 #define HP_MAX_RECORDS_IN_BLOCK 8192
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
606 /* Some extern variables */
608 extern LIST *heap_open_list,*heap_share_list;
610 #define hp_find_hash(A,B) ((HASH_INFO*) hp_find_block((A),(B)))
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)
617 +#define get_chunk_status(info,ptr) (ptr[(info)->offset_status])
619 +#define get_chunk_count(info,rec_length) \
620 + ((rec_length + (info)->chunk_dataspace_length - 1) / \
621 + (info)->chunk_dataspace_length)
623 typedef struct st_hp_hash_info
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);
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);
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,
644 +extern void hp_free_chunks(HP_DATASPACE *info, uchar *pos);
645 +extern void hp_clear_dataspace(HP_DATASPACE *info);
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,
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,
656 extern mysql_mutex_t THR_LOCK_heap;
658 --- a/storage/heap/hp_clear.c
659 +++ b/storage/heap/hp_clear.c
662 DBUG_ENTER("hp_clear");
664 - if (info->block.levels)
665 - (void) hp_free_level(&info->block,info->block.levels,info->block.root,
667 - info->block.levels=0;
668 + hp_clear_dataspace(&info->recordspace);
670 - info->records= info->deleted= 0;
671 - info->data_length= 0;
681 HP_SHARE *share= info->s;
683 - if (share->data_length || share->index_length)
684 + if (share->recordspace.total_data_length || share->index_length)
685 error= HA_ERR_CRASHED;
687 if (share->currently_disabled_keys)
688 --- a/storage/heap/hp_close.c
689 +++ b/storage/heap/hp_close.c
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)
696 + my_free(info->blob_buffer);
701 --- a/storage/heap/hp_create.c
702 +++ b/storage/heap/hp_create.c
704 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
707 +#include <mysql_com.h>
708 +#include <mysqld_error.h>
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,
715 +#define FIXED_REC_OVERHEAD (sizeof(uchar))
716 +#define VARIABLE_REC_OVERHEAD (sizeof(uchar **) + sizeof(uchar))
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))
723 /* Create a heap table */
725 int heap_create(const char *name, HP_CREATE_INFO *create_info,
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");
733 if (!create_info->internal_table)
738 + uint chunk_dataspace_length, chunk_length, is_variable_size;
739 + uint fixed_data_length, fixed_column_count;
741 DBUG_PRINT("info",("Initializing new table"));
744 + if (create_info->max_chunk_size)
746 + uint configured_chunk_size= create_info->max_chunk_size;
748 + /* User requested variable-size records, let's see if they're possible */
750 + if (configured_chunk_size < create_info->fixed_data_size)
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().
757 + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "key_block_size");
761 + if (reclength > configured_chunk_size + VARIABLE_REC_OVERHEAD ||
762 + create_info->blobs > 0)
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.
771 + is_variable_size= 1;
772 + chunk_dataspace_length= configured_chunk_size;
776 + /* max_chunk_size is near the full reclength, let's use fixed size */
777 + is_variable_size= 0;
778 + chunk_dataspace_length= reclength;
781 + else if ((create_info->is_dynamic && reclength >
782 + 256 + VARIABLE_REC_OVERHEAD)
783 + || create_info->blobs > 0)
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.
789 + if ((create_info->fixed_data_size + VARIABLE_REC_OVERHEAD) > 256)
790 + chunk_dataspace_length= create_info->fixed_data_size;
792 + chunk_dataspace_length= 256 - VARIABLE_REC_OVERHEAD;
794 + is_variable_size= 1;
799 + If max_chunk_size is not specified, put the whole record in one chunk
801 + is_variable_size= 0;
802 + chunk_dataspace_length= reclength;
805 + if (is_variable_size)
807 + /* Check whether we have any variable size records past key data */
808 + uint has_variable_fields= 0;
810 + fixed_data_length= create_info->fixed_data_size;
811 + fixed_column_count= create_info->fixed_key_fieldnr;
813 + for (i= create_info->fixed_key_fieldnr; i < create_info->columns; i++)
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)
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?
825 + has_variable_fields= 1;
829 + if (has_variable_fields)
834 + if ((column->offset + column->length) <= chunk_dataspace_length)
836 + /* Still no variable-size columns, add one fixed-length */
837 + fixed_column_count= i + 1;
838 + fixed_data_length= column->offset + column->length;
842 + if (!has_variable_fields && create_info->blobs == 0)
845 + There is no need to use variable-size records without variable-size
847 + Reset sizes if it's not variable size anymore.
849 + is_variable_size= 0;
850 + chunk_dataspace_length= reclength;
851 + fixed_data_length= reclength;
852 + fixed_column_count= create_info->columns;
857 + fixed_data_length= reclength;
858 + fixed_column_count= create_info->columns;
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*)
867 - set_if_bigger(reclength, sizeof (uchar*));
869 + set_if_bigger(chunk_dataspace_length, sizeof (uchar **));
871 + if (is_variable_size)
873 + chunk_length= chunk_dataspace_length + VARIABLE_REC_OVERHEAD;
877 + chunk_length= chunk_dataspace_length + FIXED_REC_OVERHEAD;
880 + /* Align chunk length to the next pointer */
881 + chunk_length= (uint) (chunk_length + sizeof(uchar **) - 1) &
882 + ~(sizeof(uchar **) - 1);
884 for (i= key_segs= max_length= 0, keyinfo= keydef; i < keys; i++, keyinfo++)
886 bzero((char*) &keyinfo->block,sizeof(keyinfo->block));
888 keyinfo->rb_tree.size_of_element++;
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;
904 case HA_KEYTYPE_VARBINARY1:
905 - /* Case-insensitiveness is handled in coll->hash_sort */
906 - keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
908 case HA_KEYTYPE_VARTEXT1:
909 - keyinfo->flag|= HA_VAR_LENGTH_KEY;
911 - /* Save number of bytes used to store length */
912 - keyinfo->seg[j].bit_start= 1;
914 case HA_KEYTYPE_VARBINARY2:
915 - /* Case-insensitiveness is handled in coll->hash_sort */
917 case HA_KEYTYPE_VARTEXT2:
918 - keyinfo->flag|= HA_VAR_LENGTH_KEY;
920 - /* Save number of bytes used to store length */
921 - keyinfo->seg[j].bit_start= 2;
923 - Make future comparison simpler by only having to check for
926 - keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
930 @@ -133,13 +245,34 @@
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),
939 - share->keydef= (HP_KEYDEF*) (share + 1);
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
947 + max_rows_for_stated_memory= (ha_rows) (create_info->max_table_size /
948 + (create_info->keys_memory_size +
950 + max_records = ((max_records && max_records < max_rows_for_stated_memory) ?
951 + max_records : max_rows_for_stated_memory);
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));
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,
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;
976 share->max_key_length= max_length;
977 + share->column_count= create_info->columns;
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;
983 + share->fixed_data_length= fixed_data_length;
984 + share->fixed_column_count= fixed_column_count;
985 + share->blobs= create_info->blobs;
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;
992 + if (is_variable_size) {
993 + share->recordspace.offset_link= chunk_dataspace_length;
994 + share->recordspace.offset_status= share->recordspace.offset_link +
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;
1002 /* Must be allocated separately for rename to work */
1003 if (!(share->name= my_strdup(name,MYF(0))))
1006 param->search_flag, not_used);
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,
1013 uint i,recbuffer,records_in_block;
1014 @@ -235,7 +388,12 @@
1015 max_records= max(min_records,max_records);
1017 max_records= 1000; /* As good as quess as anything */
1018 - recbuffer= (uint) (reclength + sizeof(uchar**) - 1) & ~(sizeof(uchar**) - 1);
1020 + We want to start each chunk at 8 bytes boundary, round recbuffer to the
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
1032 HP_SHARE *share=info->s;
1033 HP_KEYDEF *keydef, *end, *p_lastinx;
1034 + uint rec_length, chunk_count;
1036 DBUG_ENTER("heap_delete");
1037 DBUG_PRINT("enter",("info: 0x%lx record: 0x%lx", (long) info, (long) record));
1040 DBUG_RETURN(my_errno); /* Record changed */
1043 + rec_length = hp_get_encoded_data_length(share, record, &chunk_count);
1045 if ( --(share->records) < share->blength >> 1) share->blength>>=1;
1046 pos=info->current_ptr;
1051 info->update=HA_STATE_DELETED;
1052 - *((uchar**) pos)=share->del_link;
1053 - share->del_link=pos;
1054 - pos[share->reclength]=0; /* Record 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););
1061 info->last_pos= NULL; /* For heap_rnext/heap_rprev */
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,
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,
1071 blength=share->blength;
1072 if (share->records+1 == blength)
1075 lastpos=hp_find_hash(&keyinfo->block,share->records);
1079 +++ b/storage/heap/hp_dspace.c
1081 +/* Copyright (C) 2000-2002 MySQL AB
1082 + Copyright (C) 2008 eBay, Inc
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.
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.
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 */
1098 + Implements various base dataspace-related functions - allocate, free, clear
1101 +#include "heapdef.h"
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
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.
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.
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).
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.
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.
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.
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.
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
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")
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",
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.
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
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
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
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.
1234 +static uchar *hp_allocate_one_chunk(HP_DATASPACE *info);
1240 + Frees memory and zeros-out any relevant counters in the dataspace
1242 + @param info the dataspace to clear
1245 +void hp_clear_dataspace(HP_DATASPACE *info)
1247 + if (info->block.levels)
1249 + hp_free_level(&info->block,info->block.levels,info->block.root,
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;
1260 + Allocate or reallocate a chunkset in the dataspace
1262 + Attempts to allocate a new chunkset or change the size of an existing chunkset
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
1269 + @return Pointer to the first chunk in the new or updated chunkset, or NULL
1273 +static uchar *hp_allocate_variable_chunkset(HP_DATASPACE *info,
1275 + uchar *existing_set)
1277 + int alloc_count= chunk_count, i;
1278 + uchar *first_chunk= 0, *curr_chunk= 0, *prev_chunk= 0;
1279 + uchar *last_existing_chunk= 0;
1281 + DBUG_ASSERT(alloc_count);
1285 + first_chunk= existing_set;
1287 + curr_chunk= existing_set;
1288 + while (curr_chunk && alloc_count)
1290 + prev_chunk= curr_chunk;
1291 + curr_chunk= *((uchar **) (curr_chunk + info->offset_link));
1300 + We came through all chunks and there is more left, let's truncate the
1303 + *((uchar **) (prev_chunk + info->offset_link))= NULL;
1304 + hp_free_chunks(info, curr_chunk);
1307 + return first_chunk;
1310 + last_existing_chunk= prev_chunk;
1314 + We can reach this point only if we're allocating new chunkset or more chunks
1318 + for (i= 0; i < alloc_count; i++)
1320 + curr_chunk= hp_allocate_one_chunk(info);
1323 + /* no space in the current block */
1325 + if (last_existing_chunk)
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);
1333 + else if (first_chunk)
1335 + /* free any chunks previously allocated */
1336 + hp_free_chunks(info, first_chunk);
1342 + /* mark as if this chunk is last in the chunkset */
1343 + *((uchar **) (curr_chunk + info->offset_link))= 0;
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;
1354 + /* Record active */
1355 + curr_chunk[info->offset_status]= CHUNK_STATUS_ACTIVE;
1360 + first_chunk= curr_chunk;
1363 + prev_chunk= curr_chunk;
1366 + return first_chunk;
1371 + Allocate a new chunkset in the dataspace
1373 + Attempts to allocate a new chunkset
1375 + @param info the hosting dataspace
1376 + @param chunk_count the number of chunks that we expect as the result
1378 + @return Pointer to the first chunk in the new or updated chunkset, or NULL if
1382 +uchar *hp_allocate_chunkset(HP_DATASPACE *info, uint chunk_count)
1386 + DBUG_ENTER("hp_allocate_chunks");
1388 + if (info->is_variable_size)
1390 + result = hp_allocate_variable_chunkset(info, chunk_count, NULL);
1394 + result= hp_allocate_one_chunk(info);
1397 + result[info->offset_status]= CHUNK_STATUS_ACTIVE;
1400 + DBUG_RETURN(result);
1403 + DBUG_RETURN(result);
1408 + Reallocate an existing chunkset in the dataspace
1410 + Attempts to change the size of an existing chunkset
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
1416 + @return Error code or zero if successful
1419 +int hp_reallocate_chunkset(HP_DATASPACE *info, uint chunk_count, uchar *pos)
1421 + DBUG_ENTER("hp_reallocate_chunks");
1423 + if (!info->is_variable_size)
1425 + /* Update should never change chunk_count in fixed-size mode */
1426 + my_errno= HA_ERR_WRONG_COMMAND;
1430 + /* Reallocate never moves the first chunk */
1431 + if (!hp_allocate_variable_chunkset(info, chunk_count, pos))
1432 + DBUG_RETURN(my_errno);
1439 + Allocate a single chunk in the dataspace
1441 + Attempts to allocate a new chunk or reuse one from deleted list
1443 + @param info the hosting dataspace
1445 + @return Pointer to the chunk, or NULL if unsuccessful
1448 +static uchar *hp_allocate_one_chunk(HP_DATASPACE *info)
1450 + uchar *curr_chunk;
1454 + if (info->del_link)
1456 + curr_chunk= info->del_link;
1457 + info->del_link= *((uchar **) curr_chunk);
1458 + info->del_chunk_count--;
1460 + DBUG_PRINT("hp_allocate_one_chunk",
1461 + ("Used old position: 0x%lx",(long) curr_chunk));
1462 + return curr_chunk;
1465 + block_pos= (info->chunk_count % info->block.records_in_block);
1468 + if (hp_get_new_block(&info->block, &length))
1470 + /* no space in the current block */
1474 + info->total_data_length+= length;
1477 + info->chunk_count++;
1478 + curr_chunk= ((uchar *) info->block.level_info[0].last_blocks +
1479 + block_pos * info->block.recbuffer);
1481 + DBUG_PRINT("hp_allocate_one_chunk",
1482 + ("Used new position: 0x%lx", (long) curr_chunk));
1484 + return curr_chunk;
1489 + Free a list of chunks
1491 + Reclaims all chunks linked by the pointer,
1492 + which could be the whole chunkset or a part of an existing chunkset
1494 + @param info the hosting dataspace
1495 + @param pos pointer to the head of the chunkset
1498 +void hp_free_chunks(HP_DATASPACE *info, uchar *pos)
1500 + uchar *curr_chunk= pos;
1502 + while (curr_chunk)
1504 + info->del_chunk_count++;
1505 + *((uchar **) curr_chunk)= info->del_link;
1506 + info->del_link= curr_chunk;
1508 + curr_chunk[info->offset_status]= CHUNK_STATUS_DELETED;
1510 + DBUG_PRINT("hp_free_chunks",("Freed position: 0x%lx", (long) curr_chunk));
1512 + if (!info->is_variable_size)
1517 + /* Delete next chunk in this chunkset */
1518 + curr_chunk= *((uchar **)(curr_chunk + info->offset_link));
1521 --- a/storage/heap/hp_extra.c
1522 +++ b/storage/heap/hp_extra.c
1524 info->current_record= (ulong) ~0L;
1525 info->current_hash_ptr=0;
1527 - info->next_block=0;
1531 --- a/storage/heap/hp_hash.c
1532 +++ b/storage/heap/hp_hash.c
1533 @@ -336,16 +336,26 @@
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);
1540 + if (seg->flag & HA_BLOB_PART)
1542 + memcpy(&pos, pos + pack_length, sizeof(char *));
1546 + pos+= pack_length;
1549 if (cs->mbmaxlen > 1)
1552 - char_length= my_charpos(cs, pos + pack_length,
1553 - pos + pack_length + length,
1554 + char_length= my_charpos(cs, pos,
1556 seg->length/cs->mbmaxlen);
1557 set_if_smaller(length, char_length);
1559 - cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2);
1560 + cs->coll->hash_sort(cs, pos, length, &nr, &nr2);
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)
1570 - char_length1= (uint) *(uchar*) pos1++;
1571 - char_length2= (uint) *(uchar*) pos2++;
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;
1580 + if (seg->flag & HA_BLOB_PART)
1582 - char_length1= uint2korr(pos1);
1583 - char_length2= uint2korr(pos2);
1586 + memcpy(&pos1, pos1, sizeof(char *));
1587 + memcpy(&pos2, pos2, sizeof(char *));
1590 if (cs->mbmaxlen > 1)
1592 uint safe_length1= char_length1;
1593 @@ -668,6 +678,34 @@
1598 + Returns a BLOB length stored in the specified number of bytes at the
1599 + specified location.
1601 + @param length the number of bytes used to store length
1602 + @param pos pointer to length bytes
1604 + @return Length of BLOB data.
1607 +uint hp_calc_blob_length(uint bytes, const uchar *pos)
1611 + return (uint) *pos;
1613 + return uint2korr(pos);
1615 + return uint3korr(pos);
1617 + return uint4korr(pos);
1622 + return 0; /* Impossible */
1625 /* Copy a key from a record to a keybuffer */
1627 void hp_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec)
1628 @@ -678,18 +716,37 @@
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;
1635 *key++= test(rec[seg->null_pos] & seg->null_bit);
1636 - if (cs->mbmaxlen > 1)
1638 + if (seg->flag & HA_BLOB_PART)
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);
1646 + memcpy(&pos, rec + seg->bit_start, sizeof(char *));
1647 + if (cs->mbmaxlen > 1)
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? */
1653 + store_key_length_inc(key, char_length);
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);
1660 + if (cs->mbmaxlen > 1)
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? */
1666 + if (seg->type == HA_KEYTYPE_VARTEXT1)
1667 + char_length+= seg->bit_start; /* Copy also length */
1670 + memcpy(key, pos, (size_t) char_length);
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)
1683 uchar *start_key= key;
1684 HA_KEYSEG *seg, *endseg;
1685 @@ -772,6 +829,29 @@
1689 + else if (seg->flag & HA_BLOB_PART)
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;
1697 + /* check_one_rb_key() calls hp_rb_make_key() for already packed records */
1700 + memcpy(&pos, pos + seg->bit_start, sizeof(char *));
1704 + pos+= seg->bit_start;
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;
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
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;
1726 + if (info->s->recordspace.is_variable_size)
1728 + if (info->s->records)
1729 + x->reclength = (uint) (info->s->recordspace.total_data_length /
1730 + (ulonglong) info->s->records);
1732 + x->reclength = info->s->recordspace.chunk_length;
1736 + x->reclength = info->s->recordspace.chunk_dataspace_length;
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
1747 info->opt_flag= READ_CHECK_USED; /* Check when changing */
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));
1759 +++ b/storage/heap/hp_record.c
1761 +/* Copyright (C) 2000-2002 MySQL AB
1762 + Copyright (C) 2008 eBay, Inc
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.
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.
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 */
1778 + Implements various base record-related functions, such as encode and decode
1782 +#include "heapdef.h"
1783 +#include <mysql_com.h>
1786 + Calculate size of the record for the purpose of storing in chunks
1788 + Walk through the fields of the record and calculates the exact space
1789 + needed in chunks as well the the total chunk count
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
1795 + @return The size of the required storage in bytes
1798 +uint hp_get_encoded_data_length(HP_SHARE *info, const uchar *record,
1799 + uint *chunk_count)
1801 + uint i, dst_offset;
1803 + dst_offset= info->fixed_data_length;
1805 + if (!info->recordspace.is_variable_size)
1807 + /* Nothing more to copy */
1809 + return dst_offset;
1812 + for (i= info->fixed_column_count; i < info->column_count; i++)
1814 + uint src_offset, length;
1816 + HP_COLUMNDEF *column= info->column_defs + i;
1818 + if (column->null_bit)
1820 + if (record[column->null_pos] & column->null_bit)
1822 + /* Skip all NULL values */
1827 + src_offset= column->offset;
1828 + if (column->type == MYSQL_TYPE_VARCHAR)
1832 + /* >= 5.0.3 true VARCHAR */
1834 + pack_length= column->length_bytes;
1835 + length= pack_length + (pack_length == 1 ?
1836 + (uint) *(uchar *) (record + src_offset) :
1837 + uint2korr(record + src_offset));
1839 + else if (column->type == MYSQL_TYPE_BLOB)
1841 + uint pack_length= column->length_bytes;
1843 + length= pack_length + hp_calc_blob_length(pack_length,
1844 + record + src_offset);
1848 + length= column->length;
1851 + dst_offset+= length;
1854 + *chunk_count= get_chunk_count(&info->recordspace, dst_offset);
1856 + return dst_offset;
1860 +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
1861 +static void dump_chunk(HP_SHARE *info, const uchar *curr_chunk)
1864 + fprintf(stdout, "Chunk dump at 0x%lx: ", (long) curr_chunk);
1865 + for (i= 0; i < info->recordspace.chunk_dataspace_length; i++)
1867 + uint b= *((uchar *)(curr_chunk + i));
1870 + fprintf(stdout, "0");
1872 + fprintf(stdout, "%lx ", (long) b);
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))));
1881 + Stores data from packed field into the preallocated chunkset,
1882 + or performs data comparison
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
1892 + @return Status of comparison
1893 + @retval non-zero if comparison found data differences
1894 + @retval zero otherwise
1898 +hp_process_field_data_to_chunkset(HP_SHARE *info, const uchar *data,
1899 + uint length, uchar **pos_ptr, uint *off_ptr,
1903 + uchar *curr_chunk= *pos_ptr;
1904 + uint dst_offset= *off_ptr;
1907 + while (length > 0)
1910 + to_copy= info->recordspace.chunk_dataspace_length - dst_offset;
1913 + /* Jump to the next chunk */
1914 +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
1915 + dump_chunk(info, curr_chunk);
1917 + curr_chunk= *((uchar **) (curr_chunk + info->recordspace.offset_link));
1922 + to_copy= min(length, to_copy);
1926 + if (memcmp(curr_chunk + dst_offset, data, (size_t) to_copy))
1933 + memcpy(curr_chunk + dst_offset, data, (size_t) to_copy);
1937 + dst_offset+= to_copy;
1944 + *pos_ptr= curr_chunk;
1945 + *off_ptr= dst_offset;
1951 + Encodes or compares record
1953 + Copies data from original unpacked record into the preallocated chunkset,
1954 + or performs data comparison
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
1962 + @return Status of comparison
1963 + @retval non-zero if comparison fond data differences
1964 + @retval zero otherwise
1967 +uint hp_process_record_data_to_chunkset(HP_SHARE *info, const uchar *record,
1968 + uchar *pos, uint is_compare)
1970 + uint i, dst_offset;
1971 + uchar *curr_chunk= pos;
1975 + if (memcmp(curr_chunk, record, (size_t) info->fixed_data_length))
1982 + memcpy(curr_chunk, record, (size_t) info->fixed_data_length);
1985 + if (!info->recordspace.is_variable_size)
1987 + /* Nothing more to copy */
1991 + dst_offset= info->fixed_data_length;
1993 + for (i= info->fixed_column_count; i < info->column_count; i++)
1996 + const uchar *data;
1998 + HP_COLUMNDEF *column= info->column_defs + i;
2000 + if (column->null_bit)
2002 + if (record[column->null_pos] & column->null_bit)
2004 + /* Skip all NULL values */
2009 + data= record + column->offset;
2010 + if (column->type == MYSQL_TYPE_VARCHAR)
2014 + /* >= 5.0.3 true VARCHAR */
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));
2020 + else if (column->type == MYSQL_TYPE_BLOB)
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 *));
2033 + length= column->length;
2036 + if (hp_process_field_data_to_chunkset(info, data, length, &curr_chunk,
2037 + &dst_offset, is_compare))
2043 +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2044 + dump_chunk(info, curr_chunk);
2052 + Stores record in the heap table chunks
2054 + Copies data from original unpacked record into the preallocated chunkset
2056 + @param info the hosting table
2057 + @param record the record in standard unpacked format
2058 + @param pos the target chunkset
2061 +void hp_copy_record_data_to_chunkset(HP_SHARE *info, const uchar *record,
2064 + DBUG_ENTER("hp_copy_record_data_to_chunks");
2066 + hp_process_record_data_to_chunkset(info, record, pos, 0);
2073 + Macro to switch curr_chunk to the next chunk in the chunkset and reset
2076 +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2077 +#define SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset) \
2079 + curr_chunk= *((uchar**) (curr_chunk + share->recordspace.offset_link)); \
2081 + dump_chunk(share, curr_chunk); \
2084 +#define SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset) \
2086 + curr_chunk= *((uchar**) (curr_chunk + share->recordspace.offset_link)); \
2092 + Copies record data from storage to unpacked record format
2094 + Copies data from chunkset into its original unpacked record
2096 + @param info the hosting table
2097 + @param[out] record the target record in standard unpacked format
2098 + @param pos the source chunkset
2100 + @return Status of conversion
2102 + @retval 1 out of memory
2105 +int hp_extract_record(HP_INFO *info, uchar *record, const uchar *pos)
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;
2113 + uint init_offset= share->blobs * sizeof(uint) * 2;
2115 + DBUG_ENTER("hp_extract_record");
2117 +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2118 + if (share->recordspace.is_variable_size)
2120 + dump_chunk(share, curr_chunk);
2124 + memcpy(record, curr_chunk, (size_t) share->fixed_data_length);
2126 + if (!share->recordspace.is_variable_size)
2128 + /* Nothing more to copy */
2132 + /* Reserve space for rec_offsets and buf_offsets.*/
2133 + info->blob_offset= init_offset;
2134 + src_offset= share->fixed_data_length;
2136 + for (i= share->fixed_column_count; i < share->column_count; i++)
2138 + uint length, is_null= 0;
2141 + HP_COLUMNDEF *column= share->column_defs + i;
2143 + if (column->null_bit)
2145 + if (record[column->null_pos] & column->null_bit)
2153 + /* TODO: is memset really needed? */
2154 + memset(record + column->offset, 0, column->length);
2158 + to= record + column->offset;
2159 + if (column->type == MYSQL_TYPE_VARCHAR || column->type == MYSQL_TYPE_BLOB)
2161 + uint pack_length, i;
2164 + pack_length= column->length_bytes;
2166 + for (i= 0; i < pack_length; i++)
2168 + if (src_offset == share->recordspace.chunk_dataspace_length)
2170 + SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset);
2172 + *to++= curr_chunk[src_offset++];
2175 + We copy byte-by-byte and then use hp_calc_blob_length to combine bytes
2176 + in the right order.
2178 + length= hp_calc_blob_length(pack_length, tmp);
2180 + if (column->type == MYSQL_TYPE_BLOB && length == 0)
2183 + Store a zero pointer for zero-length BLOBs because the server
2184 + relies on that (see Field_blob::val_*().
2186 + *(uchar **) to= 0;
2188 + else if (column->type == MYSQL_TYPE_BLOB && length > 0)
2190 + uint newsize= info->blob_offset + length;
2192 + DBUG_ASSERT(share->blobs > 0);
2194 + Make sure we have enough space in blob_buffer and store the pointer
2195 + to this blob in record.
2197 + if (info->blob_size < newsize)
2200 + ptr= my_realloc(info->blob_buffer, newsize, MYF(MY_ALLOW_ZERO_PTR));
2206 + if (info->blob_buffer == NULL)
2208 + memset(ptr, 0, init_offset);
2210 + info->blob_buffer= ptr;
2211 + info->blob_size= newsize;
2214 + rec_offsets= (uint *) info->blob_buffer;
2215 + buf_offsets= rec_offsets + share->blobs;
2217 + rec_offsets[nblobs]= (uint) (to - record);
2218 + buf_offsets[nblobs]= info->blob_offset;
2221 + /* Change 'to' so blob data is copied into blob_buffer */
2222 + to= info->blob_buffer + info->blob_offset;
2223 + info->blob_offset= newsize;
2228 + length= column->length;
2231 + while (length > 0)
2235 + to_copy= share->recordspace.chunk_dataspace_length - src_offset;
2238 + SWITCH_TO_NEXT_CHUNK_FOR_READ(share, curr_chunk, src_offset);
2239 + to_copy= share->recordspace.chunk_dataspace_length;
2242 + to_copy= min(length, to_copy);
2244 + memcpy(to, curr_chunk + src_offset, (size_t) to_copy);
2245 + src_offset+= to_copy;
2251 + /* Store pointers to blob data in record */
2252 + for (i= 0; i < nblobs; i++)
2254 + *(uchar **) (record + rec_offsets[i]) = info->blob_buffer + buf_offsets[i];
2259 --- a/storage/heap/hp_rfirst.c
2260 +++ b/storage/heap/hp_rfirst.c
2262 memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos),
2264 info->current_ptr = pos;
2265 - memcpy(record, pos, (size_t)share->reclength);
2266 + if (hp_extract_record(info, record, pos))
2268 + DBUG_RETURN(my_errno);
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
2276 if (!(keyinfo->flag & HA_NOSAME))
2277 memcpy(info->lastkey, key, (size_t) keyinfo->length);
2279 - memcpy(record, pos, (size_t) share->reclength);
2280 + if (hp_extract_record(info, record, pos))
2282 + DBUG_RETURN(my_errno);
2284 info->update= HA_STATE_AKTIV;
2287 --- a/storage/heap/hp_rlast.c
2288 +++ b/storage/heap/hp_rlast.c
2290 memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos),
2292 info->current_ptr = pos;
2293 - memcpy(record, pos, (size_t)share->reclength);
2294 + if (hp_extract_record(info, record, pos))
2296 + DBUG_RETURN(my_errno);
2298 info->update = HA_STATE_AKTIV;
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);
2307 - memcpy(record,pos,(size_t) share->reclength);
2308 + if (hp_extract_record(info, record, pos))
2310 + DBUG_RETURN(my_errno);
2312 info->update=HA_STATE_AKTIV | HA_STATE_NEXT_FOUND;
2315 --- a/storage/heap/hp_rprev.c
2316 +++ b/storage/heap/hp_rprev.c
2318 my_errno=HA_ERR_END_OF_FILE;
2319 DBUG_RETURN(my_errno);
2321 - memcpy(record,pos,(size_t) share->reclength);
2322 + if (hp_extract_record(info, record, pos))
2324 + DBUG_RETURN(my_errno);
2326 info->update=HA_STATE_AKTIV | HA_STATE_PREV_FOUND;
2329 --- a/storage/heap/hp_rrnd.c
2330 +++ b/storage/heap/hp_rrnd.c
2333 DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2335 - if (!info->current_ptr[share->reclength])
2336 + if (get_chunk_status(&share->recordspace, info->current_ptr) !=
2337 + CHUNK_STATUS_ACTIVE)
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);
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))
2347 + DBUG_RETURN(my_errno);
2349 DBUG_PRINT("exit", ("found record at 0x%lx", (long) info->current_ptr));
2350 info->current_hash_ptr=0; /* Can't use rnext */
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))
2361 - info->current_ptr+=share->block.recbuffer;
2362 + info->current_ptr+= share->block.recbufferlen;
2367 info->current_record=pos;
2369 - if (pos >= share->records+share->deleted)
2370 + if (pos >= share->used_chunk_count + share->deleted_chunk_count)
2373 DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2375 hp_find_record(info, pos);
2378 - if (!info->current_ptr[share->reclength])
2379 + if (GET_CHUNK_STATUS(info, info->current_ptr) != CHUNK_STATUS_ACTIVE)
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);
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))
2389 + DBUG_RETURN(my_errno);
2391 DBUG_PRINT("exit",("found record at 0x%lx",info->current_ptr));
2392 info->current_hash_ptr=0; /* Can't use rnext */
2394 --- a/storage/heap/hp_rsame.c
2395 +++ b/storage/heap/hp_rsame.c
2397 DBUG_ENTER("heap_rsame");
2400 - if (info->current_ptr[share->reclength])
2401 + if (get_chunk_status(&share->recordspace, info->current_ptr) ==
2402 + CHUNK_STATUS_ACTIVE)
2404 if (inx < -1 || inx >= (int) share->keys)
2407 DBUG_RETURN(my_errno);
2410 - memcpy(record,info->current_ptr,(size_t) share->reclength);
2411 + if (hp_extract_record(info, record, info->current_ptr))
2413 + DBUG_RETURN(my_errno);
2418 + /* treat deleted and linked chunks as deleted */
2422 DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
2423 --- a/storage/heap/hp_scan.c
2424 +++ b/storage/heap/hp_scan.c
2427 info->current_record= (ulong) ~0L; /* No current record */
2429 - info->next_block=0;
2434 DBUG_ENTER("heap_scan");
2436 pos= ++info->current_record;
2437 - if (pos < info->next_block)
2438 + if (pos >= share->recordspace.chunk_count)
2440 - info->current_ptr+=share->block.recbuffer;
2442 + DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2446 - info->next_block+=share->block.records_in_block;
2447 - if (info->next_block >= share->records+share->deleted)
2449 - info->next_block= share->records+share->deleted;
2450 - if (pos >= info->next_block)
2453 - DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
2456 - hp_find_record(info, pos);
2458 - if (!info->current_ptr[share->reclength])
2460 + hp_find_record(info, pos);
2462 + if (get_chunk_status(&share->recordspace, info->current_ptr) !=
2463 + CHUNK_STATUS_ACTIVE)
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);
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))
2474 + DBUG_RETURN(my_errno);
2476 info->current_hash_ptr=0; /* Can't use read_next */
2479 --- a/storage/heap/hp_test1.c
2480 +++ b/storage/heap/hp_test1.c
2482 #include <my_global.h>
2484 #include <m_string.h>
2485 +#include <mysql_com.h>
2488 static int get_options(int argc, char *argv[]);
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;
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;
2506 keyinfo[0].keysegs=1;
2507 keyinfo[0].seg=keyseg;
2509 keyinfo[0].seg[0].null_bit= 0;
2510 keyinfo[0].flag = HA_NOSAME;
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;
2521 bzero((uchar*) flags,sizeof(flags));
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)))
2529 printf("- Writing records:s\n");
2530 --- a/storage/heap/hp_test2.c
2531 +++ b/storage/heap/hp_test2.c
2534 #include "heapdef.h" /* Because of hp_find_block */
2536 +#include <mysql_com.h>
2538 #define MAX_RECORDS 100000
2543 uint write_count,update,opt_delete,check2,dupp_keys,found_key;
2544 + uint mem_per_keys;
2547 unsigned long key_check;
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;
2557 get_options(argc,argv);
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;
2572 write_count=update=opt_delete=0;
2574 @@ -118,11 +125,30 @@
2575 keyinfo[3].seg[0].null_pos=38;
2576 keyinfo[3].seg[0].charset=cs;
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;
2594 + mem_per_keys= (sizeof(char*) * 2) * 4;
2596 bzero((char*) key1,sizeof(key1));
2597 bzero((char*) key3,sizeof(key3));
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)))
2605 signal(SIGINT,endprog);
2606 --- a/storage/heap/hp_write.c
2607 +++ b/storage/heap/hp_write.c
2612 -static uchar *next_free_record_pos(HP_SHARE *info);
2613 static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
2617 HP_KEYDEF *keydef, *end;
2619 HP_SHARE *share=info->s;
2620 + uint rec_length, chunk_count;
2622 DBUG_ENTER("heap_write");
2624 if (info->mode & O_RDONLY)
2626 DBUG_RETURN(my_errno=EACCES);
2629 - if (!(pos=next_free_record_pos(share)))
2631 + if ((share->records >= share->max_records && share->max_records) ||
2632 + (share->recordspace.total_data_length + share->index_length >=
2633 + share->max_table_size))
2635 + my_errno= HA_ERR_RECORD_FILE_FULL;
2636 + DBUG_RETURN(my_errno);
2639 + rec_length= hp_get_encoded_data_length(share, record, &chunk_count);
2641 + if (!(pos= hp_allocate_chunkset(&share->recordspace, chunk_count)))
2642 DBUG_RETURN(my_errno);
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);
2653 if (++share->records == share->blength)
2654 share->blength+= share->blength;
2655 info->current_ptr=pos;
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);
2666 DBUG_RETURN(my_errno);
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,
2675 if (keyinfo->flag & HA_NOSAME)
2677 custom_arg.search_flag= SEARCH_FIND | SEARCH_UPDATE;
2678 @@ -129,42 +139,6 @@
2682 - /* Find where to place new record */
2684 -static uchar *next_free_record_pos(HP_SHARE *info)
2689 - DBUG_ENTER("next_free_record_pos");
2691 - if (info->del_link)
2693 - pos=info->del_link;
2694 - info->del_link= *((uchar**) pos);
2696 - DBUG_PRINT("exit",("Used old position: 0x%lx",(long) pos));
2699 - if (!(block_pos=(info->records % info->block.records_in_block)))
2701 - if ((info->records > info->max_records && info->max_records) ||
2702 - (info->data_length + info->index_length >= info->max_table_size))
2704 - my_errno=HA_ERR_RECORD_FILE_FULL;
2705 - DBUG_RETURN(NULL);
2707 - if (hp_get_new_block(&info->block,&length))
2708 - DBUG_RETURN(NULL);
2709 - info->data_length+=length;
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);
2720 Write a hash-key to the hash-index
2721 --- a/storage/heap/hp_update.c
2722 +++ b/storage/heap/hp_update.c
2725 #include "heapdef.h"
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)
2730 HP_KEYDEF *keydef, *end, *p_lastinx;
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;
2737 DBUG_ENTER("heap_update");
2740 pos=info->current_ptr;
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 */
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);
2749 + if (new_chunk_count > old_chunk_count)
2751 + /* extend the old chunkset size as necessary, but do not shrink yet */
2752 + if (hp_reallocate_chunkset(&share->recordspace, new_chunk_count, pos))
2754 + DBUG_RETURN(my_errno); /* Out of memory or table space */
2758 if (--(share->records) < share->blength >> 1) share->blength>>= 1;
2761 p_lastinx= share->keydef + info->lastinx;
2762 for (keydef= share->keydef, end= keydef + share->keys; keydef < end; keydef++)
2764 - if (hp_rec_key_cmp(keydef, old, heap_new, 0))
2765 + if (hp_rec_key_cmp(keydef, old_record, new_record, 0))
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))
2773 if (share->auto_key == (uint) (keydef - share->keydef + 1))
2774 auto_key_changed= 1;
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;
2782 + if (new_chunk_count < old_chunk_count)
2784 + /* Shrink the chunkset to its new size */
2785 + hp_reallocate_chunkset(&share->recordspace, new_chunk_count, pos);
2788 #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
2789 DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
2791 if (auto_key_changed)
2792 - heap_update_auto_increment(info, heap_new);
2793 + heap_update_auto_increment(info, new_record);
2798 if (keydef->algorithm == HA_KEY_ALG_BTREE)
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))
2804 if (++(share->records) == share->blength)
2805 share->blength+= share->blength;
2808 while (keydef >= share->keydef)
2810 - if (hp_rec_key_cmp(keydef, old, heap_new, 0))
2811 + if (hp_rec_key_cmp(keydef, old_record, new_record, 0))
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))
2822 if (++(share->records) == share->blength)
2823 share->blength+= share->blength;
2825 + if (new_chunk_count > old_chunk_count)
2827 + /* Shrink the chunkset to its original size */
2828 + hp_reallocate_chunkset(&share->recordspace, old_chunk_count, pos);
2831 DBUG_RETURN(my_errno);