summaryrefslogtreecommitdiff
path: root/storage/heap
diff options
context:
space:
mode:
Diffstat (limited to 'storage/heap')
-rw-r--r--storage/heap/.cvsignore6
-rwxr-xr-xstorage/heap/CMakeLists.txt25
-rw-r--r--storage/heap/ChangeLog8
-rw-r--r--storage/heap/Makefile.am55
-rw-r--r--storage/heap/_check.c203
-rw-r--r--storage/heap/_rectest.c30
-rw-r--r--storage/heap/ha_heap.cc769
-rw-r--r--storage/heap/ha_heap.h121
-rw-r--r--storage/heap/heapdef.h111
-rw-r--r--storage/heap/hp_block.c152
-rw-r--r--storage/heap/hp_clear.c194
-rw-r--r--storage/heap/hp_close.c51
-rw-r--r--storage/heap/hp_create.c306
-rw-r--r--storage/heap/hp_delete.c202
-rw-r--r--storage/heap/hp_extra.c85
-rw-r--r--storage/heap/hp_hash.c998
-rw-r--r--storage/heap/hp_info.c59
-rw-r--r--storage/heap/hp_open.c135
-rw-r--r--storage/heap/hp_panic.c56
-rw-r--r--storage/heap/hp_rename.c41
-rw-r--r--storage/heap/hp_rfirst.c71
-rw-r--r--storage/heap/hp_rkey.c80
-rw-r--r--storage/heap/hp_rlast.c55
-rw-r--r--storage/heap/hp_rnext.c115
-rw-r--r--storage/heap/hp_rprev.c83
-rw-r--r--storage/heap/hp_rrnd.c105
-rw-r--r--storage/heap/hp_rsame.c56
-rw-r--r--storage/heap/hp_scan.c72
-rw-r--r--storage/heap/hp_static.c25
-rw-r--r--storage/heap/hp_test1.c195
-rw-r--r--storage/heap/hp_test2.c695
-rw-r--r--storage/heap/hp_update.c88
-rw-r--r--storage/heap/hp_write.c403
-rwxr-xr-xstorage/heap/make-ccc4
-rw-r--r--storage/heap/plug.in7
35 files changed, 5661 insertions, 0 deletions
diff --git a/storage/heap/.cvsignore b/storage/heap/.cvsignore
new file mode 100644
index 00000000000..675df8a3eb6
--- /dev/null
+++ b/storage/heap/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+hp_test1
+hp_test2
diff --git a/storage/heap/CMakeLists.txt b/storage/heap/CMakeLists.txt
new file mode 100755
index 00000000000..c2d2cd1290f
--- /dev/null
+++ b/storage/heap/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Copyright (C) 2006 MySQL AB
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
+SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
+
+INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake")
+SET(HEAP_SOURCES _check.c _rectest.c hp_block.c hp_clear.c hp_close.c hp_create.c
+ ha_heap.cc
+ hp_delete.c hp_extra.c hp_hash.c hp_info.c hp_open.c hp_panic.c
+ hp_rename.c hp_rfirst.c hp_rkey.c hp_rlast.c hp_rnext.c hp_rprev.c
+ hp_rrnd.c hp_rsame.c hp_scan.c hp_static.c hp_update.c hp_write.c)
+MYSQL_STORAGE_ENGINE(HEAP)
diff --git a/storage/heap/ChangeLog b/storage/heap/ChangeLog
new file mode 100644
index 00000000000..9d3ced84cc9
--- /dev/null
+++ b/storage/heap/ChangeLog
@@ -0,0 +1,8 @@
+Sun Sep 6 10:56:59 1992 Michael Widenius (monty@bitch)
+
+ * Added functions for first,next,last,prev and clear of database-heap
+ * Added optional index to rsame for compability.
+
+Fri Aug 14 14:34:35 1992 Michael Widenius (monty@bitch)
+
+ * changed file parameter to HP_INFO *
diff --git a/storage/heap/Makefile.am b/storage/heap/Makefile.am
new file mode 100644
index 00000000000..ec1445dea67
--- /dev/null
+++ b/storage/heap/Makefile.am
@@ -0,0 +1,55 @@
+# Copyright (C) 2000-2002, 2005-2006 MySQL AB
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+MYSQLDATAdir = $(localstatedir)
+MYSQLSHAREdir = $(pkgdatadir)
+MYSQLBASEdir= $(prefix)
+MYSQLLIBdir= $(pkglibdir)
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \
+ -I$(top_srcdir)/regex \
+ -I$(top_srcdir)/sql \
+ -I$(srcdir)
+WRAPLIBS=
+
+LDADD =
+
+DEFS = @DEFS@
+pkglib_LIBRARIES = libheap.a
+noinst_PROGRAMS = hp_test1 hp_test2
+noinst_LIBRARIES = libheap.a
+hp_test1_LDFLAGS = @NOINST_LDFLAGS@
+hp_test1_LDADD = libheap.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/strings/libmystrings.a
+hp_test2_LDFLAGS = @NOINST_LDFLAGS@
+hp_test2_LDADD = libheap.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/strings/libmystrings.a
+noinst_HEADERS = heapdef.h ha_heap.h
+libheap_a_SOURCES = hp_open.c hp_extra.c hp_close.c hp_panic.c hp_info.c \
+ hp_rrnd.c hp_scan.c hp_update.c hp_write.c hp_delete.c \
+ hp_rsame.c hp_create.c hp_rename.c hp_rfirst.c \
+ hp_rnext.c hp_rlast.c hp_rprev.c hp_clear.c \
+ hp_rkey.c hp_block.c \
+ ha_heap.cc \
+ hp_hash.c _check.c _rectest.c hp_static.c
+
+
+EXTRA_DIST = CMakeLists.txt plug.in
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
diff --git a/storage/heap/_check.c b/storage/heap/_check.c
new file mode 100644
index 00000000000..08b6da62ae1
--- /dev/null
+++ b/storage/heap/_check.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2000-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Check that heap-structure is ok */
+
+#include "heapdef.h"
+
+static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records,
+ ulong blength, my_bool print_status);
+static int check_one_rb_key(HP_INFO *info, uint keynr, ulong records,
+ my_bool print_status);
+
+
+/*
+ Check if keys and rows are ok in a heap table
+
+ SYNOPSIS
+ heap_check_heap()
+ info Table handler
+ print_status Prints some extra status
+
+ NOTES
+ Doesn't change the state of the table handler
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+int heap_check_heap(HP_INFO *info, my_bool print_status)
+{
+ int error;
+ uint key;
+ ulong records=0, deleted=0, pos, next_block;
+ HP_SHARE *share=info->s;
+ HP_INFO save_info= *info; /* Needed because scan_init */
+ DBUG_ENTER("heap_check_heap");
+
+ for (error=key= 0 ; key < share->keys ; key++)
+ {
+ if (share->keydef[key].algorithm == HA_KEY_ALG_BTREE)
+ error|= check_one_rb_key(info, key, share->records, print_status);
+ else
+ error|= check_one_key(share->keydef + key, key, share->records,
+ share->blength, print_status);
+ }
+ /*
+ This is basicly the same code as in hp_scan, but we repeat it here to
+ get shorter DBUG log file.
+ */
+ for (pos=next_block= 0 ; ; pos++)
+ {
+ if (pos < next_block)
+ {
+ info->current_ptr+= share->block.recbuffer;
+ }
+ else
+ {
+ next_block+= share->block.records_in_block;
+ if (next_block >= share->records+share->deleted)
+ {
+ next_block= share->records+share->deleted;
+ if (pos >= next_block)
+ break; /* End of file */
+ }
+ }
+ hp_find_record(info,pos);
+
+ if (!info->current_ptr[share->reclength])
+ deleted++;
+ else
+ records++;
+ }
+
+ if (records != share->records || deleted != share->deleted)
+ {
+ DBUG_PRINT("error",("Found rows: %lu (%lu) deleted %lu (%lu)",
+ records, (ulong) share->records,
+ deleted, (ulong) share->deleted));
+ error= 1;
+ }
+ *info= save_info;
+ DBUG_RETURN(error);
+}
+
+
+static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records,
+ ulong blength, my_bool print_status)
+{
+ int error;
+ ulong i,found,max_links,seek,links;
+ ulong rec_link; /* Only used with debugging */
+ ulong hash_buckets_found;
+ HASH_INFO *hash_info;
+
+ error=0;
+ hash_buckets_found= 0;
+ for (i=found=max_links=seek=0 ; i < records ; i++)
+ {
+ hash_info=hp_find_hash(&keydef->block,i);
+ if (hp_mask(hp_rec_hashnr(keydef, hash_info->ptr_to_rec),
+ blength,records) == i)
+ {
+ found++;
+ seek++;
+ links=1;
+ while ((hash_info=hash_info->next_key) && found < records + 1)
+ {
+ seek+= ++links;
+ if ((rec_link = hp_mask(hp_rec_hashnr(keydef, hash_info->ptr_to_rec),
+ blength, records))
+ != i)
+ {
+ DBUG_PRINT("error",
+ ("Record in wrong link: Link %lu Record: 0x%lx Record-link %lu",
+ i, (long) hash_info->ptr_to_rec, rec_link));
+ error=1;
+ }
+ else
+ found++;
+ }
+ if (links > max_links) max_links=links;
+ hash_buckets_found++;
+ }
+ }
+ if (found != records)
+ {
+ DBUG_PRINT("error",("Found %ld of %ld records", found, records));
+ error=1;
+ }
+ if (keydef->hash_buckets != hash_buckets_found)
+ {
+ DBUG_PRINT("error",("Found %ld buckets, stats shows %ld buckets",
+ hash_buckets_found, (long) keydef->hash_buckets));
+ error=1;
+ }
+ DBUG_PRINT("info",
+ ("records: %ld seeks: %lu max links: %lu hitrate: %.2f "
+ "buckets: %lu",
+ records,seek,max_links,
+ (float) seek / (float) (records ? records : 1),
+ hash_buckets_found));
+ if (print_status)
+ printf("Key: %d records: %ld seeks: %lu max links: %lu "
+ "hitrate: %.2f buckets: %lu\n",
+ keynr, records, seek, max_links,
+ (float) seek / (float) (records ? records : 1),
+ hash_buckets_found);
+ return error;
+}
+
+static int check_one_rb_key(HP_INFO *info, uint keynr, ulong records,
+ my_bool print_status)
+{
+ HP_KEYDEF *keydef= info->s->keydef + keynr;
+ int error= 0;
+ ulong found= 0;
+ uchar *key, *recpos;
+ uint key_length;
+ uint not_used[2];
+
+ if ((key= tree_search_edge(&keydef->rb_tree, info->parents,
+ &info->last_pos, offsetof(TREE_ELEMENT, left))))
+ {
+ do
+ {
+ memcpy(&recpos, key + (*keydef->get_key_length)(keydef,key), sizeof(uchar*));
+ key_length= hp_rb_make_key(keydef, info->recbuf, recpos, 0);
+ if (ha_key_cmp(keydef->seg, (uchar*) info->recbuf, (uchar*) key,
+ key_length, SEARCH_FIND | SEARCH_SAME, not_used))
+ {
+ error= 1;
+ DBUG_PRINT("error",("Record in wrong link: key: %u Record: 0x%lx\n",
+ keynr, (long) recpos));
+ }
+ else
+ found++;
+ key= tree_search_next(&keydef->rb_tree, &info->last_pos,
+ offsetof(TREE_ELEMENT, left),
+ offsetof(TREE_ELEMENT, right));
+ } while (key);
+ }
+ if (found != records)
+ {
+ DBUG_PRINT("error",("Found %lu of %lu records", found, records));
+ error= 1;
+ }
+ if (print_status)
+ printf("Key: %d records: %ld\n", keynr, records);
+ return error;
+}
diff --git a/storage/heap/_rectest.c b/storage/heap/_rectest.c
new file mode 100644
index 00000000000..068fedf719c
--- /dev/null
+++ b/storage/heap/_rectest.c
@@ -0,0 +1,30 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Test if a record has changed since last read */
+/* In heap this is only used when debugging */
+
+#include "heapdef.h"
+
+int hp_rectest(register HP_INFO *info, register const uchar *old)
+{
+ DBUG_ENTER("hp_rectest");
+
+ if (memcmp(info->current_ptr,old,(size_t) info->s->reclength))
+ {
+ DBUG_RETURN((my_errno=HA_ERR_RECORD_CHANGED)); /* Record have changed */
+ }
+ DBUG_RETURN(0);
+} /* _heap_rectest */
diff --git a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc
new file mode 100644
index 00000000000..fb7c13e4e41
--- /dev/null
+++ b/storage/heap/ha_heap.cc
@@ -0,0 +1,769 @@
+/* Copyright (C) 2000-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#define MYSQL_SERVER 1
+#include "mysql_priv.h"
+#include <mysql/plugin.h>
+#include "ha_heap.h"
+#include "heapdef.h"
+
+static handler *heap_create_handler(handlerton *hton,
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root);
+
+int heap_panic(handlerton *hton, ha_panic_function flag)
+{
+ return hp_panic(flag);
+}
+
+
+int heap_init(void *p)
+{
+ handlerton *heap_hton;
+
+ heap_hton= (handlerton *)p;
+ heap_hton->state= SHOW_OPTION_YES;
+ heap_hton->db_type= DB_TYPE_HEAP;
+ heap_hton->create= heap_create_handler;
+ heap_hton->panic= heap_panic;
+ heap_hton->flags= HTON_CAN_RECREATE;
+
+ return 0;
+}
+
+static handler *heap_create_handler(handlerton *hton,
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root)
+{
+ return new (mem_root) ha_heap(hton, table);
+}
+
+
+/*****************************************************************************
+** HEAP tables
+*****************************************************************************/
+
+ha_heap::ha_heap(handlerton *hton, TABLE_SHARE *table_arg)
+ :handler(hton, table_arg), file(0), records_changed(0), key_stat_version(0),
+ internal_table(0)
+{}
+
+
+static const char *ha_heap_exts[] = {
+ NullS
+};
+
+const char **ha_heap::bas_ext() const
+{
+ return ha_heap_exts;
+}
+
+/*
+ Hash index statistics is updated (copied from HP_KEYDEF::hash_buckets to
+ rec_per_key) after 1/HEAP_STATS_UPDATE_THRESHOLD fraction of table records
+ have been inserted/updated/deleted. delete_all_rows() and table flush cause
+ immediate update.
+
+ NOTE
+ hash index statistics must be updated when number of table records changes
+ from 0 to non-zero value and vice versa. Otherwise records_in_range may
+ erroneously return 0 and 'range' may miss records.
+*/
+#define HEAP_STATS_UPDATE_THRESHOLD 10
+
+int ha_heap::open(const char *name, int mode, uint test_if_locked)
+{
+ if ((test_if_locked & HA_OPEN_INTERNAL_TABLE) ||
+ (!(file= heap_open(name, mode)) && my_errno == ENOENT))
+ {
+ HA_CREATE_INFO create_info;
+ internal_table= test(test_if_locked & HA_OPEN_INTERNAL_TABLE);
+ bzero(&create_info, sizeof(create_info));
+ file= 0;
+ if (!create(name, table, &create_info))
+ {
+ file= internal_table ?
+ heap_open_from_share(internal_share, mode) :
+ heap_open_from_share_and_register(internal_share, mode);
+ if (!file)
+ {
+ /* Couldn't open table; Remove the newly created table */
+ pthread_mutex_lock(&THR_LOCK_heap);
+ hp_free(internal_share);
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ }
+ implicit_emptied= 1;
+ }
+ }
+ ref_length= sizeof(HEAP_PTR);
+ if (file)
+ {
+ /* Initialize variables for the opened table */
+ set_keys_for_scanning();
+ /*
+ We cannot run update_key_stats() here because we do not have a
+ lock on the table. The 'records' count might just be changed
+ temporarily at this moment and we might get wrong statistics (Bug
+ #10178). Instead we request for update. This will be done in
+ ha_heap::info(), which is always called before key statistics are
+ used.
+ */
+ key_stat_version= file->s->key_stat_version-1;
+ }
+ return (file ? 0 : 1);
+}
+
+int ha_heap::close(void)
+{
+ return internal_table ? hp_close(file) : heap_close(file);
+}
+
+
+/*
+ Create a copy of this table
+
+ DESCRIPTION
+ Do same as default implementation but use file->s->name instead of
+ table->s->path. This is needed by Windows where the clone() call sees
+ '/'-delimited path in table->s->path, while ha_peap::open() was called
+ with '\'-delimited path.
+*/
+
+handler *ha_heap::clone(MEM_ROOT *mem_root)
+{
+ handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type());
+ if (new_handler && !new_handler->ha_open(table, file->s->name, table->db_stat,
+ HA_OPEN_IGNORE_IF_LOCKED))
+ return new_handler;
+ return NULL; /* purecov: inspected */
+}
+
+
+/*
+ Compute which keys to use for scanning
+
+ SYNOPSIS
+ set_keys_for_scanning()
+ no parameter
+
+ DESCRIPTION
+ Set the bitmap btree_keys, which is used when the upper layers ask
+ which keys to use for scanning. For each btree index the
+ corresponding bit is set.
+
+ RETURN
+ void
+*/
+
+void ha_heap::set_keys_for_scanning(void)
+{
+ btree_keys.clear_all();
+ for (uint i= 0 ; i < table->s->keys ; i++)
+ {
+ if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE)
+ btree_keys.set_bit(i);
+ }
+}
+
+
+void ha_heap::update_key_stats()
+{
+ for (uint i= 0; i < table->s->keys; i++)
+ {
+ KEY *key=table->key_info+i;
+ if (!key->rec_per_key)
+ continue;
+ if (key->algorithm != HA_KEY_ALG_BTREE)
+ {
+ if (key->flags & HA_NOSAME)
+ key->rec_per_key[key->key_parts-1]= 1;
+ else
+ {
+ ha_rows hash_buckets= file->s->keydef[i].hash_buckets;
+ uint no_records= hash_buckets ? (uint) (file->s->records/hash_buckets) : 2;
+ if (no_records < 2)
+ no_records= 2;
+ key->rec_per_key[key->key_parts-1]= no_records;
+ }
+ }
+ }
+ records_changed= 0;
+ /* At the end of update_key_stats() we can proudly claim they are OK. */
+ key_stat_version= file->s->key_stat_version;
+}
+
+
+int ha_heap::write_row(uchar * buf)
+{
+ int res;
+ ha_statistic_increment(&SSV::ha_write_count);
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
+ table->timestamp_field->set_time();
+ if (table->next_number_field && buf == table->record[0])
+ {
+ if ((res= update_auto_increment()))
+ return res;
+ }
+ res= heap_write(file,buf);
+ if (!res && (++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
+ file->s->records))
+ {
+ /*
+ We can perform this safely since only one writer at the time is
+ allowed on the table.
+ */
+ file->s->key_stat_version++;
+ }
+ return res;
+}
+
+int ha_heap::update_row(const uchar * old_data, uchar * new_data)
+{
+ int res;
+ ha_statistic_increment(&SSV::ha_update_count);
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+ table->timestamp_field->set_time();
+ res= heap_update(file,old_data,new_data);
+ if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
+ file->s->records)
+ {
+ /*
+ We can perform this safely since only one writer at the time is
+ allowed on the table.
+ */
+ file->s->key_stat_version++;
+ }
+ return res;
+}
+
+int ha_heap::delete_row(const uchar * buf)
+{
+ int res;
+ ha_statistic_increment(&SSV::ha_delete_count);
+ res= heap_delete(file,buf);
+ if (!res && table->s->tmp_table == NO_TMP_TABLE &&
+ ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)
+ {
+ /*
+ We can perform this safely since only one writer at the time is
+ allowed on the table.
+ */
+ file->s->key_stat_version++;
+ }
+ return res;
+}
+
+int ha_heap::index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ DBUG_ASSERT(inited==INDEX);
+ ha_statistic_increment(&SSV::ha_read_key_count);
+ int error = heap_rkey(file,buf,active_index, key, keypart_map, find_flag);
+ table->status = error ? STATUS_NOT_FOUND : 0;
+ return error;
+}
+
+int ha_heap::index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map)
+{
+ DBUG_ASSERT(inited==INDEX);
+ ha_statistic_increment(&SSV::ha_read_key_count);
+ int error= heap_rkey(file, buf, active_index, key, keypart_map,
+ HA_READ_PREFIX_LAST);
+ table->status= error ? STATUS_NOT_FOUND : 0;
+ return error;
+}
+
+int ha_heap::index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ ha_statistic_increment(&SSV::ha_read_key_count);
+ int error = heap_rkey(file, buf, index, key, keypart_map, find_flag);
+ table->status = error ? STATUS_NOT_FOUND : 0;
+ return error;
+}
+
+int ha_heap::index_next(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ ha_statistic_increment(&SSV::ha_read_next_count);
+ int error=heap_rnext(file,buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::index_prev(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ ha_statistic_increment(&SSV::ha_read_prev_count);
+ int error=heap_rprev(file,buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::index_first(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ ha_statistic_increment(&SSV::ha_read_first_count);
+ int error=heap_rfirst(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::index_last(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ ha_statistic_increment(&SSV::ha_read_last_count);
+ int error=heap_rlast(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::rnd_init(bool scan)
+{
+ return scan ? heap_scan_init(file) : 0;
+}
+
+int ha_heap::rnd_next(uchar *buf)
+{
+ ha_statistic_increment(&SSV::ha_read_rnd_next_count);
+ int error=heap_scan(file, buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::rnd_pos(uchar * buf, uchar *pos)
+{
+ int error;
+ HEAP_PTR heap_position;
+ ha_statistic_increment(&SSV::ha_read_rnd_count);
+ memcpy_fixed((char*) &heap_position, pos, sizeof(HEAP_PTR));
+ error=heap_rrnd(file, buf, heap_position);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+void ha_heap::position(const uchar *record)
+{
+ *(HEAP_PTR*) ref= heap_position(file); // Ref is aligned
+}
+
+int ha_heap::info(uint flag)
+{
+ HEAPINFO hp_info;
+ (void) heap_info(file,&hp_info,flag);
+
+ errkey= hp_info.errkey;
+ stats.records= hp_info.records;
+ stats.deleted= hp_info.deleted;
+ stats.mean_rec_length= hp_info.reclength;
+ stats.data_file_length= hp_info.data_length;
+ stats.index_file_length= hp_info.index_length;
+ stats.max_data_file_length= hp_info.max_records * hp_info.reclength;
+ stats.delete_length= hp_info.deleted * hp_info.reclength;
+ if (flag & HA_STATUS_AUTO)
+ stats.auto_increment_value= hp_info.auto_increment;
+ /*
+ If info() is called for the first time after open(), we will still
+ have to update the key statistics. Hoping that a table lock is now
+ in place.
+ */
+ if (key_stat_version != file->s->key_stat_version)
+ update_key_stats();
+ return 0;
+}
+
+
+int ha_heap::extra(enum ha_extra_function operation)
+{
+ return heap_extra(file,operation);
+}
+
+
+int ha_heap::reset()
+{
+ return heap_reset(file);
+}
+
+
+int ha_heap::delete_all_rows()
+{
+ heap_clear(file);
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ {
+ /*
+ We can perform this safely since only one writer at the time is
+ allowed on the table.
+ */
+ file->s->key_stat_version++;
+ }
+ return 0;
+}
+
+
+int ha_heap::reset_auto_increment(ulonglong value)
+{
+ file->s->auto_increment= value;
+ return 0;
+}
+
+
+int ha_heap::external_lock(THD *thd, int lock_type)
+{
+ return 0; // No external locking
+}
+
+
+/*
+ Disable indexes.
+
+ SYNOPSIS
+ disable_indexes()
+ mode mode of operation:
+ HA_KEY_SWITCH_NONUNIQ disable all non-unique keys
+ HA_KEY_SWITCH_ALL disable all keys
+ HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
+ HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent
+
+ DESCRIPTION
+ Disable indexes and clear keys to use for scanning.
+
+ IMPLEMENTATION
+ HA_KEY_SWITCH_NONUNIQ is not implemented.
+ HA_KEY_SWITCH_NONUNIQ_SAVE is not implemented with HEAP.
+ HA_KEY_SWITCH_ALL_SAVE is not implemented with HEAP.
+
+ RETURN
+ 0 ok
+ HA_ERR_WRONG_COMMAND mode not implemented.
+*/
+
+int ha_heap::disable_indexes(uint mode)
+{
+ int error;
+
+ if (mode == HA_KEY_SWITCH_ALL)
+ {
+ if (!(error= heap_disable_indexes(file)))
+ set_keys_for_scanning();
+ }
+ else
+ {
+ /* mode not implemented */
+ error= HA_ERR_WRONG_COMMAND;
+ }
+ return error;
+}
+
+
+/*
+ Enable indexes.
+
+ SYNOPSIS
+ enable_indexes()
+ mode mode of operation:
+ HA_KEY_SWITCH_NONUNIQ enable all non-unique keys
+ HA_KEY_SWITCH_ALL enable all keys
+ HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
+ HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent
+
+ DESCRIPTION
+ Enable indexes and set keys to use for scanning.
+ The indexes might have been disabled by disable_index() before.
+ The function works only if both data and indexes are empty,
+ since the heap storage engine cannot repair the indexes.
+ To be sure, call handler::delete_all_rows() before.
+
+ IMPLEMENTATION
+ HA_KEY_SWITCH_NONUNIQ is not implemented.
+ HA_KEY_SWITCH_NONUNIQ_SAVE is not implemented with HEAP.
+ HA_KEY_SWITCH_ALL_SAVE is not implemented with HEAP.
+
+ RETURN
+ 0 ok
+ HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry.
+ HA_ERR_WRONG_COMMAND mode not implemented.
+*/
+
+int ha_heap::enable_indexes(uint mode)
+{
+ int error;
+
+ if (mode == HA_KEY_SWITCH_ALL)
+ {
+ if (!(error= heap_enable_indexes(file)))
+ set_keys_for_scanning();
+ }
+ else
+ {
+ /* mode not implemented */
+ error= HA_ERR_WRONG_COMMAND;
+ }
+ return error;
+}
+
+
+/*
+ Test if indexes are disabled.
+
+ SYNOPSIS
+ indexes_are_disabled()
+ no parameters
+
+ RETURN
+ 0 indexes are not disabled
+ 1 all indexes are disabled
+ [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
+*/
+
+int ha_heap::indexes_are_disabled(void)
+{
+ return heap_indexes_are_disabled(file);
+}
+
+THR_LOCK_DATA **ha_heap::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
+ file->lock.type=lock_type;
+ *to++= &file->lock;
+ return to;
+}
+
+/*
+ We have to ignore ENOENT entries as the HEAP table is created on open and
+ not when doing a CREATE on the table.
+*/
+
+int ha_heap::delete_table(const char *name)
+{
+ int error= heap_delete_table(name);
+ return error == ENOENT ? 0 : error;
+}
+
+
+void ha_heap::drop_table(const char *name)
+{
+ file->s->delete_on_close= 1;
+ close();
+}
+
+
+int ha_heap::rename_table(const char * from, const char * to)
+{
+ return heap_rename(from,to);
+}
+
+
+ha_rows ha_heap::records_in_range(uint inx, key_range *min_key,
+ key_range *max_key)
+{
+ KEY *key=table->key_info+inx;
+ if (key->algorithm == HA_KEY_ALG_BTREE)
+ return hp_rb_records_in_range(file, inx, min_key, max_key);
+
+ if (!min_key || !max_key ||
+ min_key->length != max_key->length ||
+ min_key->length != key->key_length ||
+ min_key->flag != HA_READ_KEY_EXACT ||
+ max_key->flag != HA_READ_AFTER_KEY)
+ return HA_POS_ERROR; // Can only use exact keys
+
+ if (stats.records <= 1)
+ return stats.records;
+
+ /* Assert that info() did run. We need current statistics here. */
+ DBUG_ASSERT(key_stat_version == file->s->key_stat_version);
+ return key->rec_per_key[key->key_parts-1];
+}
+
+
+int ha_heap::create(const char *name, TABLE *table_arg,
+ HA_CREATE_INFO *create_info)
+{
+ uint key, parts, mem_per_row= 0, keys= table_arg->s->keys;
+ uint auto_key= 0, auto_key_type= 0;
+ ha_rows max_rows;
+ HP_KEYDEF *keydef;
+ HA_KEYSEG *seg;
+ int error;
+ TABLE_SHARE *share= table_arg->s;
+ bool found_real_auto_increment= 0;
+
+ for (key= parts= 0; key < keys; key++)
+ parts+= table_arg->key_info[key].key_parts;
+
+ if (!(keydef= (HP_KEYDEF*) my_malloc(keys * sizeof(HP_KEYDEF) +
+ parts * sizeof(HA_KEYSEG),
+ MYF(MY_WME))))
+ return my_errno;
+ seg= my_reinterpret_cast(HA_KEYSEG*) (keydef + keys);
+ for (key= 0; key < keys; key++)
+ {
+ KEY *pos= table_arg->key_info+key;
+ KEY_PART_INFO *key_part= pos->key_part;
+ KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
+
+ keydef[key].keysegs= (uint) pos->key_parts;
+ keydef[key].flag= (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL));
+ keydef[key].seg= seg;
+
+ switch (pos->algorithm) {
+ case HA_KEY_ALG_UNDEF:
+ case HA_KEY_ALG_HASH:
+ keydef[key].algorithm= HA_KEY_ALG_HASH;
+ mem_per_row+= sizeof(char*) * 2; // = sizeof(HASH_INFO)
+ break;
+ case HA_KEY_ALG_BTREE:
+ keydef[key].algorithm= HA_KEY_ALG_BTREE;
+ mem_per_row+=sizeof(TREE_ELEMENT)+pos->key_length+sizeof(char*);
+ break;
+ default:
+ DBUG_ASSERT(0); // cannot happen
+ }
+
+ for (; key_part != key_part_end; key_part++, seg++)
+ {
+ Field *field= key_part->field;
+
+ if (pos->algorithm == HA_KEY_ALG_BTREE)
+ seg->type= field->key_type();
+ else
+ {
+ if ((seg->type = field->key_type()) != (int) HA_KEYTYPE_TEXT &&
+ seg->type != HA_KEYTYPE_VARTEXT1 &&
+ seg->type != HA_KEYTYPE_VARTEXT2 &&
+ seg->type != HA_KEYTYPE_VARBINARY1 &&
+ seg->type != HA_KEYTYPE_VARBINARY2)
+ seg->type= HA_KEYTYPE_BINARY;
+ }
+ seg->start= (uint) key_part->offset;
+ seg->length= (uint) key_part->length;
+ seg->flag= key_part->key_part_flag;
+
+ if (field->flags & (ENUM_FLAG | SET_FLAG))
+ seg->charset= &my_charset_bin;
+ else
+ seg->charset= field->charset();
+ if (field->null_ptr)
+ {
+ seg->null_bit= field->null_bit;
+ seg->null_pos= (uint) (field->null_ptr - (uchar*) table_arg->record[0]);
+ }
+ else
+ {
+ seg->null_bit= 0;
+ seg->null_pos= 0;
+ }
+ if (field->flags & AUTO_INCREMENT_FLAG &&
+ table_arg->found_next_number_field &&
+ key == share->next_number_index)
+ {
+ /*
+ Store key number and type for found auto_increment key
+ We have to store type as seg->type can differ from it
+ */
+ auto_key= key+ 1;
+ auto_key_type= field->key_type();
+ }
+ }
+ }
+ mem_per_row+= MY_ALIGN(share->reclength + 1, sizeof(char*));
+ max_rows = (ha_rows) (table_arg->in_use->variables.max_heap_table_size /
+ (ulonglong) mem_per_row);
+ if (table_arg->found_next_number_field)
+ {
+ keydef[share->next_number_index].flag|= HA_AUTO_KEY;
+ found_real_auto_increment= share->next_number_key_offset == 0;
+ }
+ HP_CREATE_INFO hp_create_info;
+ hp_create_info.auto_key= auto_key;
+ hp_create_info.auto_key_type= auto_key_type;
+ hp_create_info.auto_increment= (create_info->auto_increment_value ?
+ create_info->auto_increment_value - 1 : 0);
+ hp_create_info.max_table_size=current_thd->variables.max_heap_table_size;
+ hp_create_info.with_auto_increment= found_real_auto_increment;
+ hp_create_info.internal_table= internal_table;
+ max_rows = (ha_rows) (hp_create_info.max_table_size / mem_per_row);
+ error= heap_create(name,
+ keys, keydef, share->reclength,
+ (ulong) ((share->max_rows < max_rows &&
+ share->max_rows) ?
+ share->max_rows : max_rows),
+ (ulong) share->min_rows, &hp_create_info, &internal_share);
+ my_free((uchar*) keydef, MYF(0));
+ DBUG_ASSERT(file == 0);
+ return (error);
+}
+
+
+void ha_heap::update_create_info(HA_CREATE_INFO *create_info)
+{
+ table->file->info(HA_STATUS_AUTO);
+ if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
+ create_info->auto_increment_value= stats.auto_increment_value;
+}
+
+void ha_heap::get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
+{
+ ha_heap::info(HA_STATUS_AUTO);
+ *first_value= stats.auto_increment_value;
+ /* such table has only table-level locking so reserves up to +inf */
+ *nb_reserved_values= ULONGLONG_MAX;
+}
+
+
+bool ha_heap::check_if_incompatible_data(HA_CREATE_INFO *info,
+ uint table_changes)
+{
+ /* Check that auto_increment value was not changed */
+ if ((info->used_fields & HA_CREATE_USED_AUTO &&
+ info->auto_increment_value != 0) ||
+ table_changes == IS_EQUAL_NO ||
+ table_changes & IS_EQUAL_PACK_LENGTH) // Not implemented yet
+ return COMPATIBLE_DATA_NO;
+ return COMPATIBLE_DATA_YES;
+}
+
+struct st_mysql_storage_engine heap_storage_engine=
+{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+
+mysql_declare_plugin(heap)
+{
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ &heap_storage_engine,
+ "MEMORY",
+ "MySQL AB",
+ "Hash based, stored in memory, useful for temporary tables",
+ PLUGIN_LICENSE_GPL,
+ heap_init,
+ NULL,
+ 0x0100, /* 1.0 */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+}
+mysql_declare_plugin_end;
diff --git a/storage/heap/ha_heap.h b/storage/heap/ha_heap.h
new file mode 100644
index 00000000000..22722129f4c
--- /dev/null
+++ b/storage/heap/ha_heap.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2000-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the heap handler */
+
+#include <heap.h>
+
+class ha_heap: public handler
+{
+ HP_INFO *file;
+ HP_SHARE *internal_share;
+ key_map btree_keys;
+ /* number of records changed since last statistics update */
+ uint records_changed;
+ uint key_stat_version;
+ my_bool internal_table;
+public:
+ ha_heap(handlerton *hton, TABLE_SHARE *table);
+ ~ha_heap() {}
+ handler *clone(MEM_ROOT *mem_root);
+ const char *table_type() const
+ {
+ return (table->in_use->variables.sql_mode & MODE_MYSQL323) ?
+ "HEAP" : "MEMORY";
+ }
+ const char *index_type(uint inx)
+ {
+ return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ?
+ "BTREE" : "HASH");
+ }
+ /* Rows also use a fixed-size format */
+ enum row_type get_row_type() const { return ROW_TYPE_FIXED; }
+ const char **bas_ext() const;
+ ulonglong table_flags() const
+ {
+ return (HA_FAST_KEY_READ | HA_NO_BLOBS | HA_NULL_IN_KEY |
+ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
+ HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED | HA_NO_TRANSACTIONS |
+ HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
+ }
+ ulong index_flags(uint inx, uint part, bool all_parts) const
+ {
+ return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ?
+ HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE :
+ HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
+ }
+ const key_map *keys_to_use_for_scanning() { return &btree_keys; }
+ uint max_supported_keys() const { return MAX_KEY; }
+ uint max_supported_key_part_length() const { return MAX_KEY_LENGTH; }
+ double scan_time()
+ { return (double) (stats.records+stats.deleted) / 20.0+10; }
+ double read_time(uint index, uint ranges, ha_rows rows)
+ { return (double) rows / 20.0+1; }
+
+ int open(const char *name, int mode, uint test_if_locked);
+ int close(void);
+ void set_keys_for_scanning(void);
+ int write_row(uchar * buf);
+ int update_row(const uchar * old_data, uchar * new_data);
+ int delete_row(const uchar * buf);
+ virtual void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ int index_read_map(uchar * buf, const uchar * key, key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map);
+ int index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int index_next(uchar * buf);
+ int index_prev(uchar * buf);
+ int index_first(uchar * buf);
+ int index_last(uchar * buf);
+ int rnd_init(bool scan);
+ int rnd_next(uchar *buf);
+ int rnd_pos(uchar * buf, uchar *pos);
+ void position(const uchar *record);
+ int info(uint);
+ int extra(enum ha_extra_function operation);
+ int reset();
+ int external_lock(THD *thd, int lock_type);
+ int delete_all_rows(void);
+ int reset_auto_increment(ulonglong value);
+ int disable_indexes(uint mode);
+ int enable_indexes(uint mode);
+ int indexes_are_disabled(void);
+ ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
+ int delete_table(const char *from);
+ void drop_table(const char *name);
+ int rename_table(const char * from, const char * to);
+ int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
+ void update_create_info(HA_CREATE_INFO *create_info);
+
+ THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+ int cmp_ref(const uchar *ref1, const uchar *ref2)
+ {
+ return memcmp(ref1, ref2, sizeof(HEAP_PTR));
+ }
+ bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
+private:
+ void update_key_stats();
+};
diff --git a/storage/heap/heapdef.h b/storage/heap/heapdef.h
new file mode 100644
index 00000000000..3fc94062303
--- /dev/null
+++ b/storage/heap/heapdef.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2000-2002, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* This file is included in all heap-files */
+
+#include <my_base.h> /* This includes global */
+C_MODE_START
+#ifdef THREAD
+#include <my_pthread.h>
+#endif
+#include "heap.h" /* Structs & some defines */
+#include "my_tree.h"
+
+/*
+ When allocating keys /rows in the internal block structure, do it
+ within the following boundaries.
+
+ The challenge is to find the balance between allocate as few blocks
+ as possible and keep memory consumption down.
+*/
+
+#define HP_MIN_RECORDS_IN_BLOCK 16
+#define HP_MAX_RECORDS_IN_BLOCK 8192
+
+ /* Some extern variables */
+
+extern LIST *heap_open_list,*heap_share_list;
+
+#define test_active(info) \
+if (!(info->update & HA_STATE_AKTIV))\
+{ my_errno=HA_ERR_NO_ACTIVE_RECORD; DBUG_RETURN(-1); }
+#define hp_find_hash(A,B) ((HASH_INFO*) hp_find_block((A),(B)))
+
+ /* Find pos for record and update it in info->current_ptr */
+#define hp_find_record(info,pos) (info)->current_ptr= hp_find_block(&(info)->s->block,pos)
+
+typedef struct st_hp_hash_info
+{
+ struct st_hp_hash_info *next_key;
+ uchar *ptr_to_rec;
+} HASH_INFO;
+
+typedef struct {
+ HA_KEYSEG *keyseg;
+ uint key_length;
+ uint search_flag;
+} heap_rb_param;
+
+ /* Prototypes for intern functions */
+
+extern HP_SHARE *hp_find_named_heap(const char *name);
+extern int hp_rectest(HP_INFO *info,const uchar *old);
+extern uchar *hp_find_block(HP_BLOCK *info,ulong pos);
+extern int hp_get_new_block(HP_BLOCK *info, size_t* alloc_length);
+extern void hp_free(HP_SHARE *info);
+extern uchar *hp_free_level(HP_BLOCK *block,uint level,HP_PTRS *pos,
+ uchar *last_pos);
+extern int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
+ const uchar *record, uchar *recpos);
+extern int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
+ const uchar *record, uchar *recpos);
+extern int hp_rb_delete_key(HP_INFO *info,HP_KEYDEF *keyinfo,
+ const uchar *record,uchar *recpos,int flag);
+extern int hp_delete_key(HP_INFO *info,HP_KEYDEF *keyinfo,
+ const uchar *record,uchar *recpos,int flag);
+extern HASH_INFO *_heap_find_hash(HP_BLOCK *block,ulong pos);
+extern uchar *hp_search(HP_INFO *info,HP_KEYDEF *keyinfo,const uchar *key,
+ uint nextflag);
+extern uchar *hp_search_next(HP_INFO *info, HP_KEYDEF *keyinfo,
+ const uchar *key, HASH_INFO *pos);
+extern ulong hp_hashnr(HP_KEYDEF *keyinfo,const uchar *key);
+extern ulong hp_rec_hashnr(HP_KEYDEF *keyinfo,const uchar *rec);
+extern ulong hp_mask(ulong hashnr,ulong buffmax,ulong maxlength);
+extern void hp_movelink(HASH_INFO *pos,HASH_INFO *next_link,
+ HASH_INFO *newlink);
+extern int hp_rec_key_cmp(HP_KEYDEF *keydef,const uchar *rec1,
+ const uchar *rec2,
+ my_bool diff_if_only_endspace_difference);
+extern int hp_key_cmp(HP_KEYDEF *keydef,const uchar *rec,
+ const uchar *key);
+extern void hp_make_key(HP_KEYDEF *keydef,uchar *key,const uchar *rec);
+extern uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key,
+ const uchar *rec, uchar *recpos);
+extern uint hp_rb_key_length(HP_KEYDEF *keydef, const uchar *key);
+extern uint hp_rb_null_key_length(HP_KEYDEF *keydef, const uchar *key);
+extern uint hp_rb_var_key_length(HP_KEYDEF *keydef, const uchar *key);
+extern my_bool hp_if_null_in_key(HP_KEYDEF *keyinfo, const uchar *record);
+extern int hp_close(register HP_INFO *info);
+extern void hp_clear(HP_SHARE *info);
+extern void hp_clear_keys(HP_SHARE *info);
+extern uint hp_rb_pack_key(HP_KEYDEF *keydef, uchar *key, const uchar *old,
+ key_part_map keypart_map);
+#ifdef THREAD
+extern pthread_mutex_t THR_LOCK_heap;
+#else
+#define pthread_mutex_lock(A)
+#define pthread_mutex_unlock(A)
+#endif
+C_MODE_END
diff --git a/storage/heap/hp_block.c b/storage/heap/hp_block.c
new file mode 100644
index 00000000000..c622a9e52f8
--- /dev/null
+++ b/storage/heap/hp_block.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2000-2002, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* functions on blocks; Keys and records are saved in blocks */
+
+#include "heapdef.h"
+
+/*
+ Find record according to record-position.
+
+ The record is located by factoring position number pos into (p_0, p_1, ...)
+ such that
+ pos = SUM_i(block->level_info[i].records_under_level * p_i)
+ {p_0, p_1, ...} serve as indexes to descend the blocks tree.
+*/
+
+uchar *hp_find_block(HP_BLOCK *block, ulong pos)
+{
+ reg1 int i;
+ reg3 HP_PTRS *ptr; /* block base ptr */
+
+ for (i=block->levels-1, ptr=block->root ; i > 0 ; i--)
+ {
+ ptr=(HP_PTRS*)ptr->blocks[pos/block->level_info[i].records_under_level];
+ pos%=block->level_info[i].records_under_level;
+ }
+ return (uchar*) ptr+ pos*block->recbuffer;
+}
+
+
+/*
+ Get one new block-of-records. Alloc ptr to block if needed
+
+ SYNOPSIS
+ hp_get_new_block()
+ block HP_BLOCK tree-like block
+ alloc_length OUT Amount of memory allocated from the heap
+
+ Interrupts are stopped to allow ha_panic in interrupts
+ RETURN
+ 0 OK
+ 1 Out of memory
+*/
+
+int hp_get_new_block(HP_BLOCK *block, size_t *alloc_length)
+{
+ reg1 uint i,j;
+ HP_PTRS *root;
+
+ for (i=0 ; i < block->levels ; i++)
+ if (block->level_info[i].free_ptrs_in_block)
+ break;
+
+ /*
+ Allocate space for leaf block plus space for upper level blocks up to
+ first level that has a free slot to put the pointer.
+ In some cases we actually allocate more then we need:
+ Consider e.g. a situation where we have one level 1 block and one level 0
+ block, the level 0 block is full and this function is called. We only
+ need a leaf block in this case. Nevertheless, we will get here with i=1
+ and will also allocate sizeof(HP_PTRS) for non-leaf block and will never
+ use this space.
+ This doesn't add much overhead - with current values of sizeof(HP_PTRS)
+ and my_default_record_cache_size we get about 1/128 unused memory.
+ */
+ *alloc_length=sizeof(HP_PTRS)*i+block->records_in_block* block->recbuffer;
+ if (!(root=(HP_PTRS*) my_malloc(*alloc_length,MYF(MY_WME))))
+ return 1;
+
+ if (i == 0)
+ {
+ block->levels=1;
+ block->root=block->level_info[0].last_blocks=root;
+ }
+ else
+ {
+ dont_break(); /* Dont allow SIGHUP or SIGINT */
+ if ((uint) i == block->levels)
+ {
+ /* Adding a new level on top of the existing ones. */
+ block->levels=i+1;
+ /*
+ Use first allocated HP_PTRS as a top-level block. Put the current
+ block tree into the first slot of a new top-level block.
+ */
+ block->level_info[i].free_ptrs_in_block=HP_PTRS_IN_NOD-1;
+ ((HP_PTRS**) root)[0]= block->root;
+ block->root=block->level_info[i].last_blocks= root++;
+ }
+ /* Occupy the free slot we've found at level i */
+ block->level_info[i].last_blocks->
+ blocks[HP_PTRS_IN_NOD - block->level_info[i].free_ptrs_in_block--]=
+ (uchar*) root;
+
+ /* Add a block subtree with each node having one left-most child */
+ for (j=i-1 ; j >0 ; j--)
+ {
+ block->level_info[j].last_blocks= root++;
+ block->level_info[j].last_blocks->blocks[0]=(uchar*) root;
+ block->level_info[j].free_ptrs_in_block=HP_PTRS_IN_NOD-1;
+ }
+
+ /*
+ root now points to last (block->records_in_block* block->recbuffer)
+ allocated bytes. Use it as a leaf block.
+ */
+ block->level_info[0].last_blocks= root;
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ }
+ return 0;
+}
+
+
+ /* free all blocks under level */
+
+uchar *hp_free_level(HP_BLOCK *block, uint level, HP_PTRS *pos, uchar *last_pos)
+{
+ int i,max_pos;
+ uchar *next_ptr;
+
+ if (level == 1)
+ next_ptr=(uchar*) pos+block->recbuffer;
+ else
+ {
+ max_pos= (block->level_info[level-1].last_blocks == pos) ?
+ HP_PTRS_IN_NOD - block->level_info[level-1].free_ptrs_in_block :
+ HP_PTRS_IN_NOD;
+
+ next_ptr=(uchar*) (pos+1);
+ for (i=0 ; i < max_pos ; i++)
+ next_ptr=hp_free_level(block,level-1,
+ (HP_PTRS*) pos->blocks[i],next_ptr);
+ }
+ if ((uchar*) pos != last_pos)
+ {
+ my_free((uchar*) pos,MYF(0));
+ return last_pos;
+ }
+ return next_ptr; /* next memory position */
+}
diff --git a/storage/heap/hp_clear.c b/storage/heap/hp_clear.c
new file mode 100644
index 00000000000..babfcbd6f41
--- /dev/null
+++ b/storage/heap/hp_clear.c
@@ -0,0 +1,194 @@
+/* Copyright (C) 2000-2002, 2004, 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ remove all records from database
+ Identical as hp_create() and hp_open() but used HP_SHARE* instead of name and
+ database remains open.
+*/
+
+#include "heapdef.h"
+
+void heap_clear(HP_INFO *info)
+{
+ hp_clear(info->s);
+}
+
+void hp_clear(HP_SHARE *info)
+{
+ DBUG_ENTER("hp_clear");
+
+ if (info->block.levels)
+ VOID(hp_free_level(&info->block,info->block.levels,info->block.root,
+ (uchar*) 0));
+ info->block.levels=0;
+ hp_clear_keys(info);
+ info->records= info->deleted= 0;
+ info->data_length= 0;
+ info->blength=1;
+ info->changed=0;
+ info->del_link=0;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Clear all keys.
+
+ SYNOPSIS
+ heap_clear_keys()
+ info A pointer to the heap storage engine HP_INFO struct.
+
+ DESCRIPTION
+ Delete all trees of all indexes and leave them empty.
+
+ RETURN
+ void
+*/
+
+void heap_clear_keys(HP_INFO *info)
+{
+ hp_clear(info->s);
+}
+
+
+/*
+ Clear all keys.
+
+ SYNOPSIS
+ hp_clear_keys()
+ info A pointer to the heap storage engine HP_SHARE struct.
+
+ DESCRIPTION
+ Delete all trees of all indexes and leave them empty.
+
+ RETURN
+ void
+*/
+
+void hp_clear_keys(HP_SHARE *info)
+{
+ uint key;
+ DBUG_ENTER("hp_clear_keys");
+
+ for (key=0 ; key < info->keys ; key++)
+ {
+ HP_KEYDEF *keyinfo = info->keydef + key;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ delete_tree(&keyinfo->rb_tree);
+ }
+ else
+ {
+ HP_BLOCK *block= &keyinfo->block;
+ if (block->levels)
+ VOID(hp_free_level(block,block->levels,block->root,(uchar*) 0));
+ block->levels=0;
+ block->last_allocated=0;
+ keyinfo->hash_buckets= 0;
+ }
+ }
+ info->index_length=0;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Disable all indexes.
+
+ SYNOPSIS
+ heap_disable_indexes()
+ info A pointer to the heap storage engine HP_INFO struct.
+
+ DESCRIPTION
+ Disable and clear (remove contents of) all indexes.
+
+ RETURN
+ 0 ok
+*/
+
+int heap_disable_indexes(HP_INFO *info)
+{
+ HP_SHARE *share= info->s;
+
+ if (share->keys)
+ {
+ hp_clear_keys(share);
+ share->currently_disabled_keys= share->keys;
+ share->keys= 0;
+ }
+ return 0;
+}
+
+
+/*
+ Enable all indexes
+
+ SYNOPSIS
+ heap_enable_indexes()
+ info A pointer to the heap storage engine HP_INFO struct.
+
+ DESCRIPTION
+ Enable all indexes. The indexes might have been disabled
+ by heap_disable_index() before.
+ The function works only if both data and indexes are empty,
+ since the heap storage engine cannot repair the indexes.
+ To be sure, call handler::delete_all_rows() before.
+
+ RETURN
+ 0 ok
+ HA_ERR_CRASHED data or index is non-empty.
+*/
+
+int heap_enable_indexes(HP_INFO *info)
+{
+ int error= 0;
+ HP_SHARE *share= info->s;
+
+ if (share->data_length || share->index_length)
+ error= HA_ERR_CRASHED;
+ else
+ if (share->currently_disabled_keys)
+ {
+ share->keys= share->currently_disabled_keys;
+ share->currently_disabled_keys= 0;
+ }
+ return error;
+}
+
+
+/*
+ Test if indexes are disabled.
+
+ SYNOPSIS
+ heap_indexes_are_disabled()
+ info A pointer to the heap storage engine HP_INFO struct.
+
+ DESCRIPTION
+ Test if indexes are disabled.
+
+ RETURN
+ 0 indexes are not disabled
+ 1 all indexes are disabled
+ [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
+*/
+
+int heap_indexes_are_disabled(HP_INFO *info)
+{
+ HP_SHARE *share= info->s;
+
+ return (! share->keys && share->currently_disabled_keys);
+}
+
diff --git a/storage/heap/hp_close.c b/storage/heap/hp_close.c
new file mode 100644
index 00000000000..d571815980c
--- /dev/null
+++ b/storage/heap/hp_close.c
@@ -0,0 +1,51 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* close a heap-database */
+
+#include "heapdef.h"
+
+ /* Close a database open by hp_open() */
+ /* Data is normally not deallocated */
+
+int heap_close(HP_INFO *info)
+{
+ int tmp;
+ DBUG_ENTER("heap_close");
+ pthread_mutex_lock(&THR_LOCK_heap);
+ tmp= hp_close(info);
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(tmp);
+}
+
+
+int hp_close(register HP_INFO *info)
+{
+ int error=0;
+ DBUG_ENTER("hp_close");
+#ifndef DBUG_OFF
+ if (info->s->changed && heap_check_heap(info,0))
+ {
+ error=my_errno=HA_ERR_CRASHED;
+ }
+#endif
+ info->s->changed=0;
+ if (info->open_list.data)
+ heap_open_list=list_delete(heap_open_list,&info->open_list);
+ if (!--info->s->open_count && info->s->delete_on_close)
+ hp_free(info->s); /* Table was deleted */
+ my_free((uchar*) info,MYF(0));
+ DBUG_RETURN(error);
+}
diff --git a/storage/heap/hp_create.c b/storage/heap/hp_create.c
new file mode 100644
index 00000000000..b6814fc1614
--- /dev/null
+++ b/storage/heap/hp_create.c
@@ -0,0 +1,306 @@
+/* Copyright (C) 2000-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2);
+static void init_block(HP_BLOCK *block,uint reclength,ulong min_records,
+ ulong max_records);
+
+/* Create a heap table */
+
+int heap_create(const char *name, uint keys, HP_KEYDEF *keydef,
+ uint reclength, ulong max_records, ulong min_records,
+ HP_CREATE_INFO *create_info, HP_SHARE **res)
+{
+ uint i, j, key_segs, max_length, length;
+ HP_SHARE *share= 0;
+ HA_KEYSEG *keyseg;
+ DBUG_ENTER("heap_create");
+
+ if (!create_info->internal_table)
+ {
+ pthread_mutex_lock(&THR_LOCK_heap);
+ if ((share= hp_find_named_heap(name)) && share->open_count == 0)
+ {
+ hp_free(share);
+ share= 0;
+ }
+ }
+
+ if (!share)
+ {
+ HP_KEYDEF *keyinfo;
+ DBUG_PRINT("info",("Initializing new table"));
+
+ /*
+ We have to store sometimes uchar* del_link in records,
+ so the record length should be at least sizeof(uchar*)
+ */
+ set_if_bigger(reclength, sizeof (uchar*));
+
+ for (i= key_segs= max_length= 0, keyinfo= keydef; i < keys; i++, keyinfo++)
+ {
+ bzero((char*) &keyinfo->block,sizeof(keyinfo->block));
+ bzero((char*) &keyinfo->rb_tree ,sizeof(keyinfo->rb_tree));
+ for (j= length= 0; j < keyinfo->keysegs; j++)
+ {
+ length+= keyinfo->seg[j].length;
+ if (keyinfo->seg[j].null_bit)
+ {
+ length++;
+ if (!(keyinfo->flag & HA_NULL_ARE_EQUAL))
+ keyinfo->flag|= HA_NULL_PART_KEY;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ keyinfo->rb_tree.size_of_element++;
+ }
+ switch (keyinfo->seg[j].type) {
+ case HA_KEYTYPE_SHORT_INT:
+ case HA_KEYTYPE_LONG_INT:
+ case HA_KEYTYPE_FLOAT:
+ case HA_KEYTYPE_DOUBLE:
+ case HA_KEYTYPE_USHORT_INT:
+ case HA_KEYTYPE_ULONG_INT:
+ case HA_KEYTYPE_LONGLONG:
+ case HA_KEYTYPE_ULONGLONG:
+ case HA_KEYTYPE_INT24:
+ case HA_KEYTYPE_UINT24:
+ case HA_KEYTYPE_INT8:
+ keyinfo->seg[j].flag|= HA_SWAP_KEY;
+ break;
+ case HA_KEYTYPE_VARBINARY1:
+ /* Case-insensitiveness is handled in coll->hash_sort */
+ keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
+ /* fall_through */
+ case HA_KEYTYPE_VARTEXT1:
+ if (!my_binary_compare(keyinfo->seg[j].charset))
+ keyinfo->flag|= HA_END_SPACE_KEY;
+ keyinfo->flag|= HA_VAR_LENGTH_KEY;
+ length+= 2;
+ /* Save number of bytes used to store length */
+ keyinfo->seg[j].bit_start= 1;
+ break;
+ case HA_KEYTYPE_VARBINARY2:
+ /* Case-insensitiveness is handled in coll->hash_sort */
+ /* fall_through */
+ case HA_KEYTYPE_VARTEXT2:
+ if (!my_binary_compare(keyinfo->seg[j].charset))
+ keyinfo->flag|= HA_END_SPACE_KEY;
+ keyinfo->flag|= HA_VAR_LENGTH_KEY;
+ length+= 2;
+ /* Save number of bytes used to store length */
+ keyinfo->seg[j].bit_start= 2;
+ /*
+ Make future comparison simpler by only having to check for
+ one type
+ */
+ keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
+ break;
+ default:
+ break;
+ }
+ if (keyinfo->seg[j].flag & HA_END_SPACE_ARE_EQUAL)
+ keyinfo->flag|= HA_END_SPACE_KEY;
+ }
+ keyinfo->length= length;
+ length+= keyinfo->rb_tree.size_of_element +
+ ((keyinfo->algorithm == HA_KEY_ALG_BTREE) ? sizeof(uchar*) : 0);
+ if (length > max_length)
+ max_length= length;
+ key_segs+= keyinfo->keysegs;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ key_segs++; /* additional HA_KEYTYPE_END segment */
+ if (keyinfo->flag & HA_VAR_LENGTH_KEY)
+ keyinfo->get_key_length= hp_rb_var_key_length;
+ else if (keyinfo->flag & HA_NULL_PART_KEY)
+ keyinfo->get_key_length= hp_rb_null_key_length;
+ else
+ keyinfo->get_key_length= hp_rb_key_length;
+ }
+ }
+ if (!(share= (HP_SHARE*) my_malloc((uint) sizeof(HP_SHARE)+
+ keys*sizeof(HP_KEYDEF)+
+ key_segs*sizeof(HA_KEYSEG),
+ MYF(MY_ZEROFILL))))
+ goto err;
+ share->keydef= (HP_KEYDEF*) (share + 1);
+ share->key_stat_version= 1;
+ keyseg= (HA_KEYSEG*) (share->keydef + keys);
+ init_block(&share->block, reclength + 1, min_records, max_records);
+ /* Fix keys */
+ memcpy(share->keydef, keydef, (size_t) (sizeof(keydef[0]) * keys));
+ for (i= 0, keyinfo= share->keydef; i < keys; i++, keyinfo++)
+ {
+ keyinfo->seg= keyseg;
+ memcpy(keyseg, keydef[i].seg,
+ (size_t) (sizeof(keyseg[0]) * keydef[i].keysegs));
+ keyseg+= keydef[i].keysegs;
+
+ if (keydef[i].algorithm == HA_KEY_ALG_BTREE)
+ {
+ /* additional HA_KEYTYPE_END keyseg */
+ keyseg->type= HA_KEYTYPE_END;
+ keyseg->length= sizeof(uchar*);
+ keyseg->flag= 0;
+ keyseg->null_bit= 0;
+ keyseg++;
+
+ init_tree(&keyinfo->rb_tree, 0, 0, sizeof(uchar*),
+ (qsort_cmp2)keys_compare, 1, NULL, NULL);
+ keyinfo->delete_key= hp_rb_delete_key;
+ keyinfo->write_key= hp_rb_write_key;
+ }
+ else
+ {
+ init_block(&keyinfo->block, sizeof(HASH_INFO), min_records,
+ max_records);
+ keyinfo->delete_key= hp_delete_key;
+ keyinfo->write_key= hp_write_key;
+ keyinfo->hash_buckets= 0;
+ }
+ if ((keyinfo->flag & HA_AUTO_KEY) && create_info->with_auto_increment)
+ share->auto_key= i + 1;
+ }
+ share->min_records= min_records;
+ share->max_records= max_records;
+ share->max_table_size= create_info->max_table_size;
+ share->data_length= share->index_length= 0;
+ share->reclength= reclength;
+ share->blength= 1;
+ share->keys= keys;
+ share->max_key_length= max_length;
+ share->changed= 0;
+ share->auto_key= create_info->auto_key;
+ share->auto_key_type= create_info->auto_key_type;
+ share->auto_increment= create_info->auto_increment;
+ /* Must be allocated separately for rename to work */
+ if (!(share->name= my_strdup(name,MYF(0))))
+ {
+ my_free((uchar*) share,MYF(0));
+ goto err;
+ }
+#ifdef THREAD
+ thr_lock_init(&share->lock);
+ VOID(pthread_mutex_init(&share->intern_lock,MY_MUTEX_INIT_FAST));
+#endif
+ if (!create_info->internal_table)
+ {
+ share->open_list.data= (void*) share;
+ heap_share_list= list_add(heap_share_list,&share->open_list);
+ }
+ else
+ share->delete_on_close= 1;
+ }
+ if (!create_info->internal_table)
+ pthread_mutex_unlock(&THR_LOCK_heap);
+
+ *res= share;
+ DBUG_RETURN(0);
+
+err:
+ if (!create_info->internal_table)
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(1);
+} /* heap_create */
+
+
+static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2)
+{
+ uint not_used[2];
+ return ha_key_cmp(param->keyseg, key1, key2, param->key_length,
+ param->search_flag, not_used);
+}
+
+static void init_block(HP_BLOCK *block, uint reclength, ulong min_records,
+ ulong max_records)
+{
+ uint i,recbuffer,records_in_block;
+
+ max_records= max(min_records,max_records);
+ if (!max_records)
+ max_records= 1000; /* As good as quess as anything */
+ recbuffer= (uint) (reclength + sizeof(uchar**) - 1) & ~(sizeof(uchar**) - 1);
+ records_in_block= max_records / 10;
+ if (records_in_block < 10 && max_records)
+ records_in_block= 10;
+ if (!records_in_block || records_in_block*recbuffer >
+ (my_default_record_cache_size-sizeof(HP_PTRS)*HP_MAX_LEVELS))
+ records_in_block= (my_default_record_cache_size - sizeof(HP_PTRS) *
+ HP_MAX_LEVELS) / recbuffer + 1;
+ block->records_in_block= records_in_block;
+ block->recbuffer= recbuffer;
+ block->last_allocated= 0L;
+
+ for (i= 0; i <= HP_MAX_LEVELS; i++)
+ block->level_info[i].records_under_level=
+ (!i ? 1 : i == 1 ? records_in_block :
+ HP_PTRS_IN_NOD * block->level_info[i - 1].records_under_level);
+}
+
+
+static inline void heap_try_free(HP_SHARE *share)
+{
+ if (share->open_count == 0)
+ hp_free(share);
+ else
+ share->delete_on_close= 1;
+}
+
+
+int heap_delete_table(const char *name)
+{
+ int result;
+ reg1 HP_SHARE *share;
+ DBUG_ENTER("heap_delete_table");
+
+ pthread_mutex_lock(&THR_LOCK_heap);
+ if ((share= hp_find_named_heap(name)))
+ {
+ heap_try_free(share);
+ result= 0;
+ }
+ else
+ {
+ result= my_errno=ENOENT;
+ }
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(result);
+}
+
+
+void heap_drop_table(HP_INFO *info)
+{
+ DBUG_ENTER("heap_drop_table");
+ pthread_mutex_lock(&THR_LOCK_heap);
+ heap_try_free(info->s);
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_VOID_RETURN;
+}
+
+
+void hp_free(HP_SHARE *share)
+{
+ if (share->open_list.data) /* If not internal table */
+ heap_share_list= list_delete(heap_share_list, &share->open_list);
+ hp_clear(share); /* Remove blocks from memory */
+#ifdef THREAD
+ thr_lock_delete(&share->lock);
+ VOID(pthread_mutex_destroy(&share->intern_lock));
+#endif
+ my_free((uchar*) share->name, MYF(0));
+ my_free((uchar*) share, MYF(0));
+ return;
+}
diff --git a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c
new file mode 100644
index 00000000000..ceba0fcf12e
--- /dev/null
+++ b/storage/heap/hp_delete.c
@@ -0,0 +1,202 @@
+/* Copyright (C) 2000-2002, 2004-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* remove current record in heap-database */
+
+#include "heapdef.h"
+
+int heap_delete(HP_INFO *info, const uchar *record)
+{
+ uchar *pos;
+ HP_SHARE *share=info->s;
+ HP_KEYDEF *keydef, *end, *p_lastinx;
+ DBUG_ENTER("heap_delete");
+ DBUG_PRINT("enter",("info: 0x%lx record: 0x%lx", (long) info, (long) record));
+
+ test_active(info);
+
+ if (info->opt_flag & READ_CHECK_USED && hp_rectest(info,record))
+ DBUG_RETURN(my_errno); /* Record changed */
+ share->changed=1;
+
+ if ( --(share->records) < share->blength >> 1) share->blength>>=1;
+ pos=info->current_ptr;
+
+ p_lastinx = share->keydef + info->lastinx;
+ for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
+ keydef++)
+ {
+ if ((*keydef->delete_key)(info, keydef, record, pos, keydef == p_lastinx))
+ goto err;
+ }
+
+ info->update=HA_STATE_DELETED;
+ *((uchar**) pos)=share->del_link;
+ share->del_link=pos;
+ pos[share->reclength]=0; /* Record deleted */
+ share->deleted++;
+ info->current_hash_ptr=0;
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
+ DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
+#endif
+
+ DBUG_RETURN(0);
+err:
+ if (++(share->records) == share->blength)
+ share->blength+= share->blength;
+ DBUG_RETURN(my_errno);
+}
+
+
+/*
+ Remove one key from rb-tree
+*/
+
+int hp_rb_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo,
+ const uchar *record, uchar *recpos, int flag)
+{
+ heap_rb_param custom_arg;
+ uint old_allocated;
+ int res;
+
+ if (flag)
+ info->last_pos= NULL; /* For heap_rnext/heap_rprev */
+
+ custom_arg.keyseg= keyinfo->seg;
+ custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
+ custom_arg.search_flag= SEARCH_SAME;
+ old_allocated= keyinfo->rb_tree.allocated;
+ res= tree_delete(&keyinfo->rb_tree, info->recbuf, custom_arg.key_length,
+ &custom_arg);
+ info->s->index_length-= (old_allocated - keyinfo->rb_tree.allocated);
+ return res;
+}
+
+
+/*
+ Remove one key from hash-table
+
+ SYNPOSIS
+ hp_delete_key()
+ info Hash handler
+ keyinfo key definition of key that we want to delete
+ record row data to be deleted
+ recpos Pointer to heap record in memory
+ flag Is set if we want's to correct info->current_ptr
+
+ RETURN
+ 0 Ok
+ other Error code
+*/
+
+int hp_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo,
+ const uchar *record, uchar *recpos, int flag)
+{
+ ulong blength,pos2,pos_hashnr,lastpos_hashnr;
+ HASH_INFO *lastpos,*gpos,*pos,*pos3,*empty,*last_ptr;
+ HP_SHARE *share=info->s;
+ DBUG_ENTER("hp_delete_key");
+
+ blength=share->blength;
+ if (share->records+1 == blength)
+ blength+= blength;
+ lastpos=hp_find_hash(&keyinfo->block,share->records);
+ last_ptr=0;
+
+ /* Search after record with key */
+ pos= hp_find_hash(&keyinfo->block,
+ hp_mask(hp_rec_hashnr(keyinfo, record), blength,
+ share->records + 1));
+ gpos = pos3 = 0;
+
+ while (pos->ptr_to_rec != recpos)
+ {
+ if (flag && !hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 0))
+ last_ptr=pos; /* Previous same key */
+ gpos=pos;
+ if (!(pos=pos->next_key))
+ {
+ DBUG_RETURN(my_errno=HA_ERR_CRASHED); /* This shouldn't happend */
+ }
+ }
+
+ /* Remove link to record */
+
+ if (flag)
+ {
+ /* Save for heap_rnext/heap_rprev */
+ info->current_hash_ptr=last_ptr;
+ info->current_ptr = last_ptr ? last_ptr->ptr_to_rec : 0;
+ DBUG_PRINT("info",("Corrected current_ptr to point at: 0x%lx",
+ (long) info->current_ptr));
+ }
+ empty=pos;
+ if (gpos)
+ gpos->next_key=pos->next_key; /* unlink current ptr */
+ else if (pos->next_key)
+ {
+ empty=pos->next_key;
+ pos->ptr_to_rec=empty->ptr_to_rec;
+ pos->next_key=empty->next_key;
+ }
+ else
+ keyinfo->hash_buckets--;
+
+ if (empty == lastpos) /* deleted last hash key */
+ DBUG_RETURN (0);
+
+ /* Move the last key (lastpos) */
+ lastpos_hashnr = hp_rec_hashnr(keyinfo, lastpos->ptr_to_rec);
+ /* pos is where lastpos should be */
+ pos=hp_find_hash(&keyinfo->block, hp_mask(lastpos_hashnr, share->blength,
+ share->records));
+ if (pos == empty) /* Move to empty position. */
+ {
+ empty[0]=lastpos[0];
+ DBUG_RETURN(0);
+ }
+ pos_hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
+ /* pos3 is where the pos should be */
+ pos3= hp_find_hash(&keyinfo->block,
+ hp_mask(pos_hashnr, share->blength, share->records));
+ if (pos != pos3)
+ { /* pos is on wrong posit */
+ empty[0]=pos[0]; /* Save it here */
+ pos[0]=lastpos[0]; /* This shold be here */
+ hp_movelink(pos, pos3, empty); /* Fix link to pos */
+ DBUG_RETURN(0);
+ }
+ pos2= hp_mask(lastpos_hashnr, blength, share->records + 1);
+ if (pos2 == hp_mask(pos_hashnr, blength, share->records + 1))
+ { /* Identical key-positions */
+ if (pos2 != share->records)
+ {
+ empty[0]=lastpos[0];
+ hp_movelink(lastpos, pos, empty);
+ DBUG_RETURN(0);
+ }
+ pos3= pos; /* Link pos->next after lastpos */
+ }
+ else
+ {
+ pos3= 0; /* Different positions merge */
+ keyinfo->hash_buckets--;
+ }
+
+ empty[0]=lastpos[0];
+ hp_movelink(pos3, empty, pos->next_key);
+ pos->next_key=empty;
+ DBUG_RETURN(0);
+}
diff --git a/storage/heap/hp_extra.c b/storage/heap/hp_extra.c
new file mode 100644
index 00000000000..9ff6bfb860a
--- /dev/null
+++ b/storage/heap/hp_extra.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2000-2001, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Extra functions we want to do with a database */
+/* - Set flags for quicker databasehandler */
+/* - Set databasehandler to normal */
+/* - Reset recordpointers as after open database */
+
+#include "heapdef.h"
+
+static void heap_extra_keyflag(register HP_INFO *info,
+ enum ha_extra_function function);
+
+
+ /* set extra flags for database */
+
+int heap_extra(register HP_INFO *info, enum ha_extra_function function)
+{
+ DBUG_ENTER("heap_extra");
+
+ switch (function) {
+ case HA_EXTRA_RESET_STATE:
+ heap_reset(info);
+ case HA_EXTRA_NO_READCHECK:
+ info->opt_flag&= ~READ_CHECK_USED; /* No readcheck */
+ break;
+ case HA_EXTRA_READCHECK:
+ info->opt_flag|= READ_CHECK_USED;
+ break;
+ case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
+ case HA_EXTRA_CHANGE_KEY_TO_DUP:
+ heap_extra_keyflag(info, function);
+ break;
+ default:
+ break;
+ }
+ DBUG_RETURN(0);
+} /* heap_extra */
+
+
+int heap_reset(HP_INFO *info)
+{
+ info->lastinx= -1;
+ info->current_record= (ulong) ~0L;
+ info->current_hash_ptr=0;
+ info->update=0;
+ info->next_block=0;
+ return 0;
+}
+
+
+/*
+ Start/Stop Inserting Duplicates Into a Table, WL#1648.
+ */
+static void heap_extra_keyflag(register HP_INFO *info,
+ enum ha_extra_function function)
+{
+ uint idx;
+
+ for (idx= 0; idx< info->s->keys; idx++)
+ {
+ switch (function) {
+ case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
+ info->s->keydef[idx].flag|= HA_NOSAME;
+ break;
+ case HA_EXTRA_CHANGE_KEY_TO_DUP:
+ info->s->keydef[idx].flag&= ~(HA_NOSAME);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/storage/heap/hp_hash.c b/storage/heap/hp_hash.c
new file mode 100644
index 00000000000..f56df42aab3
--- /dev/null
+++ b/storage/heap/hp_hash.c
@@ -0,0 +1,998 @@
+/* Copyright (C) 2000-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* The hash functions used for saveing keys */
+
+#include "heapdef.h"
+#include <m_ctype.h>
+
+
+
+/*
+ Find out how many rows there is in the given range
+
+ SYNOPSIS
+ hp_rb_records_in_range()
+ info HEAP handler
+ inx Index to use
+ min_key Min key. Is = 0 if no min range
+ max_key Max key. Is = 0 if no max range
+
+ NOTES
+ min_key.flag can have one of the following values:
+ HA_READ_KEY_EXACT Include the key in the range
+ HA_READ_AFTER_KEY Don't include key in range
+
+ max_key.flag can have one of the following values:
+ HA_READ_BEFORE_KEY Don't include key in range
+ HA_READ_AFTER_KEY Include all 'end_key' values in the range
+
+ RETURN
+ HA_POS_ERROR Something is wrong with the index tree.
+ 0 There is no matching keys in the given range
+ number > 0 There is approximately 'number' matching rows in
+ the range.
+*/
+
+ha_rows hp_rb_records_in_range(HP_INFO *info, int inx, key_range *min_key,
+ key_range *max_key)
+{
+ ha_rows start_pos, end_pos;
+ HP_KEYDEF *keyinfo= info->s->keydef + inx;
+ TREE *rb_tree = &keyinfo->rb_tree;
+ heap_rb_param custom_arg;
+ DBUG_ENTER("hp_rb_records_in_range");
+
+ info->lastinx= inx;
+ custom_arg.keyseg= keyinfo->seg;
+ custom_arg.search_flag= SEARCH_FIND | SEARCH_SAME;
+ if (min_key)
+ {
+ custom_arg.key_length= hp_rb_pack_key(keyinfo, (uchar*) info->recbuf,
+ (uchar*) min_key->key,
+ min_key->keypart_map);
+ start_pos= tree_record_pos(rb_tree, info->recbuf, min_key->flag,
+ &custom_arg);
+ }
+ else
+ {
+ start_pos= 0;
+ }
+
+ if (max_key)
+ {
+ custom_arg.key_length= hp_rb_pack_key(keyinfo, (uchar*) info->recbuf,
+ (uchar*) max_key->key,
+ max_key->keypart_map);
+ end_pos= tree_record_pos(rb_tree, info->recbuf, max_key->flag,
+ &custom_arg);
+ }
+ else
+ {
+ end_pos= rb_tree->elements_in_tree + (ha_rows)1;
+ }
+
+ DBUG_PRINT("info",("start_pos: %lu end_pos: %lu", (ulong) start_pos,
+ (ulong) end_pos));
+ if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR)
+ DBUG_RETURN(HA_POS_ERROR);
+ DBUG_RETURN(end_pos < start_pos ? (ha_rows) 0 :
+ (end_pos == start_pos ? (ha_rows) 1 : end_pos - start_pos));
+}
+
+
+ /* Search after a record based on a key */
+ /* Sets info->current_ptr to found record */
+ /* next_flag: Search=0, next=1, prev =2, same =3 */
+
+uchar *hp_search(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key,
+ uint nextflag)
+{
+ reg1 HASH_INFO *pos,*prev_ptr;
+ int flag;
+ uint old_nextflag;
+ HP_SHARE *share=info->s;
+ DBUG_ENTER("hp_search");
+ old_nextflag=nextflag;
+ flag=1;
+ prev_ptr=0;
+
+ if (share->records)
+ {
+ pos=hp_find_hash(&keyinfo->block, hp_mask(hp_hashnr(keyinfo, key),
+ share->blength, share->records));
+ do
+ {
+ if (!hp_key_cmp(keyinfo, pos->ptr_to_rec, key))
+ {
+ switch (nextflag) {
+ case 0: /* Search after key */
+ DBUG_PRINT("exit", ("found key at 0x%lx", (long) pos->ptr_to_rec));
+ info->current_hash_ptr=pos;
+ DBUG_RETURN(info->current_ptr= pos->ptr_to_rec);
+ case 1: /* Search next */
+ if (pos->ptr_to_rec == info->current_ptr)
+ nextflag=0;
+ break;
+ case 2: /* Search previous */
+ if (pos->ptr_to_rec == info->current_ptr)
+ {
+ my_errno=HA_ERR_KEY_NOT_FOUND; /* If gpos == 0 */
+ info->current_hash_ptr=prev_ptr;
+ DBUG_RETURN(info->current_ptr=prev_ptr ? prev_ptr->ptr_to_rec : 0);
+ }
+ prev_ptr=pos; /* Prev. record found */
+ break;
+ case 3: /* Search same */
+ if (pos->ptr_to_rec == info->current_ptr)
+ {
+ info->current_hash_ptr=pos;
+ DBUG_RETURN(info->current_ptr);
+ }
+ }
+ }
+ if (flag)
+ {
+ flag=0; /* Reset flag */
+ if (hp_find_hash(&keyinfo->block,
+ hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
+ share->blength, share->records)) != pos)
+ break; /* Wrong link */
+ }
+ }
+ while ((pos=pos->next_key));
+ }
+ my_errno=HA_ERR_KEY_NOT_FOUND;
+ if (nextflag == 2 && ! info->current_ptr)
+ {
+ /* Do a previous from end */
+ info->current_hash_ptr=prev_ptr;
+ DBUG_RETURN(info->current_ptr=prev_ptr ? prev_ptr->ptr_to_rec : 0);
+ }
+
+ if (old_nextflag && nextflag)
+ my_errno=HA_ERR_RECORD_CHANGED; /* Didn't find old record */
+ DBUG_PRINT("exit",("Error: %d",my_errno));
+ info->current_hash_ptr=0;
+ DBUG_RETURN((info->current_ptr= 0));
+}
+
+
+/*
+ Search next after last read; Assumes that the table hasn't changed
+ since last read !
+*/
+
+uchar *hp_search_next(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key,
+ HASH_INFO *pos)
+{
+ DBUG_ENTER("hp_search_next");
+
+ while ((pos= pos->next_key))
+ {
+ if (! hp_key_cmp(keyinfo, pos->ptr_to_rec, key))
+ {
+ info->current_hash_ptr=pos;
+ DBUG_RETURN (info->current_ptr= pos->ptr_to_rec);
+ }
+ }
+ my_errno=HA_ERR_KEY_NOT_FOUND;
+ DBUG_PRINT("exit",("Error: %d",my_errno));
+ info->current_hash_ptr=0;
+ DBUG_RETURN ((info->current_ptr= 0));
+}
+
+
+/*
+ Calculate position number for hash value.
+ SYNOPSIS
+ hp_mask()
+ hashnr Hash value
+ buffmax Value such that
+ 2^(n-1) < maxlength <= 2^n = buffmax
+ maxlength
+
+ RETURN
+ Array index, in [0..maxlength)
+*/
+
+ulong hp_mask(ulong hashnr, ulong buffmax, ulong maxlength)
+{
+ if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
+ return (hashnr & ((buffmax >> 1) -1));
+}
+
+
+/*
+ Change
+ next_link -> ... -> X -> pos
+ to
+ next_link -> ... -> X -> newlink
+*/
+
+void hp_movelink(HASH_INFO *pos, HASH_INFO *next_link, HASH_INFO *newlink)
+{
+ HASH_INFO *old_link;
+ do
+ {
+ old_link=next_link;
+ }
+ while ((next_link=next_link->next_key) != pos);
+ old_link->next_key=newlink;
+ return;
+}
+
+#ifndef NEW_HASH_FUNCTION
+
+ /* Calc hashvalue for a key */
+
+ulong hp_hashnr(register HP_KEYDEF *keydef, register const uchar *key)
+{
+ /*register*/
+ ulong nr=1, nr2=4;
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ uchar *pos=(uchar*) key;
+ key+=seg->length;
+ if (seg->null_bit)
+ {
+ key++; /* Skip null byte */
+ if (*pos) /* Found null */
+ {
+ nr^= (nr << 1) | 1;
+ /* Add key pack length (2) to key for VARCHAR segments */
+ if (seg->type == HA_KEYTYPE_VARTEXT1)
+ key+= 2;
+ continue;
+ }
+ pos++;
+ }
+ if (seg->type == HA_KEYTYPE_TEXT)
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint length= seg->length;
+ if (cs->mbmaxlen > 1)
+ {
+ uint char_length;
+ char_length= my_charpos(cs, pos, pos + length, length/cs->mbmaxlen);
+ set_if_smaller(length, char_length);
+ }
+ cs->coll->hash_sort(cs, pos, length, &nr, &nr2);
+ }
+ else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint pack_length= 2; /* Key packing is constant */
+ uint length= uint2korr(pos);
+ if (cs->mbmaxlen > 1)
+ {
+ uint char_length;
+ char_length= my_charpos(cs, pos +pack_length,
+ pos +pack_length + length,
+ seg->length/cs->mbmaxlen);
+ set_if_smaller(length, char_length);
+ }
+ cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2);
+ key+= pack_length;
+ }
+ else
+ {
+ for (; pos < (uchar*) key ; pos++)
+ {
+ nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) *pos)) + (nr << 8);
+ nr2+=3;
+ }
+ }
+ }
+ DBUG_PRINT("exit", ("hash: 0x%lx", nr));
+ return((ulong) nr);
+}
+
+ /* Calc hashvalue for a key in a record */
+
+ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec)
+{
+ ulong nr=1, nr2=4;
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ uchar *pos=(uchar*) rec+seg->start,*end=pos+seg->length;
+ if (seg->null_bit)
+ {
+ if (rec[seg->null_pos] & seg->null_bit)
+ {
+ nr^= (nr << 1) | 1;
+ continue;
+ }
+ }
+ if (seg->type == HA_KEYTYPE_TEXT)
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint char_length= seg->length;
+ if (cs->mbmaxlen > 1)
+ {
+ char_length= my_charpos(cs, pos, pos + char_length,
+ char_length / cs->mbmaxlen);
+ set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */
+ }
+ cs->coll->hash_sort(cs, pos, char_length, &nr, &nr2);
+ }
+ else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint pack_length= seg->bit_start;
+ uint length= (pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos));
+ if (cs->mbmaxlen > 1)
+ {
+ uint char_length;
+ char_length= my_charpos(cs, pos + pack_length,
+ pos + pack_length + length,
+ seg->length/cs->mbmaxlen);
+ set_if_smaller(length, char_length);
+ }
+ cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2);
+ }
+ else
+ {
+ for (; pos < end ; pos++)
+ {
+ nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) *pos))+ (nr << 8);
+ nr2+=3;
+ }
+ }
+ }
+ DBUG_PRINT("exit", ("hash: 0x%lx", nr));
+ return(nr);
+}
+
+#else
+
+/*
+ * Fowler/Noll/Vo hash
+ *
+ * The basis of the hash algorithm was taken from an idea sent by email to the
+ * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
+ * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com)
+ * later improved on their algorithm.
+ *
+ * The magic is in the interesting relationship between the special prime
+ * 16777619 (2^24 + 403) and 2^32 and 2^8.
+ *
+ * This hash produces the fewest collisions of any function that we've seen so
+ * far, and works well on both numbers and strings.
+ */
+
+ulong hp_hashnr(register HP_KEYDEF *keydef, register const uchar *key)
+{
+ /*
+ Note, if a key consists of a combination of numeric and
+ a text columns, it most likely won't work well.
+ Making text columns work with NEW_HASH_FUNCTION
+ needs also changes in strings/ctype-xxx.c.
+ */
+ ulong nr= 1, nr2= 4;
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ uchar *pos=(uchar*) key;
+ key+=seg->length;
+ if (seg->null_bit)
+ {
+ key++;
+ if (*pos)
+ {
+ nr^= (nr << 1) | 1;
+ /* Add key pack length (2) to key for VARCHAR segments */
+ if (seg->type == HA_KEYTYPE_VARTEXT1)
+ key+= 2;
+ continue;
+ }
+ pos++;
+ }
+ if (seg->type == HA_KEYTYPE_TEXT)
+ {
+ seg->charset->coll->hash_sort(seg->charset, pos, ((uchar*)key)-pos,
+ &nr, &nr2);
+ }
+ else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
+ {
+ uint pack_length= 2; /* Key packing is constant */
+ uint length= uint2korr(pos);
+ seg->charset->coll->hash_sort(seg->charset, pos+pack_length, length,
+ &nr, &nr2);
+ key+= pack_length;
+ }
+ else
+ {
+ for ( ; pos < (uchar*) key ; pos++)
+ {
+ nr *=16777619;
+ nr ^=(uint) *pos;
+ }
+ }
+ }
+ DBUG_PRINT("exit", ("hash: 0x%lx", nr));
+ return(nr);
+}
+
+ /* Calc hashvalue for a key in a record */
+
+ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec)
+{
+ ulong nr= 1, nr2= 4;
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ uchar *pos=(uchar*) rec+seg->start;
+ if (seg->null_bit)
+ {
+ if (rec[seg->null_pos] & seg->null_bit)
+ {
+ nr^= (nr << 1) | 1;
+ continue;
+ }
+ }
+ if (seg->type == HA_KEYTYPE_TEXT)
+ {
+ uint char_length= seg->length; /* TODO: fix to use my_charpos() */
+ seg->charset->coll->hash_sort(seg->charset, pos, char_length,
+ &nr, &nr2);
+ }
+ else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
+ {
+ uint pack_length= seg->bit_start;
+ uint length= (pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos));
+ seg->charset->coll->hash_sort(seg->charset, pos+pack_length,
+ length, &nr, &nr2);
+ }
+ else
+ {
+ uchar *end= pos+seg->length;
+ for ( ; pos < end ; pos++)
+ {
+ nr *=16777619;
+ nr ^=(uint) *pos;
+ }
+ }
+ }
+ DBUG_PRINT("exit", ("hash: 0x%lx", nr));
+ return(nr);
+}
+
+#endif
+
+
+/*
+ Compare keys for two records. Returns 0 if they are identical
+
+ SYNOPSIS
+ hp_rec_key_cmp()
+ keydef Key definition
+ rec1 Record to compare
+ rec2 Other record to compare
+ diff_if_only_endspace_difference
+ Different number of end space is significant
+
+ NOTES
+ diff_if_only_endspace_difference is used to allow us to insert
+ 'a' and 'a ' when there is an an unique key.
+
+ RETURN
+ 0 Key is identical
+ <> 0 Key differes
+*/
+
+int hp_rec_key_cmp(HP_KEYDEF *keydef, const uchar *rec1, const uchar *rec2,
+ my_bool diff_if_only_endspace_difference)
+{
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ if (seg->null_bit)
+ {
+ if ((rec1[seg->null_pos] & seg->null_bit) !=
+ (rec2[seg->null_pos] & seg->null_bit))
+ return 1;
+ if (rec1[seg->null_pos] & seg->null_bit)
+ continue;
+ }
+ if (seg->type == HA_KEYTYPE_TEXT)
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint char_length1;
+ uint char_length2;
+ uchar *pos1= (uchar*)rec1 + seg->start;
+ uchar *pos2= (uchar*)rec2 + seg->start;
+ if (cs->mbmaxlen > 1)
+ {
+ uint char_length= seg->length / cs->mbmaxlen;
+ char_length1= my_charpos(cs, pos1, pos1 + seg->length, char_length);
+ set_if_smaller(char_length1, seg->length);
+ char_length2= my_charpos(cs, pos2, pos2 + seg->length, char_length);
+ set_if_smaller(char_length2, seg->length);
+ }
+ else
+ {
+ char_length1= char_length2= seg->length;
+ }
+ if (seg->charset->coll->strnncollsp(seg->charset,
+ pos1,char_length1,
+ pos2,char_length2, 0))
+ return 1;
+ }
+ else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
+ {
+ uchar *pos1= (uchar*) rec1 + seg->start;
+ uchar *pos2= (uchar*) rec2 + seg->start;
+ uint char_length1, char_length2;
+ uint pack_length= seg->bit_start;
+ CHARSET_INFO *cs= seg->charset;
+ if (pack_length == 1)
+ {
+ char_length1= (uint) *(uchar*) pos1++;
+ char_length2= (uint) *(uchar*) pos2++;
+ }
+ else
+ {
+ char_length1= uint2korr(pos1);
+ char_length2= uint2korr(pos2);
+ pos1+= 2;
+ pos2+= 2;
+ }
+ if (cs->mbmaxlen > 1)
+ {
+ uint safe_length1= char_length1;
+ uint safe_length2= char_length2;
+ uint char_length= seg->length / cs->mbmaxlen;
+ char_length1= my_charpos(cs, pos1, pos1 + char_length1, char_length);
+ set_if_smaller(char_length1, safe_length1);
+ char_length2= my_charpos(cs, pos2, pos2 + char_length2, char_length);
+ set_if_smaller(char_length2, safe_length2);
+ }
+
+ if (cs->coll->strnncollsp(seg->charset,
+ pos1, char_length1,
+ pos2, char_length2,
+ seg->flag & HA_END_SPACE_ARE_EQUAL ?
+ 0 : diff_if_only_endspace_difference))
+ return 1;
+ }
+ else
+ {
+ if (memcmp(rec1+seg->start,rec2+seg->start,seg->length))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+ /* Compare a key in a record to a whole key */
+
+int hp_key_cmp(HP_KEYDEF *keydef, const uchar *rec, const uchar *key)
+{
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ;
+ seg < endseg ;
+ key+= (seg++)->length)
+ {
+ if (seg->null_bit)
+ {
+ int found_null=test(rec[seg->null_pos] & seg->null_bit);
+ if (found_null != (int) *key++)
+ return 1;
+ if (found_null)
+ {
+ /* Add key pack length (2) to key for VARCHAR segments */
+ if (seg->type == HA_KEYTYPE_VARTEXT1)
+ key+= 2;
+ continue;
+ }
+ }
+ if (seg->type == HA_KEYTYPE_TEXT)
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint char_length_key;
+ uint char_length_rec;
+ uchar *pos= (uchar*) rec + seg->start;
+ if (cs->mbmaxlen > 1)
+ {
+ uint char_length= seg->length / cs->mbmaxlen;
+ char_length_key= my_charpos(cs, key, key + seg->length, char_length);
+ set_if_smaller(char_length_key, seg->length);
+ char_length_rec= my_charpos(cs, pos, pos + seg->length, char_length);
+ set_if_smaller(char_length_rec, seg->length);
+ }
+ else
+ {
+ char_length_key= seg->length;
+ char_length_rec= seg->length;
+ }
+
+ if (seg->charset->coll->strnncollsp(seg->charset,
+ (uchar*) pos, char_length_rec,
+ (uchar*) key, char_length_key, 0))
+ return 1;
+ }
+ else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
+ {
+ uchar *pos= (uchar*) rec + seg->start;
+ CHARSET_INFO *cs= seg->charset;
+ uint pack_length= seg->bit_start;
+ uint char_length_rec= (pack_length == 1 ? (uint) *(uchar*) pos :
+ uint2korr(pos));
+ /* Key segments are always packed with 2 bytes */
+ uint char_length_key= uint2korr(key);
+ pos+= pack_length;
+ key+= 2; /* skip key pack length */
+ if (cs->mbmaxlen > 1)
+ {
+ uint char_length1, char_length2;
+ char_length1= char_length2= seg->length / cs->mbmaxlen;
+ char_length1= my_charpos(cs, key, key + char_length_key, char_length1);
+ set_if_smaller(char_length_key, char_length1);
+ char_length2= my_charpos(cs, pos, pos + char_length_rec, char_length2);
+ set_if_smaller(char_length_rec, char_length2);
+ }
+
+ if (cs->coll->strnncollsp(seg->charset,
+ (uchar*) pos, char_length_rec,
+ (uchar*) key, char_length_key, 0))
+ return 1;
+ }
+ else
+ {
+ if (memcmp(rec+seg->start,key,seg->length))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+ /* Copy a key from a record to a keybuffer */
+
+void hp_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec)
+{
+ HA_KEYSEG *seg,*endseg;
+
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ CHARSET_INFO *cs= seg->charset;
+ uint char_length= seg->length;
+ uchar *pos= (uchar*) rec + seg->start;
+ if (seg->null_bit)
+ *key++= test(rec[seg->null_pos] & seg->null_bit);
+ if (cs->mbmaxlen > 1)
+ {
+ char_length= my_charpos(cs, pos, pos + seg->length,
+ char_length / cs->mbmaxlen);
+ set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */
+ }
+ if (seg->type == HA_KEYTYPE_VARTEXT1)
+ char_length+= seg->bit_start; /* Copy also length */
+ memcpy(key,rec+seg->start,(size_t) char_length);
+ key+= char_length;
+ }
+}
+
+#define FIX_LENGTH(cs, pos, length, char_length) \
+ do { \
+ if (length > char_length) \
+ char_length= my_charpos(cs, pos, pos+length, char_length); \
+ set_if_smaller(char_length,length); \
+ } while(0)
+
+
+uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key,
+ const uchar *rec, uchar *recpos)
+{
+ uchar *start_key= key;
+ HA_KEYSEG *seg, *endseg;
+
+ for (seg= keydef->seg, endseg= seg + keydef->keysegs; seg < endseg; seg++)
+ {
+ uint char_length;
+ if (seg->null_bit)
+ {
+ if (!(*key++= 1 - test(rec[seg->null_pos] & seg->null_bit)))
+ continue;
+ }
+ if (seg->flag & HA_SWAP_KEY)
+ {
+ uint length= seg->length;
+ uchar *pos= (uchar*) rec + seg->start;
+
+#ifdef HAVE_ISNAN
+ if (seg->type == HA_KEYTYPE_FLOAT)
+ {
+ float nr;
+ float4get(nr, pos);
+ if (isnan(nr))
+ {
+ /* Replace NAN with zero */
+ bzero(key, length);
+ key+= length;
+ continue;
+ }
+ }
+ else if (seg->type == HA_KEYTYPE_DOUBLE)
+ {
+ double nr;
+ float8get(nr, pos);
+ if (isnan(nr))
+ {
+ bzero(key, length);
+ key+= length;
+ continue;
+ }
+ }
+#endif
+ pos+= length;
+ while (length--)
+ {
+ *key++= *--pos;
+ }
+ continue;
+ }
+
+ if (seg->flag & HA_VAR_LENGTH_PART)
+ {
+ uchar *pos= (uchar*) rec + seg->start;
+ uint length= seg->length;
+ uint pack_length= seg->bit_start;
+ uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
+ uint2korr(pos));
+ CHARSET_INFO *cs= seg->charset;
+ char_length= length/cs->mbmaxlen;
+
+ pos+= pack_length; /* Skip VARCHAR length */
+ set_if_smaller(length,tmp_length);
+ FIX_LENGTH(cs, pos, length, char_length);
+ store_key_length_inc(key,char_length);
+ memcpy((uchar*) key,(uchar*) pos,(size_t) char_length);
+ key+= char_length;
+ continue;
+ }
+
+ char_length= seg->length;
+ if (seg->charset->mbmaxlen > 1)
+ {
+ char_length= my_charpos(seg->charset,
+ rec + seg->start, rec + seg->start + char_length,
+ char_length / seg->charset->mbmaxlen);
+ set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */
+ if (char_length < seg->length)
+ seg->charset->cset->fill(seg->charset, (char*) key + char_length,
+ seg->length - char_length, ' ');
+ }
+ memcpy(key, rec + seg->start, (size_t) char_length);
+ key+= seg->length;
+ }
+ memcpy(key, &recpos, sizeof(uchar*));
+ return (uint) (key - start_key);
+}
+
+
+uint hp_rb_pack_key(HP_KEYDEF *keydef, uchar *key, const uchar *old,
+ key_part_map keypart_map)
+{
+ HA_KEYSEG *seg, *endseg;
+ uchar *start_key= key;
+
+ for (seg= keydef->seg, endseg= seg + keydef->keysegs;
+ seg < endseg && keypart_map; old+= seg->length, seg++)
+ {
+ uint char_length;
+ keypart_map>>= 1;
+ if (seg->null_bit)
+ {
+ if (!(*key++= (char) 1 - *old++))
+ continue;
+ }
+ if (seg->flag & HA_SWAP_KEY)
+ {
+ uint length= seg->length;
+ uchar *pos= (uchar*) old + length;
+
+ while (length--)
+ {
+ *key++= *--pos;
+ }
+ continue;
+ }
+ if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
+ {
+ /* Length of key-part used with heap_rkey() always 2 */
+ uint tmp_length=uint2korr(old);
+ uint length= seg->length;
+ CHARSET_INFO *cs= seg->charset;
+ char_length= length/cs->mbmaxlen;
+
+ old+= 2;
+ set_if_smaller(length,tmp_length); /* Safety */
+ FIX_LENGTH(cs, old, length, char_length);
+ store_key_length_inc(key,char_length);
+ memcpy((uchar*) key, old,(size_t) char_length);
+ key+= char_length;
+ continue;
+ }
+ char_length= seg->length;
+ if (seg->charset->mbmaxlen > 1)
+ {
+ char_length= my_charpos(seg->charset, old, old+char_length,
+ char_length / seg->charset->mbmaxlen);
+ set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */
+ if (char_length < seg->length)
+ seg->charset->cset->fill(seg->charset, (char*) key + char_length,
+ seg->length - char_length, ' ');
+ }
+ memcpy(key, old, (size_t) char_length);
+ key+= seg->length;
+ }
+ return (uint) (key - start_key);
+}
+
+
+uint hp_rb_key_length(HP_KEYDEF *keydef,
+ const uchar *key __attribute__((unused)))
+{
+ return keydef->length;
+}
+
+
+uint hp_rb_null_key_length(HP_KEYDEF *keydef, const uchar *key)
+{
+ const uchar *start_key= key;
+ HA_KEYSEG *seg, *endseg;
+
+ for (seg= keydef->seg, endseg= seg + keydef->keysegs; seg < endseg; seg++)
+ {
+ if (seg->null_bit && !*key++)
+ continue;
+ key+= seg->length;
+ }
+ return (uint) (key - start_key);
+}
+
+
+uint hp_rb_var_key_length(HP_KEYDEF *keydef, const uchar *key)
+{
+ const uchar *start_key= key;
+ HA_KEYSEG *seg, *endseg;
+
+ for (seg= keydef->seg, endseg= seg + keydef->keysegs; seg < endseg; seg++)
+ {
+ uint length= seg->length;
+ if (seg->null_bit && !*key++)
+ continue;
+ if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
+ {
+ get_key_length(length, key);
+ }
+ key+= length;
+ }
+ return (uint) (key - start_key);
+}
+
+
+/*
+ Test if any of the key parts are NULL.
+ Return:
+ 1 if any of the key parts was NULL
+ 0 otherwise
+*/
+
+my_bool hp_if_null_in_key(HP_KEYDEF *keydef, const uchar *record)
+{
+ HA_KEYSEG *seg,*endseg;
+ for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
+ {
+ if (seg->null_bit && (record[seg->null_pos] & seg->null_bit))
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ Update auto_increment info
+
+ SYNOPSIS
+ update_auto_increment()
+ info MyISAM handler
+ record Row to update
+
+ IMPLEMENTATION
+ Only replace the auto_increment value if it is higher than the previous
+ one. For signed columns we don't update the auto increment value if it's
+ less than zero.
+*/
+
+void heap_update_auto_increment(HP_INFO *info, const uchar *record)
+{
+ ulonglong value= 0; /* Store unsigned values here */
+ longlong s_value= 0; /* Store signed values here */
+
+ HA_KEYSEG *keyseg= info->s->keydef[info->s->auto_key - 1].seg;
+ const uchar *key= (uchar*) record + keyseg->start;
+
+ switch (info->s->auto_key_type) {
+ case HA_KEYTYPE_INT8:
+ s_value= (longlong) *(char*)key;
+ break;
+ case HA_KEYTYPE_BINARY:
+ value=(ulonglong) *(uchar*) key;
+ break;
+ case HA_KEYTYPE_SHORT_INT:
+ s_value= (longlong) sint2korr(key);
+ break;
+ case HA_KEYTYPE_USHORT_INT:
+ value=(ulonglong) uint2korr(key);
+ break;
+ case HA_KEYTYPE_LONG_INT:
+ s_value= (longlong) sint4korr(key);
+ break;
+ case HA_KEYTYPE_ULONG_INT:
+ value=(ulonglong) uint4korr(key);
+ break;
+ case HA_KEYTYPE_INT24:
+ s_value= (longlong) sint3korr(key);
+ break;
+ case HA_KEYTYPE_UINT24:
+ value=(ulonglong) uint3korr(key);
+ break;
+ case HA_KEYTYPE_FLOAT: /* This shouldn't be used */
+ {
+ float f_1;
+ float4get(f_1,key);
+ /* Ignore negative values */
+ value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
+ break;
+ }
+ case HA_KEYTYPE_DOUBLE: /* This shouldn't be used */
+ {
+ double f_1;
+ float8get(f_1,key);
+ /* Ignore negative values */
+ value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
+ break;
+ }
+ case HA_KEYTYPE_LONGLONG:
+ s_value= sint8korr(key);
+ break;
+ case HA_KEYTYPE_ULONGLONG:
+ value= uint8korr(key);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ value=0; /* Error */
+ break;
+ }
+
+ /*
+ The following code works becasue if s_value < 0 then value is 0
+ and if s_value == 0 then value will contain either s_value or the
+ correct value.
+ */
+ set_if_bigger(info->s->auto_increment,
+ (s_value > 0) ? (ulonglong) s_value : value);
+}
diff --git a/storage/heap/hp_info.c b/storage/heap/hp_info.c
new file mode 100644
index 00000000000..ea78c53fd40
--- /dev/null
+++ b/storage/heap/hp_info.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2000-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Returns info about database status */
+
+#include "heapdef.h"
+
+
+uchar *heap_position(HP_INFO *info)
+{
+ return ((info->update & HA_STATE_AKTIV) ? info->current_ptr :
+ (HEAP_PTR) 0);
+}
+
+
+#ifdef WANT_OLD_HEAP_VERSION
+
+/*
+ The following should NOT be used anymore as this can't be used together with
+ heap_rkey()
+*/
+
+ulong heap_position_old(HP_INFO *info)
+{
+ return ((info->update & HA_STATE_AKTIV) ? info->current_record :
+ (ulong) ~0L);
+}
+
+#endif /* WANT_OLD_HEAP_CODE */
+
+/* Note that heap_info does NOT return information about the
+ current position anymore; Use heap_position instead */
+
+int heap_info(reg1 HP_INFO *info,reg2 HEAPINFO *x, int flag )
+{
+ DBUG_ENTER("heap_info");
+ x->records = info->s->records;
+ x->deleted = info->s->deleted;
+ x->reclength = info->s->reclength;
+ x->data_length = info->s->data_length;
+ x->index_length = info->s->index_length;
+ x->max_records = info->s->max_records;
+ x->errkey = info->errkey;
+ if (flag & HA_STATUS_AUTO)
+ x->auto_increment= info->s->auto_increment + 1;
+ DBUG_RETURN(0);
+} /* heap_info */
diff --git a/storage/heap/hp_open.c b/storage/heap/hp_open.c
new file mode 100644
index 00000000000..4d5ec6e27ac
--- /dev/null
+++ b/storage/heap/hp_open.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2000-2004, 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* open a heap-database */
+
+#include "heapdef.h"
+#ifdef VMS
+#include "hp_static.c" /* Stupid vms-linker */
+#endif
+
+#include "my_sys.h"
+
+/*
+ Open heap table based on HP_SHARE structure
+
+ NOTE
+ This doesn't register the table in the open table list.
+*/
+
+HP_INFO *heap_open_from_share(HP_SHARE *share, int mode)
+{
+ HP_INFO *info;
+ DBUG_ENTER("heap_open_from_share");
+
+ if (!(info= (HP_INFO*) my_malloc((uint) sizeof(HP_INFO) +
+ 2 * share->max_key_length,
+ MYF(MY_ZEROFILL))))
+ {
+ DBUG_RETURN(0);
+ }
+ share->open_count++;
+#ifdef THREAD
+ thr_lock_data_init(&share->lock,&info->lock,NULL);
+#endif
+ info->s= share;
+ info->lastkey= (uchar*) (info + 1);
+ info->recbuf= (uchar*) (info->lastkey + share->max_key_length);
+ info->mode= mode;
+ info->current_record= (ulong) ~0L; /* No current record */
+ info->lastinx= info->errkey= -1;
+#ifndef DBUG_OFF
+ info->opt_flag= READ_CHECK_USED; /* Check when changing */
+#endif
+ DBUG_PRINT("exit",("heap: 0x%lx reclength: %d records_in_block: %d",
+ (long) info, share->reclength,
+ share->block.records_in_block));
+ DBUG_RETURN(info);
+}
+
+
+/*
+ Open heap table based on HP_SHARE structure and register it
+*/
+
+HP_INFO *heap_open_from_share_and_register(HP_SHARE *share, int mode)
+{
+ HP_INFO *info;
+ DBUG_ENTER("heap_open_from_share_and_register");
+
+ pthread_mutex_lock(&THR_LOCK_heap);
+ if ((info= heap_open_from_share(share, mode)))
+ {
+ info->open_list.data= (void*) info;
+ heap_open_list= list_add(heap_open_list,&info->open_list);
+ }
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(info);
+}
+
+
+/*
+ Open heap table based on name
+
+ NOTE
+ This register the table in the open table list. so that it can be
+ found by future heap_open() calls.
+*/
+
+HP_INFO *heap_open(const char *name, int mode)
+{
+ HP_INFO *info;
+ HP_SHARE *share;
+ DBUG_ENTER("heap_open");
+
+ pthread_mutex_lock(&THR_LOCK_heap);
+ if (!(share= hp_find_named_heap(name)))
+ {
+ my_errno= ENOENT;
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(0);
+ }
+ if ((info= heap_open_from_share(share, mode)))
+ {
+ info->open_list.data= (void*) info;
+ heap_open_list= list_add(heap_open_list,&info->open_list);
+ }
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(info);
+}
+
+
+/* map name to a heap-nr. If name isn't found return 0 */
+
+HP_SHARE *hp_find_named_heap(const char *name)
+{
+ LIST *pos;
+ HP_SHARE *info;
+ DBUG_ENTER("heap_find");
+ DBUG_PRINT("enter",("name: %s",name));
+
+ for (pos= heap_share_list; pos; pos= pos->next)
+ {
+ info= (HP_SHARE*) pos->data;
+ if (!strcmp(name, info->name))
+ {
+ DBUG_PRINT("exit", ("Old heap_database: 0x%lx", (long) info));
+ DBUG_RETURN(info);
+ }
+ }
+ DBUG_RETURN((HP_SHARE *) 0);
+}
+
+
diff --git a/storage/heap/hp_panic.c b/storage/heap/hp_panic.c
new file mode 100644
index 00000000000..9a2bd2c574e
--- /dev/null
+++ b/storage/heap/hp_panic.c
@@ -0,0 +1,56 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+ /* if flag == HA_PANIC_CLOSE then all files are removed for more
+ memory */
+
+int hp_panic(enum ha_panic_function flag)
+{
+ LIST *element,*next_open;
+ DBUG_ENTER("hp_panic");
+
+ pthread_mutex_lock(&THR_LOCK_heap);
+ for (element=heap_open_list ; element ; element=next_open)
+ {
+ HP_INFO *info=(HP_INFO*) element->data;
+ next_open=element->next; /* Save if close */
+ switch (flag) {
+ case HA_PANIC_CLOSE:
+ hp_close(info);
+ break;
+ default:
+ break;
+ }
+ }
+ for (element=heap_share_list ; element ; element=next_open)
+ {
+ HP_SHARE *share=(HP_SHARE*) element->data;
+ next_open=element->next; /* Save if close */
+ switch (flag) {
+ case HA_PANIC_CLOSE:
+ {
+ if (!share->open_count)
+ hp_free(share);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(0);
+} /* hp_panic */
diff --git a/storage/heap/hp_rename.c b/storage/heap/hp_rename.c
new file mode 100644
index 00000000000..c2350450e2b
--- /dev/null
+++ b/storage/heap/hp_rename.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Rename a table
+*/
+
+#include "heapdef.h"
+
+int heap_rename(const char *old_name, const char *new_name)
+{
+ reg1 HP_SHARE *info;
+ char *name_buff;
+ DBUG_ENTER("heap_rename");
+
+ pthread_mutex_lock(&THR_LOCK_heap);
+ if ((info = hp_find_named_heap(old_name)))
+ {
+ if (!(name_buff=(char*) my_strdup(new_name,MYF(MY_WME))))
+ {
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(my_errno);
+ }
+ my_free(info->name,MYF(0));
+ info->name=name_buff;
+ }
+ pthread_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(0);
+}
diff --git a/storage/heap/hp_rfirst.c b/storage/heap/hp_rfirst.c
new file mode 100644
index 00000000000..d0d2ec9b506
--- /dev/null
+++ b/storage/heap/hp_rfirst.c
@@ -0,0 +1,71 @@
+/* Copyright (C) 2000-2002, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+/* Read first record with the current key */
+
+int heap_rfirst(HP_INFO *info, uchar *record, int inx)
+{
+ HP_SHARE *share = info->s;
+ HP_KEYDEF *keyinfo = share->keydef + inx;
+
+ DBUG_ENTER("heap_rfirst");
+ info->lastinx= inx;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ uchar *pos;
+
+ if ((pos = tree_search_edge(&keyinfo->rb_tree, info->parents,
+ &info->last_pos, offsetof(TREE_ELEMENT, left))))
+ {
+ memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos),
+ sizeof(uchar*));
+ info->current_ptr = pos;
+ memcpy(record, pos, (size_t)share->reclength);
+ /*
+ If we're performing index_first on a table that was taken from
+ table cache, info->lastkey_len is initialized to previous query.
+ Thus we set info->lastkey_len to proper value for subsequent
+ heap_rnext() calls.
+ This is needed for DELETE queries only, otherwise this variable is
+ not used.
+ Note that the same workaround may be needed for heap_rlast(), but
+ for now heap_rlast() is never used for DELETE queries.
+ */
+ info->lastkey_len= 0;
+ info->update = HA_STATE_AKTIV;
+ }
+ else
+ {
+ my_errno = HA_ERR_END_OF_FILE;
+ DBUG_RETURN(my_errno);
+ }
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ if (!(info->s->records))
+ {
+ my_errno=HA_ERR_END_OF_FILE;
+ DBUG_RETURN(my_errno);
+ }
+ DBUG_ASSERT(0); /* TODO fix it */
+ info->current_record=0;
+ info->current_hash_ptr=0;
+ info->update=HA_STATE_PREV_FOUND;
+ DBUG_RETURN(heap_rnext(info,record));
+ }
+}
diff --git a/storage/heap/hp_rkey.c b/storage/heap/hp_rkey.c
new file mode 100644
index 00000000000..6eeac6acd7b
--- /dev/null
+++ b/storage/heap/hp_rkey.c
@@ -0,0 +1,80 @@
+/* Copyright (C) 2000-2004, 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+int heap_rkey(HP_INFO *info, uchar *record, int inx, const uchar *key,
+ key_part_map keypart_map, enum ha_rkey_function find_flag)
+{
+ uchar *pos;
+ HP_SHARE *share= info->s;
+ HP_KEYDEF *keyinfo= share->keydef + inx;
+ DBUG_ENTER("heap_rkey");
+ DBUG_PRINT("enter",("info: 0x%lx inx: %d", (long) info, inx));
+
+ if ((uint) inx >= share->keys)
+ {
+ DBUG_RETURN(my_errno= HA_ERR_WRONG_INDEX);
+ }
+ info->lastinx= inx;
+ info->current_record= (ulong) ~0L; /* For heap_rrnd() */
+
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ heap_rb_param custom_arg;
+
+ custom_arg.keyseg= info->s->keydef[inx].seg;
+ custom_arg.key_length= info->lastkey_len=
+ hp_rb_pack_key(keyinfo, (uchar*) info->lastkey,
+ (uchar*) key, keypart_map);
+ custom_arg.search_flag= SEARCH_FIND | SEARCH_SAME;
+ /* for next rkey() after deletion */
+ if (find_flag == HA_READ_AFTER_KEY)
+ info->last_find_flag= HA_READ_KEY_OR_NEXT;
+ else if (find_flag == HA_READ_BEFORE_KEY)
+ info->last_find_flag= HA_READ_KEY_OR_PREV;
+ else
+ info->last_find_flag= find_flag;
+ if (!(pos= tree_search_key(&keyinfo->rb_tree, info->lastkey, info->parents,
+ &info->last_pos, find_flag, &custom_arg)))
+ {
+ info->update= 0;
+ DBUG_RETURN(my_errno= HA_ERR_KEY_NOT_FOUND);
+ }
+ memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos), sizeof(uchar*));
+ info->current_ptr= pos;
+ }
+ else
+ {
+ if (!(pos= hp_search(info, share->keydef + inx, key, 0)))
+ {
+ info->update= 0;
+ DBUG_RETURN(my_errno);
+ }
+ if (!(keyinfo->flag & HA_NOSAME) || (keyinfo->flag & HA_END_SPACE_KEY))
+ memcpy(info->lastkey, key, (size_t) keyinfo->length);
+ }
+ memcpy(record, pos, (size_t) share->reclength);
+ info->update= HA_STATE_AKTIV;
+ DBUG_RETURN(0);
+}
+
+
+ /* Quick find of record */
+
+uchar* heap_find(HP_INFO *info, int inx, const uchar *key)
+{
+ return hp_search(info, info->s->keydef + inx, key, 0);
+}
diff --git a/storage/heap/hp_rlast.c b/storage/heap/hp_rlast.c
new file mode 100644
index 00000000000..45ad7c21f49
--- /dev/null
+++ b/storage/heap/hp_rlast.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+ /* Read first record with the current key */
+
+
+int heap_rlast(HP_INFO *info, uchar *record, int inx)
+{
+ HP_SHARE *share= info->s;
+ HP_KEYDEF *keyinfo= share->keydef + inx;
+
+ DBUG_ENTER("heap_rlast");
+ info->lastinx= inx;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ uchar *pos;
+
+ if ((pos = tree_search_edge(&keyinfo->rb_tree, info->parents,
+ &info->last_pos, offsetof(TREE_ELEMENT, right))))
+ {
+ memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos),
+ sizeof(uchar*));
+ info->current_ptr = pos;
+ memcpy(record, pos, (size_t)share->reclength);
+ info->update = HA_STATE_AKTIV;
+ }
+ else
+ {
+ my_errno = HA_ERR_END_OF_FILE;
+ DBUG_RETURN(my_errno);
+ }
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ info->current_ptr=0;
+ info->current_hash_ptr=0;
+ info->update=HA_STATE_NEXT_FOUND;
+ DBUG_RETURN(heap_rprev(info,record));
+ }
+}
diff --git a/storage/heap/hp_rnext.c b/storage/heap/hp_rnext.c
new file mode 100644
index 00000000000..3d715f4e6d3
--- /dev/null
+++ b/storage/heap/hp_rnext.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+/* Read next record with the same key */
+
+int heap_rnext(HP_INFO *info, uchar *record)
+{
+ uchar *pos;
+ HP_SHARE *share=info->s;
+ HP_KEYDEF *keyinfo;
+ DBUG_ENTER("heap_rnext");
+
+ if (info->lastinx < 0)
+ DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
+
+ keyinfo = share->keydef + info->lastinx;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ heap_rb_param custom_arg;
+
+ if (info->last_pos)
+ {
+ /*
+ We enter this branch for non-DELETE queries after heap_rkey()
+ or heap_rfirst(). As last key position (info->last_pos) is available,
+ we only need to climb the tree using tree_search_next().
+ */
+ pos = tree_search_next(&keyinfo->rb_tree, &info->last_pos,
+ offsetof(TREE_ELEMENT, left),
+ offsetof(TREE_ELEMENT, right));
+ }
+ else if (!info->lastkey_len)
+ {
+ /*
+ We enter this branch only for DELETE queries after heap_rfirst(). E.g.
+ DELETE FROM t1 WHERE a<10. As last key position is not available
+ (last key is removed by heap_delete()), we must restart search as it
+ is done in heap_rfirst().
+
+ It should be safe to handle this situation without this branch. That is
+ branch below should find smallest element in a tree as lastkey_len is
+ zero. tree_search_edge() is a kind of optimisation here as it should be
+ faster than tree_search_key().
+ */
+ pos= tree_search_edge(&keyinfo->rb_tree, info->parents,
+ &info->last_pos, offsetof(TREE_ELEMENT, left));
+ }
+ else
+ {
+ /*
+ We enter this branch only for DELETE queries after heap_rkey(). E.g.
+ DELETE FROM t1 WHERE a=10. As last key position is not available
+ (last key is removed by heap_delete()), we must restart search as it
+ is done in heap_rkey().
+ */
+ custom_arg.keyseg = keyinfo->seg;
+ custom_arg.key_length = info->lastkey_len;
+ custom_arg.search_flag = SEARCH_SAME | SEARCH_FIND;
+ pos = tree_search_key(&keyinfo->rb_tree, info->lastkey, info->parents,
+ &info->last_pos, info->last_find_flag, &custom_arg);
+ }
+ if (pos)
+ {
+ memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos),
+ sizeof(uchar*));
+ info->current_ptr = pos;
+ }
+ else
+ {
+ my_errno = HA_ERR_KEY_NOT_FOUND;
+ }
+ }
+ else
+ {
+ if (info->current_hash_ptr)
+ pos= hp_search_next(info, keyinfo, info->lastkey,
+ info->current_hash_ptr);
+ else
+ {
+ if (!info->current_ptr && (info->update & HA_STATE_NEXT_FOUND))
+ {
+ pos=0; /* Read next after last */
+ my_errno=HA_ERR_KEY_NOT_FOUND;
+ }
+ else if (!info->current_ptr) /* Deleted or first call */
+ pos= hp_search(info, keyinfo, info->lastkey, 0);
+ else
+ pos= hp_search(info, keyinfo, info->lastkey, 1);
+ }
+ }
+ if (!pos)
+ {
+ info->update=HA_STATE_NEXT_FOUND; /* For heap_rprev */
+ if (my_errno == HA_ERR_KEY_NOT_FOUND)
+ my_errno=HA_ERR_END_OF_FILE;
+ DBUG_RETURN(my_errno);
+ }
+ memcpy(record,pos,(size_t) share->reclength);
+ info->update=HA_STATE_AKTIV | HA_STATE_NEXT_FOUND;
+ DBUG_RETURN(0);
+}
diff --git a/storage/heap/hp_rprev.c b/storage/heap/hp_rprev.c
new file mode 100644
index 00000000000..63bfffffba9
--- /dev/null
+++ b/storage/heap/hp_rprev.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "heapdef.h"
+
+ /* Read prev record for key */
+
+
+int heap_rprev(HP_INFO *info, uchar *record)
+{
+ uchar *pos;
+ HP_SHARE *share=info->s;
+ HP_KEYDEF *keyinfo;
+ DBUG_ENTER("heap_rprev");
+
+ if (info->lastinx < 0)
+ DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
+ keyinfo = share->keydef + info->lastinx;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ heap_rb_param custom_arg;
+
+ if (info->last_pos)
+ pos = tree_search_next(&keyinfo->rb_tree, &info->last_pos,
+ offsetof(TREE_ELEMENT, right),
+ offsetof(TREE_ELEMENT, left));
+ else
+ {
+ custom_arg.keyseg = keyinfo->seg;
+ custom_arg.key_length = keyinfo->length;
+ custom_arg.search_flag = SEARCH_SAME;
+ pos = tree_search_key(&keyinfo->rb_tree, info->lastkey, info->parents,
+ &info->last_pos, info->last_find_flag, &custom_arg);
+ }
+ if (pos)
+ {
+ memcpy(&pos, pos + (*keyinfo->get_key_length)(keyinfo, pos),
+ sizeof(uchar*));
+ info->current_ptr = pos;
+ }
+ else
+ {
+ my_errno = HA_ERR_KEY_NOT_FOUND;
+ }
+ }
+ else
+ {
+ if (info->current_ptr || (info->update & HA_STATE_NEXT_FOUND))
+ {
+ if ((info->update & HA_STATE_DELETED))
+ pos= hp_search(info, share->keydef + info->lastinx, info->lastkey, 3);
+ else
+ pos= hp_search(info, share->keydef + info->lastinx, info->lastkey, 2);
+ }
+ else
+ {
+ pos=0; /* Read next after last */
+ my_errno=HA_ERR_KEY_NOT_FOUND;
+ }
+ }
+ if (!pos)
+ {
+ info->update=HA_STATE_PREV_FOUND; /* For heap_rprev */
+ if (my_errno == HA_ERR_KEY_NOT_FOUND)
+ my_errno=HA_ERR_END_OF_FILE;
+ DBUG_RETURN(my_errno);
+ }
+ memcpy(record,pos,(size_t) share->reclength);
+ info->update=HA_STATE_AKTIV | HA_STATE_PREV_FOUND;
+ DBUG_RETURN(0);
+}
diff --git a/storage/heap/hp_rrnd.c b/storage/heap/hp_rrnd.c
new file mode 100644
index 00000000000..3ac23d293f2
--- /dev/null
+++ b/storage/heap/hp_rrnd.c
@@ -0,0 +1,105 @@
+/* Copyright (C) 2000-2002, 2004, 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Read a record from a random position */
+
+#include "heapdef.h"
+
+/*
+ Returns one of following values:
+ 0 = Ok.
+ HA_ERR_RECORD_DELETED = Record is deleted.
+ HA_ERR_END_OF_FILE = EOF.
+*/
+
+int heap_rrnd(register HP_INFO *info, uchar *record, uchar *pos)
+{
+ HP_SHARE *share=info->s;
+ DBUG_ENTER("heap_rrnd");
+ DBUG_PRINT("enter",("info: 0x%lx pos: %lx",(long) info, (long) pos));
+
+ info->lastinx= -1;
+ if (!(info->current_ptr= pos))
+ {
+ info->update= 0;
+ DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
+ }
+ if (!info->current_ptr[share->reclength])
+ {
+ info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
+ DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
+ }
+ info->update=HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND | HA_STATE_AKTIV;
+ memcpy(record,info->current_ptr,(size_t) share->reclength);
+ DBUG_PRINT("exit", ("found record at 0x%lx", (long) info->current_ptr));
+ info->current_hash_ptr=0; /* Can't use rnext */
+ DBUG_RETURN(0);
+} /* heap_rrnd */
+
+
+#ifdef WANT_OLD_HEAP_VERSION
+
+/*
+ If pos == -1 then read next record
+ Returns one of following values:
+ 0 = Ok.
+ HA_ERR_RECORD_DELETED = Record is deleted.
+ HA_ERR_END_OF_FILE = EOF.
+*/
+
+int heap_rrnd_old(register HP_INFO *info, uchar *record, ulong pos)
+{
+ HP_SHARE *share=info->s;
+ DBUG_ENTER("heap_rrnd");
+ DBUG_PRINT("enter",("info: 0x%lx pos: %ld",info,pos));
+
+ info->lastinx= -1;
+ if (pos == (ulong) -1)
+ {
+ pos= ++info->current_record;
+ if (pos % share->block.records_in_block && /* Quick next record */
+ pos < share->records+share->deleted &&
+ (info->update & HA_STATE_PREV_FOUND))
+ {
+ info->current_ptr+=share->block.recbuffer;
+ goto end;
+ }
+ }
+ else
+ info->current_record=pos;
+
+ if (pos >= share->records+share->deleted)
+ {
+ info->update= 0;
+ DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
+ }
+
+ /* Find record number pos */
+ hp_find_record(info, pos);
+
+end:
+ if (!info->current_ptr[share->reclength])
+ {
+ info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
+ DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
+ }
+ info->update=HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND | HA_STATE_AKTIV;
+ memcpy(record,info->current_ptr,(size_t) share->reclength);
+ DBUG_PRINT("exit",("found record at 0x%lx",info->current_ptr));
+ info->current_hash_ptr=0; /* Can't use rnext */
+ DBUG_RETURN(0);
+} /* heap_rrnd */
+
+#endif /* WANT_OLD_HEAP_VERSION */
diff --git a/storage/heap/hp_rsame.c b/storage/heap/hp_rsame.c
new file mode 100644
index 00000000000..1a3724672b6
--- /dev/null
+++ b/storage/heap/hp_rsame.c
@@ -0,0 +1,56 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* re-read current record */
+
+#include "heapdef.h"
+
+ /* If inx != -1 the new record is read according to index
+ (for next/prev). Record must in this case point to last record
+ Returncodes:
+ 0 = Ok.
+ HA_ERR_RECORD_DELETED = Record was removed
+ HA_ERR_KEY_NOT_FOUND = Record not found with key
+ */
+
+int heap_rsame(register HP_INFO *info, uchar *record, int inx)
+{
+ HP_SHARE *share=info->s;
+ DBUG_ENTER("heap_rsame");
+
+ test_active(info);
+ if (info->current_ptr[share->reclength])
+ {
+ if (inx < -1 || inx >= (int) share->keys)
+ {
+ DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
+ }
+ else if (inx != -1)
+ {
+ info->lastinx=inx;
+ hp_make_key(share->keydef + inx, info->lastkey, record);
+ if (!hp_search(info, share->keydef + inx, info->lastkey, 3))
+ {
+ info->update=0;
+ DBUG_RETURN(my_errno);
+ }
+ }
+ memcpy(record,info->current_ptr,(size_t) share->reclength);
+ DBUG_RETURN(0);
+ }
+ info->update=0;
+
+ DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
+}
diff --git a/storage/heap/hp_scan.c b/storage/heap/hp_scan.c
new file mode 100644
index 00000000000..e8913e92c86
--- /dev/null
+++ b/storage/heap/hp_scan.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Scan through all rows */
+
+#include "heapdef.h"
+
+/*
+ Returns one of following values:
+ 0 = Ok.
+ HA_ERR_RECORD_DELETED = Record is deleted.
+ HA_ERR_END_OF_FILE = EOF.
+*/
+
+int heap_scan_init(register HP_INFO *info)
+{
+ DBUG_ENTER("heap_scan_init");
+ info->lastinx= -1;
+ info->current_record= (ulong) ~0L; /* No current record */
+ info->update=0;
+ info->next_block=0;
+ DBUG_RETURN(0);
+}
+
+int heap_scan(register HP_INFO *info, uchar *record)
+{
+ HP_SHARE *share=info->s;
+ ulong pos;
+ DBUG_ENTER("heap_scan");
+
+ pos= ++info->current_record;
+ if (pos < info->next_block)
+ {
+ info->current_ptr+=share->block.recbuffer;
+ }
+ else
+ {
+ info->next_block+=share->block.records_in_block;
+ if (info->next_block >= share->records+share->deleted)
+ {
+ info->next_block= share->records+share->deleted;
+ if (pos >= info->next_block)
+ {
+ info->update= 0;
+ DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
+ }
+ }
+ hp_find_record(info, pos);
+ }
+ if (!info->current_ptr[share->reclength])
+ {
+ DBUG_PRINT("warning",("Found deleted record"));
+ info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
+ DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
+ }
+ info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND | HA_STATE_AKTIV;
+ memcpy(record,info->current_ptr,(size_t) share->reclength);
+ info->current_hash_ptr=0; /* Can't use read_next */
+ DBUG_RETURN(0);
+} /* heap_scan */
diff --git a/storage/heap/hp_static.c b/storage/heap/hp_static.c
new file mode 100644
index 00000000000..a501ba7e359
--- /dev/null
+++ b/storage/heap/hp_static.c
@@ -0,0 +1,25 @@
+/* Copyright (C) 2000-2001 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Static variables for heap library. All definied here for easy making of
+ a shared library
+*/
+
+#ifndef _global_h
+#include "heapdef.h"
+#endif
+
+LIST *heap_open_list=0,*heap_share_list=0;
diff --git a/storage/heap/hp_test1.c b/storage/heap/hp_test1.c
new file mode 100644
index 00000000000..b1b55098a78
--- /dev/null
+++ b/storage/heap/hp_test1.c
@@ -0,0 +1,195 @@
+/* Copyright (C) 2000-2003, 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Test av heap-database */
+/* Programmet skapar en heap-databas. Till denna skrivs ett antal poster.
+ Databasen st{ngs. D{refter |ppnas den p} nytt och en del av posterna
+ raderas.
+*/
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include "heap.h"
+
+static int get_options(int argc, char *argv[]);
+
+static int flag=0,verbose=0,remove_ant=0,flags[50];
+
+int main(int argc, char **argv)
+{
+ int i,j,error,deleted;
+ HP_INFO *file;
+ uchar record[128],key[32];
+ const char *filename;
+ HP_KEYDEF keyinfo[10];
+ HA_KEYSEG keyseg[4];
+ HP_CREATE_INFO hp_create_info;
+ HP_SHARE *tmp_share;
+ MY_INIT(argv[0]);
+
+ filename= "test1";
+ get_options(argc,argv);
+
+ bzero(&hp_create_info, sizeof(hp_create_info));
+ hp_create_info.max_table_size= 1024L*1024L;
+
+ keyinfo[0].keysegs=1;
+ keyinfo[0].seg=keyseg;
+ keyinfo[0].algorithm= HA_KEY_ALG_HASH;
+ keyinfo[0].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[0].seg[0].start=1;
+ keyinfo[0].seg[0].length=6;
+ keyinfo[0].seg[0].charset= &my_charset_latin1;
+ keyinfo[0].seg[0].null_bit= 0;
+ keyinfo[0].flag = HA_NOSAME;
+
+ deleted=0;
+ bzero((uchar*) flags,sizeof(flags));
+
+ printf("- Creating heap-file\n");
+ if (heap_create(filename,1,keyinfo,30,(ulong) flag*100000L,10L,
+ &hp_create_info, &tmp_share) ||
+ !(file= heap_open(filename, 2)))
+ goto err;
+ printf("- Writing records:s\n");
+ strmov((char*) record," ..... key ");
+
+ for (i=49 ; i>=1 ; i-=2 )
+ {
+ j=i%25 +1;
+ sprintf((char*) key,"%6d",j);
+ bmove(record+1,key,6);
+ error=heap_write(file,record);
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+ flags[j]=1;
+ if (verbose || error) printf("J= %2d heap_write: %d my_errno: %d\n",
+ j,error,my_errno);
+ }
+ if (heap_close(file))
+ goto err;
+ printf("- Reopening file\n");
+ if (!(file=heap_open(filename, 2)))
+ goto err;
+
+ printf("- Removing records\n");
+ for (i=1 ; i<=10 ; i++)
+ {
+ if (i == remove_ant) { VOID(heap_close(file)) ; return (0) ; }
+ sprintf((char*) key,"%6d",(j=(int) ((rand() & 32767)/32767.*25)));
+ if ((error = heap_rkey(file,record,0,key,6,HA_READ_KEY_EXACT)))
+ {
+ if (verbose || (flags[j] == 1 ||
+ (error && my_errno != HA_ERR_KEY_NOT_FOUND)))
+ printf("key: %s rkey: %3d my_errno: %3d\n",(char*) key,error,my_errno);
+ }
+ else
+ {
+ error=heap_delete(file,record);
+ if (error || verbose)
+ printf("key: %s delete: %d my_errno: %d\n",(char*) key,error,my_errno);
+ flags[j]=0;
+ if (! error)
+ deleted++;
+ }
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+ }
+
+ printf("- Reading records with key\n");
+ for (i=1 ; i<=25 ; i++)
+ {
+ sprintf((char*) key,"%6d",i);
+ bmove(record+1,key,6);
+ my_errno=0;
+ error=heap_rkey(file,record,0,key,6,HA_READ_KEY_EXACT);
+ if (verbose ||
+ (error == 0 && flags[i] != 1) ||
+ (error && (flags[i] != 0 || my_errno != HA_ERR_KEY_NOT_FOUND)))
+ {
+ printf("key: %s rkey: %3d my_errno: %3d record: %s\n",
+ (char*) key,error,my_errno,record+1);
+ }
+ }
+
+#ifdef OLD_HEAP_VERSION
+ {
+ int found;
+ printf("- Reading records with position\n");
+ for (i=1,found=0 ; i <= 30 ; i++)
+ {
+ my_errno=0;
+ if ((error=heap_rrnd(file,record,i == 1 ? 0L : (ulong) -1)) ==
+ HA_ERR_END_OF_FILE)
+ {
+ if (found != 25-deleted)
+ printf("Found only %d of %d records\n",found,25-deleted);
+ break;
+ }
+ if (!error)
+ found++;
+ if (verbose || (error != 0 && error != HA_ERR_RECORD_DELETED))
+ {
+ printf("pos: %2d ni_rrnd: %3d my_errno: %3d record: %s\n",
+ i-1,error,my_errno,(char*) record+1);
+ }
+ }
+ }
+#endif
+
+ if (heap_close(file) || hp_panic(HA_PANIC_CLOSE))
+ goto err;
+ my_end(MY_GIVE_INFO);
+ return(0);
+err:
+ printf("got error: %d when using heap-database\n",my_errno);
+ return(1);
+} /* main */
+
+
+/* Read options */
+
+static int get_options(int argc, char **argv)
+{
+ char *pos;
+
+ while (--argc >0 && *(pos = *(++argv)) == '-' ) {
+ switch(*++pos) {
+ case 'B': /* Big file */
+ flag=1;
+ break;
+ case 'v': /* verbose */
+ verbose=1;
+ break;
+ case 'm':
+ remove_ant=atoi(++pos);
+ break;
+ case 'V':
+ printf("hp_test1 Ver 3.0 \n");
+ exit(0);
+ case '#':
+ DBUG_PUSH (++pos);
+ break;
+ }
+ }
+ return 0;
+} /* get options */
diff --git a/storage/heap/hp_test2.c b/storage/heap/hp_test2.c
new file mode 100644
index 00000000000..bf06cf03035
--- /dev/null
+++ b/storage/heap/hp_test2.c
@@ -0,0 +1,695 @@
+/* Copyright (C) 2000-2003, 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Test av isam-databas: stor test */
+
+#ifndef USE_MY_FUNC /* We want to be able to dbug this !! */
+#define USE_MY_FUNC
+#endif
+#ifdef DBUG_OFF
+#undef DBUG_OFF
+#endif
+#ifndef SAFEMALLOC
+#define SAFEMALLOC
+#endif
+
+#include "heapdef.h" /* Because of hp_find_block */
+#include <signal.h>
+
+#define MAX_RECORDS 100000
+#define MAX_KEYS 4
+
+static int get_options(int argc, char *argv[]);
+static int rnd(int max_value);
+static sig_handler endprog(int sig_number);
+
+static uint flag=0,verbose=0,testflag=0,recant=10000,silent=0;
+static uint keys=MAX_KEYS;
+static uint16 key1[1001];
+static my_bool key3[MAX_RECORDS];
+static int reclength=39;
+
+
+static int calc_check(uchar *buf,uint length);
+static void make_record(uchar *record, uint n1, uint n2, uint n3,
+ const char *mark, uint count);
+
+/* Main program */
+
+int main(int argc, char *argv[])
+{
+ register uint i,j;
+ uint ant,n1,n2,n3;
+ uint write_count,update,opt_delete,check2,dupp_keys,found_key;
+ int error;
+ ulong pos;
+ unsigned long key_check;
+ uchar record[128],record2[128],record3[128],key[10];
+ const char *filename,*filename2;
+ HP_INFO *file,*file2;
+ HP_SHARE *tmp_share;
+ HP_KEYDEF keyinfo[MAX_KEYS];
+ HA_KEYSEG keyseg[MAX_KEYS*5];
+ HEAP_PTR UNINIT_VAR(position);
+ HP_CREATE_INFO hp_create_info;
+ CHARSET_INFO *cs= &my_charset_latin1;
+ MY_INIT(argv[0]); /* init my_sys library & pthreads */
+
+ filename= "test2";
+ filename2= "test2_2";
+ file=file2=0;
+ get_options(argc,argv);
+
+ bzero(&hp_create_info, sizeof(hp_create_info));
+ hp_create_info.max_table_size= 1024L*1024L;
+
+ write_count=update=opt_delete=0;
+ key_check=0;
+
+ keyinfo[0].seg=keyseg;
+ keyinfo[0].keysegs=1;
+ keyinfo[0].flag= 0;
+ keyinfo[0].algorithm= HA_KEY_ALG_HASH;
+ keyinfo[0].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[0].seg[0].start=0;
+ keyinfo[0].seg[0].length=6;
+ keyinfo[0].seg[0].null_bit=0;
+ keyinfo[0].seg[0].charset=cs;
+ keyinfo[1].seg=keyseg+1;
+ keyinfo[1].keysegs=2;
+ keyinfo[1].flag=0;
+ keyinfo[1].algorithm= HA_KEY_ALG_HASH;
+ keyinfo[1].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[1].seg[0].start=7;
+ keyinfo[1].seg[0].length=6;
+ keyinfo[1].seg[0].null_bit=0;
+ keyinfo[1].seg[0].charset=cs;
+ keyinfo[1].seg[1].type=HA_KEYTYPE_TEXT;
+ keyinfo[1].seg[1].start=0; /* key in two parts */
+ keyinfo[1].seg[1].length=6;
+ keyinfo[1].seg[1].null_bit=0;
+ keyinfo[1].seg[1].charset=cs;
+ keyinfo[2].seg=keyseg+3;
+ keyinfo[2].keysegs=1;
+ keyinfo[2].flag=HA_NOSAME;
+ keyinfo[2].algorithm= HA_KEY_ALG_HASH;
+ keyinfo[2].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[2].seg[0].start=12;
+ keyinfo[2].seg[0].length=8;
+ keyinfo[2].seg[0].null_bit=0;
+ keyinfo[2].seg[0].charset=cs;
+ keyinfo[3].seg=keyseg+4;
+ keyinfo[3].keysegs=1;
+ keyinfo[3].flag=HA_NOSAME;
+ keyinfo[3].algorithm= HA_KEY_ALG_HASH;
+ keyinfo[3].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[3].seg[0].start=37;
+ keyinfo[3].seg[0].length=1;
+ keyinfo[3].seg[0].null_bit=1;
+ keyinfo[3].seg[0].null_pos=38;
+ keyinfo[3].seg[0].charset=cs;
+
+ bzero((char*) key1,sizeof(key1));
+ bzero((char*) key3,sizeof(key3));
+
+ printf("- Creating heap-file\n");
+ if (heap_create(filename,keys,keyinfo,reclength,(ulong) flag*100000L,
+ (ulong) recant/2, &hp_create_info, &tmp_share) ||
+ !(file= heap_open(filename, 2)))
+ goto err;
+ signal(SIGINT,endprog);
+
+ printf("- Writing records:s\n");
+ strmov((char*) record," ..... key");
+
+ for (i=0 ; i < recant ; i++)
+ {
+ n1=rnd(1000); n2=rnd(100); n3=rnd(min(recant*5,MAX_RECORDS));
+ make_record(record,n1,n2,n3,"Pos",write_count);
+
+ if (heap_write(file,record))
+ {
+ if (my_errno != HA_ERR_FOUND_DUPP_KEY || key3[n3] == 0)
+ {
+ printf("Error: %d in write at record: %d\n",my_errno,i);
+ goto err;
+ }
+ if (verbose) printf(" Double key: %d\n",n3);
+ }
+ else
+ {
+ if (key3[n3] == 1)
+ {
+ printf("Error: Didn't get error when writing second key: '%8d'\n",n3);
+ goto err;
+ }
+ write_count++; key1[n1]++; key3[n3]=1;
+ key_check+=n1;
+ }
+ if (testflag == 1 && heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+ }
+ if (testflag == 1)
+ goto end;
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+
+ printf("- Delete\n");
+ for (i=0 ; i < write_count/10 ; i++)
+ {
+ for (j=rnd(1000)+1 ; j>0 && key1[j] == 0 ; j--) ;
+ if (j != 0)
+ {
+ sprintf((char*) key,"%6d",j);
+ if (heap_rkey(file,record,0,key,6, HA_READ_KEY_EXACT))
+ {
+ printf("can't find key1: \"%s\"\n",(char*) key);
+ goto err;
+ }
+#ifdef NOT_USED
+ if (file->current_ptr == hp_find_block(&file->s->block,0) ||
+ file->current_ptr == hp_find_block(&file->s->block,1))
+ continue; /* Don't remove 2 first records */
+#endif
+ if (heap_delete(file,record))
+ {
+ printf("error: %d; can't delete record: \"%s\"\n", my_errno,(char*) record);
+ goto err;
+ }
+ opt_delete++;
+ key1[atoi((char*) record+keyinfo[0].seg[0].start)]--;
+ key3[atoi((char*) record+keyinfo[2].seg[0].start)]=0;
+ key_check-=atoi((char*) record);
+ if (testflag == 2 && heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+ }
+ else
+ puts("Warning: Skipping delete test because no dupplicate keys");
+ }
+ if (testflag==2) goto end;
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+
+ printf("- Update\n");
+ for (i=0 ; i < write_count/10 ; i++)
+ {
+ n1=rnd(1000); n2=rnd(100); n3=rnd(min(recant*2,MAX_RECORDS));
+ make_record(record2, n1, n2, n3, "XXX", update);
+ if (rnd(2) == 1)
+ {
+ if (heap_scan_init(file))
+ goto err;
+ j=rnd(write_count-opt_delete);
+ while ((error=heap_scan(file,record) == HA_ERR_RECORD_DELETED) ||
+ (!error && j))
+ {
+ if (!error)
+ j--;
+ }
+ if (error)
+ goto err;
+ }
+ else
+ {
+ for (j=rnd(1000)+1 ; j>0 && key1[j] == 0 ; j--) ;
+ if (!key1[j])
+ continue;
+ sprintf((char*) key,"%6d",j);
+ if (heap_rkey(file,record,0,key,6, HA_READ_KEY_EXACT))
+ {
+ printf("can't find key1: \"%s\"\n",(char*) key);
+ goto err;
+ }
+ }
+ if (heap_update(file,record,record2))
+ {
+ if (my_errno != HA_ERR_FOUND_DUPP_KEY || key3[n3] == 0)
+ {
+ printf("error: %d; can't update:\nFrom: \"%s\"\nTo: \"%s\"\n",
+ my_errno,(char*) record, (char*) record2);
+ goto err;
+ }
+ if (verbose)
+ printf("Double key when tried to update:\nFrom: \"%s\"\nTo: \"%s\"\n",
+ (char*) record, (char*) record2);
+ }
+ else
+ {
+ key1[atoi((char*) record+keyinfo[0].seg[0].start)]--;
+ key3[atoi((char*) record+keyinfo[2].seg[0].start)]=0;
+ key1[n1]++; key3[n3]=1;
+ update++;
+ key_check=key_check-atoi((char*) record)+n1;
+ }
+ if (testflag == 3 && heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+ }
+ if (testflag == 3) goto end;
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+
+ for (i=999, dupp_keys=found_key=0 ; i>0 ; i--)
+ {
+ if (key1[i] > dupp_keys) { dupp_keys=key1[i]; found_key=i; }
+ sprintf((char*) key,"%6d",found_key);
+ }
+
+ if (dupp_keys > 3)
+ {
+ if (!silent)
+ printf("- Read first key - next - delete - next -> last\n");
+ DBUG_PRINT("progpos",("first - next - delete - next -> last"));
+
+ if (heap_rkey(file,record,0,key,6, HA_READ_KEY_EXACT))
+ goto err;
+ if (heap_rnext(file,record3)) goto err;
+ if (heap_delete(file,record3)) goto err;
+ key_check-=atoi((char*) record3);
+ key1[atoi((char*) record+keyinfo[0].seg[0].start)]--;
+ key3[atoi((char*) record+keyinfo[2].seg[0].start)]=0;
+ opt_delete++;
+ ant=2;
+ while ((error=heap_rnext(file,record3)) == 0 ||
+ error == HA_ERR_RECORD_DELETED)
+ if (! error)
+ ant++;
+ if (ant != dupp_keys)
+ {
+ printf("next: I can only find: %d records of %d\n",
+ ant,dupp_keys);
+ goto end;
+ }
+ dupp_keys--;
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+
+ if (!silent)
+ printf("- Read last key - delete - prev - prev - opt_delete - prev -> first\n");
+
+ if (heap_rlast(file,record3,0)) goto err;
+ if (heap_delete(file,record3)) goto err;
+ key_check-=atoi((char*) record3);
+ key1[atoi((char*) record+keyinfo[0].seg[0].start)]--;
+ key3[atoi((char*) record+keyinfo[2].seg[0].start)]=0;
+ opt_delete++;
+ if (heap_rprev(file,record3) || heap_rprev(file,record3))
+ goto err;
+ if (heap_delete(file,record3)) goto err;
+ key_check-=atoi((char*) record3);
+ key1[atoi((char*) record+keyinfo[0].seg[0].start)]--;
+ key3[atoi((char*) record+keyinfo[2].seg[0].start)]=0;
+ opt_delete++;
+ ant=3;
+ while ((error=heap_rprev(file,record3)) == 0 ||
+ error == HA_ERR_RECORD_DELETED)
+ {
+ if (! error)
+ ant++;
+ }
+ if (ant != dupp_keys)
+ {
+ printf("next: I can only find: %d records of %d\n",
+ ant,dupp_keys);
+ goto end;
+ }
+ dupp_keys-=2;
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+ }
+ else
+ puts("Warning: Not enough duplicated keys: Skipping delete key check");
+
+ if (!silent)
+ printf("- Read (first) - next - delete - next -> last\n");
+ DBUG_PRINT("progpos",("first - next - delete - next -> last"));
+
+ if (heap_scan_init(file))
+ goto err;
+ while ((error=heap_scan(file,record3) == HA_ERR_RECORD_DELETED)) ;
+ if (error)
+ goto err;
+ if (heap_delete(file,record3)) goto err;
+ key_check-=atoi((char*) record3);
+ opt_delete++;
+ key1[atoi((char*) record+keyinfo[0].seg[0].start)]--;
+ key3[atoi((char*) record+keyinfo[2].seg[0].start)]=0;
+ ant=0;
+ while ((error=heap_scan(file,record3)) == 0 ||
+ error == HA_ERR_RECORD_DELETED)
+ if (! error)
+ ant++;
+ if (ant != write_count-opt_delete)
+ {
+ printf("next: Found: %d records of %d\n",ant,write_count-opt_delete);
+ goto end;
+ }
+ if (heap_check_heap(file,0))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+
+ puts("- Test if: Read rrnd - same - rkey - same");
+ DBUG_PRINT("progpos",("Read rrnd - same"));
+ pos=rnd(write_count-opt_delete-5)+5;
+ heap_scan_init(file);
+ i=5;
+ while ((error=heap_scan(file,record)) == HA_ERR_RECORD_DELETED ||
+ (error == 0 && pos))
+ {
+ if (!error)
+ pos--;
+ if (i-- == 0)
+ {
+ bmove(record3,record,reclength);
+ position=heap_position(file);
+ }
+ }
+ if (error)
+ goto err;
+ bmove(record2,record,reclength);
+ if (heap_rsame(file,record,-1) || heap_rsame(file,record2,2))
+ goto err;
+ if (memcmp(record2,record,reclength))
+ {
+ puts("heap_rsame didn't find right record");
+ goto end;
+ }
+
+ puts("- Test of read through position");
+ if (heap_rrnd(file,record,position))
+ goto err;
+ if (memcmp(record3,record,reclength))
+ {
+ puts("heap_frnd didn't find right record");
+ goto end;
+ }
+
+ printf("- heap_info\n");
+ {
+ HEAPINFO info;
+ heap_info(file,&info,0);
+ /* We have to test with opt_delete +1 as this may be the case if the last
+ inserted row was a duplicate key */
+ if (info.records != write_count-opt_delete ||
+ (info.deleted != opt_delete && info.deleted != opt_delete+1))
+ {
+ puts("Wrong info from heap_info");
+ printf("Got: records: %ld(%d) deleted: %ld(%d)\n",
+ info.records,write_count-opt_delete,info.deleted,opt_delete);
+ }
+ }
+
+#ifdef OLD_HEAP_VERSION
+ {
+ uint check;
+ printf("- Read through all records with rnd\n");
+ if (heap_extra(file,HA_EXTRA_RESET) || heap_extra(file,HA_EXTRA_CACHE))
+ {
+ puts("got error from heap_extra");
+ goto end;
+ }
+ ant=check=0;
+ while ((error=heap_rrnd(file,record,(ulong) -1)) != HA_ERR_END_OF_FILE &&
+ ant < write_count + 10)
+ {
+ if (!error)
+ {
+ ant++;
+ check+=calc_check(record,reclength);
+ }
+ }
+ if (ant != write_count-opt_delete)
+ {
+ printf("rrnd: I can only find: %d records of %d\n", ant,
+ write_count-opt_delete);
+ goto end;
+ }
+ if (heap_extra(file,HA_EXTRA_NO_CACHE))
+ {
+ puts("got error from heap_extra(HA_EXTRA_NO_CACHE)");
+ goto end;
+ }
+ }
+#endif
+
+ printf("- Read through all records with scan\n");
+ if (heap_reset(file) || heap_extra(file,HA_EXTRA_CACHE))
+ {
+ puts("got error from heap_extra");
+ goto end;
+ }
+ ant=check2=0;
+ heap_scan_init(file);
+ while ((error=heap_scan(file,record)) != HA_ERR_END_OF_FILE &&
+ ant < write_count + 10)
+ {
+ if (!error)
+ {
+ ant++;
+ check2+=calc_check(record,reclength);
+ }
+ }
+ if (ant != write_count-opt_delete)
+ {
+ printf("scan: I can only find: %d records of %d\n", ant,
+ write_count-opt_delete);
+ goto end;
+ }
+#ifdef OLD_HEAP_VERSION
+ if (check != check2)
+ {
+ puts("scan: Checksum didn't match reading with rrnd");
+ goto end;
+ }
+#endif
+
+
+ if (heap_extra(file,HA_EXTRA_NO_CACHE))
+ {
+ puts("got error from heap_extra(HA_EXTRA_NO_CACHE)");
+ goto end;
+ }
+
+ for (i=999, dupp_keys=found_key=0 ; i>0 ; i--)
+ {
+ if (key1[i] > dupp_keys) { dupp_keys=key1[i]; found_key=i; }
+ sprintf((char*) key,"%6d",found_key);
+ }
+ printf("- Read through all keys with first-next-last-prev\n");
+ ant=0;
+ for (error=heap_rkey(file,record,0,key,6, HA_READ_KEY_EXACT);
+ ! error ;
+ error=heap_rnext(file,record))
+ ant++;
+ if (ant != dupp_keys)
+ {
+ printf("first-next: I can only find: %d records of %d\n", ant,
+ dupp_keys);
+ goto end;
+ }
+
+ ant=0;
+ for (error=heap_rlast(file,record,0) ;
+ ! error ;
+ error=heap_rprev(file,record))
+ {
+ ant++;
+ check2+=calc_check(record,reclength);
+ }
+ if (ant != dupp_keys)
+ {
+ printf("last-prev: I can only find: %d records of %d\n", ant,
+ dupp_keys);
+ goto end;
+ }
+
+ if (testflag == 4) goto end;
+
+ printf("- Reading through all rows through keys\n");
+ if (!(file2=heap_open(filename, 2)))
+ goto err;
+ if (heap_scan_init(file))
+ goto err;
+ while ((error=heap_scan(file,record)) != HA_ERR_END_OF_FILE)
+ {
+ if (error == 0)
+ {
+ if (heap_rkey(file2,record2,2,record+keyinfo[2].seg[0].start,8,
+ HA_READ_KEY_EXACT))
+ {
+ printf("can't find key3: \"%.8s\"\n",
+ record+keyinfo[2].seg[0].start);
+ goto err;
+ }
+ }
+ }
+ heap_close(file2);
+
+ printf("- Creating output heap-file 2\n");
+ if (heap_create(filename2, 1, keyinfo, reclength, 0L, 0L, &hp_create_info,
+ &tmp_share) ||
+ !(file2= heap_open_from_share_and_register(tmp_share, 2)))
+ goto err;
+
+ printf("- Copying and removing records\n");
+ if (heap_scan_init(file))
+ goto err;
+ while ((error=heap_scan(file,record)) != HA_ERR_END_OF_FILE)
+ {
+ if (error == 0)
+ {
+ if (heap_write(file2,record))
+ goto err;
+ key_check-=atoi((char*) record);
+ write_count++;
+ if (heap_delete(file,record))
+ goto err;
+ opt_delete++;
+ }
+ pos++;
+ }
+ printf("- Checking heap tables\n");
+ if (heap_check_heap(file,1) || heap_check_heap(file2,1))
+ {
+ puts("Heap keys crashed");
+ goto err;
+ }
+
+ if (my_errno != HA_ERR_END_OF_FILE)
+ printf("error: %d from heap_rrnd\n",my_errno);
+ if (key_check)
+ printf("error: Some read got wrong: check is %ld\n",(long) key_check);
+
+end:
+ printf("\nFollowing test have been made:\n");
+ printf("Write records: %d\nUpdate records: %d\nDelete records: %d\n", write_count,update,opt_delete);
+ heap_clear(file);
+ if (heap_close(file) || (file2 && heap_close(file2)))
+ goto err;
+ heap_delete_table(filename2);
+ hp_panic(HA_PANIC_CLOSE);
+ my_end(MY_GIVE_INFO);
+ return(0);
+err:
+ printf("Got error: %d when using heap-database\n",my_errno);
+ VOID(heap_close(file));
+ return(1);
+} /* main */
+
+
+ /* Read options */
+
+static int get_options(int argc,char *argv[])
+{
+ char *pos,*progname;
+
+ progname= argv[0];
+
+ while (--argc >0 && *(pos = *(++argv)) == '-' ) {
+ switch(*++pos) {
+ case 'B': /* Big file */
+ flag=1;
+ break;
+ case 'v': /* verbose */
+ verbose=1;
+ break;
+ case 'm': /* records */
+ recant=atoi(++pos);
+ break;
+ case 's':
+ silent=1;
+ break;
+ case 't':
+ testflag=atoi(++pos); /* testmod */
+ break;
+ case 'V':
+ case 'I':
+ case '?':
+ printf("%s Ver 1.1 for %s at %s\n",progname,SYSTEM_TYPE,MACHINE_TYPE);
+ puts("TCX Datakonsult AB, by Monty, for your professional use\n");
+ printf("Usage: %s [-?ABIKLsWv] [-m#] [-t#]\n",progname);
+ exit(0);
+ case '#':
+ DBUG_PUSH (++pos);
+ break;
+ }
+ }
+ return 0;
+} /* get options */
+
+ /* Generate a random value in intervall 0 <=x <= n */
+
+static int rnd(int max_value)
+{
+ return (int) ((rand() & 32767)/32767.0*max_value);
+} /* rnd */
+
+
+static sig_handler endprog(int sig_number __attribute__((unused)))
+{
+#ifndef THREAD
+ if (my_dont_interrupt)
+ my_remember_signal(sig_number,endprog);
+ else
+#endif
+ {
+ hp_panic(HA_PANIC_CLOSE);
+ my_end(1);
+ exit(1);
+ }
+}
+
+static int calc_check(uchar *buf, uint length)
+{
+ int check=0;
+ while (length--)
+ check+= (int) (uchar) *(buf++);
+ return check;
+}
+
+static void make_record(uchar *record, uint n1, uint n2, uint n3,
+ const char *mark, uint count)
+{
+ bfill(record,reclength,' ');
+ sprintf((char*) record,"%6d:%4d:%8d:%3.3s: %4d",
+ n1,n2,n3,mark,count);
+ record[37]='A'; /* Store A in null key */
+ record[38]=1; /* set as null */
+}
diff --git a/storage/heap/hp_update.c b/storage/heap/hp_update.c
new file mode 100644
index 00000000000..7f469af3c96
--- /dev/null
+++ b/storage/heap/hp_update.c
@@ -0,0 +1,88 @@
+/* Copyright (C) 2000-2002, 2004-2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Update current record in heap-database */
+
+#include "heapdef.h"
+
+int heap_update(HP_INFO *info, const uchar *old, const uchar *heap_new)
+{
+ HP_KEYDEF *keydef, *end, *p_lastinx;
+ uchar *pos;
+ my_bool auto_key_changed= 0;
+ HP_SHARE *share= info->s;
+ DBUG_ENTER("heap_update");
+
+ test_active(info);
+ pos=info->current_ptr;
+
+ if (info->opt_flag & READ_CHECK_USED && hp_rectest(info,old))
+ DBUG_RETURN(my_errno); /* Record changed */
+ if (--(share->records) < share->blength >> 1) share->blength>>= 1;
+ share->changed=1;
+
+ p_lastinx= share->keydef + info->lastinx;
+ for (keydef= share->keydef, end= keydef + share->keys; keydef < end; keydef++)
+ {
+ if (hp_rec_key_cmp(keydef, old, heap_new, 0))
+ {
+ if ((*keydef->delete_key)(info, keydef, old, pos, keydef == p_lastinx) ||
+ (*keydef->write_key)(info, keydef, heap_new, pos))
+ goto err;
+ if (share->auto_key == (uint) (keydef - share->keydef + 1))
+ auto_key_changed= 1;
+ }
+ }
+
+ memcpy(pos,heap_new,(size_t) share->reclength);
+ if (++(share->records) == share->blength) share->blength+= share->blength;
+
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
+ DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
+#endif
+ if (auto_key_changed)
+ heap_update_auto_increment(info, heap_new);
+ DBUG_RETURN(0);
+
+ err:
+ if (my_errno == HA_ERR_FOUND_DUPP_KEY)
+ {
+ info->errkey = (int) (keydef - share->keydef);
+ if (keydef->algorithm == HA_KEY_ALG_BTREE)
+ {
+ /* we don't need to delete non-inserted key from rb-tree */
+ if ((*keydef->write_key)(info, keydef, old, pos))
+ {
+ if (++(share->records) == share->blength)
+ share->blength+= share->blength;
+ DBUG_RETURN(my_errno);
+ }
+ keydef--;
+ }
+ while (keydef >= share->keydef)
+ {
+ if (hp_rec_key_cmp(keydef, old, heap_new, 0))
+ {
+ if ((*keydef->delete_key)(info, keydef, heap_new, pos, 0) ||
+ (*keydef->write_key)(info, keydef, old, pos))
+ break;
+ }
+ keydef--;
+ }
+ }
+ if (++(share->records) == share->blength)
+ share->blength+= share->blength;
+ DBUG_RETURN(my_errno);
+} /* heap_update */
diff --git a/storage/heap/hp_write.c b/storage/heap/hp_write.c
new file mode 100644
index 00000000000..fe83fb1e8e7
--- /dev/null
+++ b/storage/heap/hp_write.c
@@ -0,0 +1,403 @@
+/* Copyright (C) 2000-2002, 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Write a record to heap-databas */
+
+#include "heapdef.h"
+#ifdef __WIN__
+#include <fcntl.h>
+#endif
+
+#define LOWFIND 1
+#define LOWUSED 2
+#define HIGHFIND 4
+#define HIGHUSED 8
+
+static uchar *next_free_record_pos(HP_SHARE *info);
+static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
+ ulong records);
+
+int heap_write(HP_INFO *info, const uchar *record)
+{
+ HP_KEYDEF *keydef, *end;
+ uchar *pos;
+ HP_SHARE *share=info->s;
+ DBUG_ENTER("heap_write");
+#ifndef DBUG_OFF
+ if (info->mode & O_RDONLY)
+ {
+ DBUG_RETURN(my_errno=EACCES);
+ }
+#endif
+ if (!(pos=next_free_record_pos(share)))
+ DBUG_RETURN(my_errno);
+ share->changed=1;
+
+ for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
+ keydef++)
+ {
+ if ((*keydef->write_key)(info, keydef, record, pos))
+ goto err;
+ }
+
+ memcpy(pos,record,(size_t) share->reclength);
+ pos[share->reclength]=1; /* Mark record as not deleted */
+ if (++share->records == share->blength)
+ share->blength+= share->blength;
+ info->current_ptr=pos;
+ info->current_hash_ptr=0;
+ info->update|=HA_STATE_AKTIV;
+#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
+ DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
+#endif
+ if (share->auto_key)
+ heap_update_auto_increment(info, record);
+ DBUG_RETURN(0);
+
+err:
+ if (my_errno == HA_ERR_FOUND_DUPP_KEY)
+ DBUG_PRINT("info",("Duplicate key: %d", (int) (keydef - share->keydef)));
+ info->errkey= (int) (keydef - share->keydef);
+ /*
+ We don't need to delete non-inserted key from rb-tree. Also, if
+ we got ENOMEM, the key wasn't inserted, so don't try to delete it
+ either. Otherwise for HASH index on HA_ERR_FOUND_DUPP_KEY the key
+ was inserted and we have to delete it.
+ */
+ if (keydef->algorithm == HA_KEY_ALG_BTREE || my_errno == ENOMEM)
+ {
+ keydef--;
+ }
+ while (keydef >= share->keydef)
+ {
+ if ((*keydef->delete_key)(info, keydef, record, pos, 0))
+ break;
+ keydef--;
+ }
+
+ share->deleted++;
+ *((uchar**) pos)=share->del_link;
+ share->del_link=pos;
+ pos[share->reclength]=0; /* Record deleted */
+
+ DBUG_RETURN(my_errno);
+} /* heap_write */
+
+/*
+ Write a key to rb_tree-index
+*/
+
+int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *record,
+ uchar *recpos)
+{
+ heap_rb_param custom_arg;
+ uint old_allocated;
+
+ custom_arg.keyseg= keyinfo->seg;
+ custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
+ if (keyinfo->flag & HA_NOSAME)
+ {
+ custom_arg.search_flag= SEARCH_FIND | SEARCH_UPDATE;
+ keyinfo->rb_tree.flag= TREE_NO_DUPS;
+ }
+ else
+ {
+ custom_arg.search_flag= SEARCH_SAME;
+ keyinfo->rb_tree.flag= 0;
+ }
+ old_allocated= keyinfo->rb_tree.allocated;
+ if (!tree_insert(&keyinfo->rb_tree, (void*)info->recbuf,
+ custom_arg.key_length, &custom_arg))
+ {
+ my_errno= HA_ERR_FOUND_DUPP_KEY;
+ return 1;
+ }
+ info->s->index_length+= (keyinfo->rb_tree.allocated-old_allocated);
+ return 0;
+}
+
+ /* Find where to place new record */
+
+static uchar *next_free_record_pos(HP_SHARE *info)
+{
+ int block_pos;
+ uchar *pos;
+ size_t length;
+ DBUG_ENTER("next_free_record_pos");
+
+ if (info->del_link)
+ {
+ pos=info->del_link;
+ info->del_link= *((uchar**) pos);
+ info->deleted--;
+ DBUG_PRINT("exit",("Used old position: 0x%lx",(long) pos));
+ DBUG_RETURN(pos);
+ }
+ if (!(block_pos=(info->records % info->block.records_in_block)))
+ {
+ if ((info->records > info->max_records && info->max_records) ||
+ (info->data_length + info->index_length >= info->max_table_size))
+ {
+ my_errno=HA_ERR_RECORD_FILE_FULL;
+ DBUG_RETURN(NULL);
+ }
+ if (hp_get_new_block(&info->block,&length))
+ DBUG_RETURN(NULL);
+ info->data_length+=length;
+ }
+ DBUG_PRINT("exit",("Used new position: 0x%lx",
+ (long) ((uchar*) info->block.level_info[0].last_blocks+
+ block_pos * info->block.recbuffer)));
+ DBUG_RETURN((uchar*) info->block.level_info[0].last_blocks+
+ block_pos*info->block.recbuffer);
+}
+
+
+/*
+ Write a hash-key to the hash-index
+ SYNOPSIS
+ info Heap table info
+ keyinfo Key info
+ record Table record to added
+ recpos Memory buffer where the table record will be stored if added
+ successfully
+ NOTE
+ Hash index uses HP_BLOCK structure as a 'growable array' of HASH_INFO
+ structs. Array size == number of entries in hash index.
+ hp_mask(hp_rec_hashnr()) maps hash entries values to hash array positions.
+ If there are several hash entries with the same hash array position P,
+ they are connected in a linked list via HASH_INFO::next_key. The first
+ list element is located at position P, next elements are located at
+ positions for which there is no record that should be located at that
+ position. The order of elements in the list is arbitrary.
+
+ RETURN
+ 0 - OK
+ -1 - Out of memory
+ HA_ERR_FOUND_DUPP_KEY - Duplicate record on unique key. The record was
+ still added and the caller must call hp_delete_key for it.
+*/
+
+int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
+ const uchar *record, uchar *recpos)
+{
+ HP_SHARE *share = info->s;
+ int flag;
+ ulong halfbuff,hashnr,first_index;
+ uchar *UNINIT_VAR(ptr_to_rec),*UNINIT_VAR(ptr_to_rec2);
+ HASH_INFO *empty,*UNINIT_VAR(gpos),*UNINIT_VAR(gpos2),*pos;
+ DBUG_ENTER("hp_write_key");
+
+ flag=0;
+ if (!(empty= hp_find_free_hash(share,&keyinfo->block,share->records)))
+ DBUG_RETURN(-1); /* No more memory */
+ halfbuff= (long) share->blength >> 1;
+ pos= hp_find_hash(&keyinfo->block,(first_index=share->records-halfbuff));
+
+ /*
+ We're about to add one more hash array position, with hash_mask=#records.
+ The number of hash positions will change and some entries might need to
+ be relocated to the newly added position. Those entries are currently
+ members of the list that starts at #first_index position (this is
+ guaranteed by properties of hp_mask(hp_rec_hashnr(X)) mapping function)
+ At #first_index position currently there may be either:
+ a) An entry with hashnr != first_index. We don't need to move it.
+ or
+ b) A list of items with hash_mask=first_index. The list contains entries
+ of 2 types:
+ 1) entries that should be relocated to the list that starts at new
+ position we're adding ('uppper' list)
+ 2) entries that should be left in the list starting at #first_index
+ position ('lower' list)
+ */
+ if (pos != empty) /* If some records */
+ {
+ do
+ {
+ hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
+ if (flag == 0)
+ {
+ /*
+ First loop, bail out if we're dealing with case a) from above
+ comment
+ */
+ if (hp_mask(hashnr, share->blength, share->records) != first_index)
+ break;
+ }
+ /*
+ flag & LOWFIND - found a record that should be put into lower position
+ flag & LOWUSED - lower position occupied by the record
+ Same for HIGHFIND and HIGHUSED and 'upper' position
+
+ gpos - ptr to last element in lower position's list
+ gpos2 - ptr to last element in upper position's list
+
+ ptr_to_rec - ptr to last entry that should go into lower list.
+ ptr_to_rec2 - same for upper list.
+ */
+ if (!(hashnr & halfbuff))
+ {
+ /* Key should be put into 'lower' list */
+ if (!(flag & LOWFIND))
+ {
+ /* key is the first element to go into lower position */
+ if (flag & HIGHFIND)
+ {
+ flag=LOWFIND | HIGHFIND;
+ /* key shall be moved to the current empty position */
+ gpos=empty;
+ ptr_to_rec=pos->ptr_to_rec;
+ empty=pos; /* This place is now free */
+ }
+ else
+ {
+ /*
+ We can only get here at first iteration: key is at 'lower'
+ position pos and should be left here.
+ */
+ flag=LOWFIND | LOWUSED;
+ gpos=pos;
+ ptr_to_rec=pos->ptr_to_rec;
+ }
+ }
+ else
+ {
+ /* Already have another key for lower position */
+ if (!(flag & LOWUSED))
+ {
+ /* Change link of previous lower-list key */
+ gpos->ptr_to_rec=ptr_to_rec;
+ gpos->next_key=pos;
+ flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
+ }
+ gpos=pos;
+ ptr_to_rec=pos->ptr_to_rec;
+ }
+ }
+ else
+ {
+ /* key will be put into 'higher' list */
+ if (!(flag & HIGHFIND))
+ {
+ flag= (flag & LOWFIND) | HIGHFIND;
+ /* key shall be moved to the last (empty) position */
+ gpos2= empty;
+ empty= pos;
+ ptr_to_rec2=pos->ptr_to_rec;
+ }
+ else
+ {
+ if (!(flag & HIGHUSED))
+ {
+ /* Change link of previous upper-list key and save */
+ gpos2->ptr_to_rec=ptr_to_rec2;
+ gpos2->next_key=pos;
+ flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
+ }
+ gpos2=pos;
+ ptr_to_rec2=pos->ptr_to_rec;
+ }
+ }
+ }
+ while ((pos=pos->next_key));
+
+ if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
+ {
+ /*
+ If both 'higher' and 'lower' list have at least one element, now
+ there are two hash buckets instead of one.
+ */
+ keyinfo->hash_buckets++;
+ }
+
+ if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
+ {
+ gpos->ptr_to_rec=ptr_to_rec;
+ gpos->next_key=0;
+ }
+ if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
+ {
+ gpos2->ptr_to_rec=ptr_to_rec2;
+ gpos2->next_key=0;
+ }
+ }
+ /* Check if we are at the empty position */
+
+ pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
+ share->blength, share->records + 1));
+ if (pos == empty)
+ {
+ pos->ptr_to_rec=recpos;
+ pos->next_key=0;
+ keyinfo->hash_buckets++;
+ }
+ else
+ {
+ /* Check if more records in same hash-nr family */
+ empty[0]=pos[0];
+ gpos=hp_find_hash(&keyinfo->block,
+ hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
+ share->blength, share->records + 1));
+ if (pos == gpos)
+ {
+ pos->ptr_to_rec=recpos;
+ pos->next_key=empty;
+ }
+ else
+ {
+ keyinfo->hash_buckets++;
+ pos->ptr_to_rec=recpos;
+ pos->next_key=0;
+ hp_movelink(pos, gpos, empty);
+ }
+
+ /* Check if duplicated keys */
+ if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
+ (!(keyinfo->flag & HA_NULL_PART_KEY) ||
+ !hp_if_null_in_key(keyinfo, record)))
+ {
+ pos=empty;
+ do
+ {
+ if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec, 1))
+ {
+ DBUG_RETURN(my_errno=HA_ERR_FOUND_DUPP_KEY);
+ }
+ } while ((pos=pos->next_key));
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+ /* Returns ptr to block, and allocates block if neaded */
+
+static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
+ HP_BLOCK *block, ulong records)
+{
+ uint block_pos;
+ size_t length;
+
+ if (records < block->last_allocated)
+ return hp_find_hash(block,records);
+ if (!(block_pos=(records % block->records_in_block)))
+ {
+ if (hp_get_new_block(block,&length))
+ return(NULL);
+ info->index_length+=length;
+ }
+ block->last_allocated=records+1;
+ return((HASH_INFO*) ((uchar*) block->level_info[0].last_blocks+
+ block_pos*block->recbuffer));
+}
diff --git a/storage/heap/make-ccc b/storage/heap/make-ccc
new file mode 100755
index 00000000000..192647298ad
--- /dev/null
+++ b/storage/heap/make-ccc
@@ -0,0 +1,4 @@
+ccc -I./../include -I../include -DDBUG_OFF -fast -O3 -c _check.c _rectest.c hp_block.c hp_clear.c hp_close.c hp_create.c hp_delete.c hp_extra.c hp_hash.c hp_info.c hp_open.c hp_panic.c hp_rename.c hp_rfirst.c hp_rkey.c hp_rlast.c hp_rnext.c hp_rprev.c hp_rrnd.c hp_rsame.c hp_scan.c hp_static.c hp_update.c hp_write.c
+rm libheap.a
+ar -cr libheap.a _check.o
+
diff --git a/storage/heap/plug.in b/storage/heap/plug.in
new file mode 100644
index 00000000000..50f31c60f2b
--- /dev/null
+++ b/storage/heap/plug.in
@@ -0,0 +1,7 @@
+MYSQL_STORAGE_ENGINE(heap,no, [Memory Storage Engine],
+ [Volatile memory based tables])
+MYSQL_PLUGIN_DIRECTORY(heap, [storage/heap])
+MYSQL_PLUGIN_STATIC(heap, [libheap.a])
+MYSQL_PLUGIN_MANDATORY(heap) dnl Memory tables
+MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(heap, [ha_heap.cc])
+