00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "heap_priv.h"
00017 #include <drizzled/error.h>
00018 #include <drizzled/table.h>
00019 #include <drizzled/session.h>
00020 #include <drizzled/field/varstring.h>
00021 #include <drizzled/plugin/daemon.h>
00022 #include <drizzled/plugin/storage_engine.h>
00023 #include <drizzled/util/test.h>
00024 #include <drizzled/session/table_messages.h>
00025 #include <drizzled/statistics_variables.h>
00026 #include <drizzled/system_variables.h>
00027
00028 #include <boost/thread/mutex.hpp>
00029
00030 #include "heap.h"
00031 #include "ha_heap.h"
00032
00033 #include <string>
00034
00035 using namespace drizzled;
00036 using namespace std;
00037
00038 static const string engine_name("MEMORY");
00039
00040 boost::mutex THR_LOCK_heap;
00041
00042 static const char *ha_heap_exts[] = {
00043 NULL
00044 };
00045
00046 class HeapEngine : public plugin::StorageEngine
00047 {
00048 public:
00049 explicit HeapEngine(string name_arg) :
00050 plugin::StorageEngine(name_arg,
00051 HTON_STATS_RECORDS_IS_EXACT |
00052 HTON_NULL_IN_KEY |
00053 HTON_FAST_KEY_READ |
00054 HTON_NO_BLOBS |
00055 HTON_HAS_RECORDS |
00056 HTON_SKIP_STORE_LOCK |
00057 HTON_TEMPORARY_ONLY)
00058 {
00059 }
00060
00061 virtual ~HeapEngine()
00062 {
00063 hp_panic(HA_PANIC_CLOSE);
00064 }
00065
00066 virtual Cursor *create(Table &table)
00067 {
00068 return new ha_heap(*this, table);
00069 }
00070
00071 const char **bas_ext() const
00072 {
00073 return ha_heap_exts;
00074 }
00075
00076 drizzled::message::Table::Index::IndexType default_index_type() const
00077 {
00078 return drizzled::message::Table::Index::HASH;
00079 }
00080
00081 int doCreateTable(Session &session,
00082 Table &table_arg,
00083 const identifier::Table &identifier,
00084 const message::Table &create_proto);
00085
00086
00087
00088
00089
00090
00091 int heap_create_table(Session *session, const char *table_name,
00092 Table *table_arg,
00093 bool internal_table,
00094 const message::Table &create_proto,
00095 HP_SHARE **internal_share);
00096
00097 int doRenameTable(Session&, const identifier::Table &from, const identifier::Table &to);
00098
00099 int doDropTable(Session&, const identifier::Table &identifier);
00100
00101 int doGetTableDefinition(Session& session,
00102 const identifier::Table &identifier,
00103 message::Table &table_message);
00104
00105 uint32_t max_supported_keys() const { return MAX_KEY; }
00106 uint32_t max_supported_key_part_length() const { return MAX_KEY_LENGTH; }
00107
00108 uint32_t index_flags(enum ha_key_alg ) const
00109 {
00110 return ( HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
00111 }
00112
00113 bool doDoesTableExist(Session& session, const identifier::Table &identifier);
00114 void doGetTableIdentifiers(CachedDirectory &directory,
00115 const identifier::Schema &schema_identifier,
00116 identifier::table::vector &set_of_identifiers);
00117 };
00118
00119 void HeapEngine::doGetTableIdentifiers(CachedDirectory&,
00120 const identifier::Schema&,
00121 identifier::table::vector&)
00122 {
00123 }
00124
00125 bool HeapEngine::doDoesTableExist(Session& session, const identifier::Table &identifier)
00126 {
00127 return session.getMessageCache().doesTableMessageExist(identifier);
00128 }
00129
00130 int HeapEngine::doGetTableDefinition(Session &session,
00131 const identifier::Table &identifier,
00132 message::Table &table_proto)
00133 {
00134 if (session.getMessageCache().getTableMessage(identifier, table_proto))
00135 return EEXIST;
00136
00137 return ENOENT;
00138 }
00139
00140
00141
00142
00143 int HeapEngine::doDropTable(Session &session, const identifier::Table &identifier)
00144 {
00145 session.getMessageCache().removeTableMessage(identifier);
00146
00147 int error= heap_delete_table(identifier.getPath().c_str());
00148
00149 if (error == ENOENT)
00150 error= 0;
00151
00152 return error;
00153 }
00154
00155 static HeapEngine *heap_storage_engine= NULL;
00156
00157 static int heap_init(module::Context &context)
00158 {
00159 heap_storage_engine= new HeapEngine(engine_name);
00160 context.add(heap_storage_engine);
00161 return 0;
00162 }
00163
00164
00165
00166
00167
00168
00169 ha_heap::ha_heap(plugin::StorageEngine &engine_arg,
00170 Table &table_arg)
00171 :Cursor(engine_arg, table_arg), file(0), records_changed(0), key_stat_version(0),
00172 internal_table(0)
00173 {}
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186 #define MEMORY_STATS_UPDATE_THRESHOLD 10
00187
00188 int ha_heap::doOpen(const drizzled::identifier::Table &identifier, int mode, uint32_t test_if_locked)
00189 {
00190 if ((test_if_locked & HA_OPEN_INTERNAL_TABLE) || (!(file= heap_open(identifier.getPath().c_str(), mode)) && errno == ENOENT))
00191 {
00192 internal_table= test(test_if_locked & HA_OPEN_INTERNAL_TABLE);
00193 file= 0;
00194 HP_SHARE *internal_share= NULL;
00195 message::Table create_proto;
00196
00197 if (not heap_storage_engine->heap_create_table(getTable()->in_use,
00198 identifier.getPath().c_str(),
00199 getTable(),
00200 internal_table,
00201 create_proto,
00202 &internal_share))
00203 {
00204 file= internal_table ?
00205 heap_open_from_share(internal_share, mode) :
00206 heap_open_from_share_and_register(internal_share, mode);
00207 if (!file)
00208 {
00209
00210 THR_LOCK_heap.lock();
00211 hp_free(internal_share);
00212 THR_LOCK_heap.unlock();
00213 }
00214 }
00215 }
00216 ref_length= sizeof(HEAP_PTR);
00217 if (file)
00218 {
00219
00220 set_keys_for_scanning();
00221
00222
00223
00224
00225
00226
00227
00228
00229 key_stat_version= file->getShare()->key_stat_version - 1;
00230 }
00231 return file ? 0 : 1;
00232 }
00233
00234 int ha_heap::close(void)
00235 {
00236 return internal_table ? hp_close(file) : heap_close(file);
00237 }
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250 Cursor *ha_heap::clone(memory::Root *)
00251 {
00252 Cursor *new_handler= getTable()->getMutableShare()->db_type()->getCursor(*getTable());
00253 identifier::Table identifier(getTable()->getShare()->getSchemaName(),
00254 getTable()->getShare()->getTableName(),
00255 getTable()->getShare()->getPath());
00256
00257 if (new_handler && !new_handler->ha_open(identifier, getTable()->db_stat,
00258 HA_OPEN_IGNORE_IF_LOCKED))
00259 return new_handler;
00260 return NULL;
00261 }
00262
00263
00264 const char *ha_heap::index_type(uint32_t )
00265 {
00266 return ("HASH");
00267 }
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286 void ha_heap::set_keys_for_scanning(void)
00287 {
00288 }
00289
00290
00291 void ha_heap::update_key_stats()
00292 {
00293 for (uint32_t i= 0; i < getTable()->getShare()->sizeKeys(); i++)
00294 {
00295 KeyInfo *key= &getTable()->key_info[i];
00296
00297 if (!key->rec_per_key)
00298 continue;
00299
00300 {
00301 if (key->flags & HA_NOSAME)
00302 key->rec_per_key[key->key_parts-1]= 1;
00303 else
00304 {
00305 ha_rows hash_buckets= file->getShare()->keydef[i].hash_buckets;
00306 uint32_t no_records= hash_buckets ? (uint) (file->getShare()->records/hash_buckets) : 2;
00307 if (no_records < 2)
00308 no_records= 2;
00309 key->rec_per_key[key->key_parts-1]= no_records;
00310 }
00311 }
00312 }
00313 records_changed= 0;
00314
00315 key_stat_version= file->getShare()->key_stat_version;
00316 }
00317
00318
00319 int ha_heap::doInsertRecord(unsigned char * buf)
00320 {
00321 int res;
00322 if (getTable()->next_number_field && buf == getTable()->getInsertRecord())
00323 {
00324 if ((res= update_auto_increment()))
00325 return res;
00326 }
00327 res= heap_write(file,buf);
00328 if (!res && (++records_changed*MEMORY_STATS_UPDATE_THRESHOLD >
00329 file->getShare()->records))
00330 {
00331
00332
00333
00334
00335 file->getShare()->key_stat_version++;
00336 }
00337 return res;
00338 }
00339
00340 int ha_heap::doUpdateRecord(const unsigned char * old_data, unsigned char * new_data)
00341 {
00342 int res;
00343
00344 res= heap_update(file,old_data,new_data);
00345 if (!res && ++records_changed*MEMORY_STATS_UPDATE_THRESHOLD >
00346 file->getShare()->records)
00347 {
00348
00349
00350
00351
00352 file->getShare()->key_stat_version++;
00353 }
00354 return res;
00355 }
00356
00357 int ha_heap::doDeleteRecord(const unsigned char * buf)
00358 {
00359 int res;
00360
00361 res= heap_delete(file,buf);
00362 if (!res && getTable()->getShare()->getType() == message::Table::STANDARD &&
00363 ++records_changed*MEMORY_STATS_UPDATE_THRESHOLD > file->getShare()->records)
00364 {
00365
00366
00367
00368
00369 file->getShare()->key_stat_version++;
00370 }
00371 return res;
00372 }
00373
00374 int ha_heap::index_read_map(unsigned char *buf, const unsigned char *key,
00375 key_part_map keypart_map,
00376 enum ha_rkey_function find_flag)
00377 {
00378 assert(inited==INDEX);
00379 ha_statistic_increment(&system_status_var::ha_read_key_count);
00380 int error = heap_rkey(file,buf,active_index, key, keypart_map, find_flag);
00381 getTable()->status = error ? STATUS_NOT_FOUND : 0;
00382 return error;
00383 }
00384
00385 int ha_heap::index_read_last_map(unsigned char *buf, const unsigned char *key,
00386 key_part_map keypart_map)
00387 {
00388 assert(inited==INDEX);
00389 ha_statistic_increment(&system_status_var::ha_read_key_count);
00390 int error= heap_rkey(file, buf, active_index, key, keypart_map,
00391 HA_READ_PREFIX_LAST);
00392 getTable()->status= error ? STATUS_NOT_FOUND : 0;
00393 return error;
00394 }
00395
00396 int ha_heap::index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key,
00397 key_part_map keypart_map,
00398 enum ha_rkey_function find_flag)
00399 {
00400 ha_statistic_increment(&system_status_var::ha_read_key_count);
00401 int error = heap_rkey(file, buf, index, key, keypart_map, find_flag);
00402 getTable()->status = error ? STATUS_NOT_FOUND : 0;
00403 return error;
00404 }
00405
00406 int ha_heap::index_next(unsigned char * buf)
00407 {
00408 assert(inited==INDEX);
00409 ha_statistic_increment(&system_status_var::ha_read_next_count);
00410 int error=heap_rnext(file,buf);
00411 getTable()->status=error ? STATUS_NOT_FOUND: 0;
00412 return error;
00413 }
00414
00415 int ha_heap::index_prev(unsigned char * buf)
00416 {
00417 assert(inited==INDEX);
00418 ha_statistic_increment(&system_status_var::ha_read_prev_count);
00419 int error=heap_rprev(file,buf);
00420 getTable()->status=error ? STATUS_NOT_FOUND: 0;
00421 return error;
00422 }
00423
00424 int ha_heap::index_first(unsigned char * buf)
00425 {
00426 assert(inited==INDEX);
00427 ha_statistic_increment(&system_status_var::ha_read_first_count);
00428 int error=heap_rfirst(file, buf, active_index);
00429 getTable()->status=error ? STATUS_NOT_FOUND: 0;
00430 return error;
00431 }
00432
00433 int ha_heap::index_last(unsigned char * buf)
00434 {
00435 assert(inited==INDEX);
00436 ha_statistic_increment(&system_status_var::ha_read_last_count);
00437 int error=heap_rlast(file, buf, active_index);
00438 getTable()->status=error ? STATUS_NOT_FOUND: 0;
00439 return error;
00440 }
00441
00442 int ha_heap::doStartTableScan(bool scan)
00443 {
00444 return scan ? heap_scan_init(file) : 0;
00445 }
00446
00447 int ha_heap::rnd_next(unsigned char *buf)
00448 {
00449 ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
00450 int error=heap_scan(file, buf);
00451 getTable()->status=error ? STATUS_NOT_FOUND: 0;
00452 return error;
00453 }
00454
00455 int ha_heap::rnd_pos(unsigned char * buf, unsigned char *pos)
00456 {
00457 int error;
00458 HEAP_PTR heap_position;
00459 ha_statistic_increment(&system_status_var::ha_read_rnd_count);
00460 memcpy(&heap_position, pos, sizeof(HEAP_PTR));
00461 error=heap_rrnd(file, buf, heap_position);
00462 getTable()->status=error ? STATUS_NOT_FOUND: 0;
00463 return error;
00464 }
00465
00466 void ha_heap::position(const unsigned char *)
00467 {
00468 *(HEAP_PTR*) ref= heap_position(file);
00469 }
00470
00471 int ha_heap::info(uint32_t flag)
00472 {
00473 HEAPINFO hp_info;
00474 (void) heap_info(file,&hp_info,flag);
00475
00476 errkey= hp_info.errkey;
00477 stats.records= hp_info.records;
00478 stats.deleted= hp_info.deleted;
00479 stats.mean_rec_length= hp_info.reclength;
00480 stats.data_file_length= hp_info.data_length;
00481 stats.index_file_length= hp_info.index_length;
00482 stats.max_data_file_length= hp_info.max_records * hp_info.reclength;
00483 stats.delete_length= hp_info.deleted * hp_info.reclength;
00484 if (flag & HA_STATUS_AUTO)
00485 stats.auto_increment_value= hp_info.auto_increment;
00486
00487
00488
00489
00490
00491 if (key_stat_version != file->getShare()->key_stat_version)
00492 update_key_stats();
00493 return 0;
00494 }
00495
00496 int ha_heap::extra(enum ha_extra_function operation)
00497 {
00498 return heap_extra(file,operation);
00499 }
00500
00501
00502 int ha_heap::reset()
00503 {
00504 return heap_reset(file);
00505 }
00506
00507
00508 int ha_heap::delete_all_rows()
00509 {
00510 heap_clear(file);
00511 if (getTable()->getShare()->getType() == message::Table::STANDARD)
00512 {
00513
00514
00515
00516
00517 file->getShare()->key_stat_version++;
00518 }
00519 return 0;
00520 }
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546 int ha_heap::disable_indexes(uint32_t mode)
00547 {
00548 int error;
00549
00550 if (mode == HA_KEY_SWITCH_ALL)
00551 {
00552 if (!(error= heap_disable_indexes(file)))
00553 set_keys_for_scanning();
00554 }
00555 else
00556 {
00557
00558 error= HA_ERR_WRONG_COMMAND;
00559 }
00560 return error;
00561 }
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593 int ha_heap::enable_indexes(uint32_t mode)
00594 {
00595 int error;
00596
00597 if (mode == HA_KEY_SWITCH_ALL)
00598 {
00599 if (!(error= heap_enable_indexes(file)))
00600 set_keys_for_scanning();
00601 }
00602 else
00603 {
00604
00605 error= HA_ERR_WRONG_COMMAND;
00606 }
00607 return error;
00608 }
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624 int ha_heap::indexes_are_disabled()
00625 {
00626 return heap_indexes_are_disabled(file);
00627 }
00628
00629 void ha_heap::drop_table()
00630 {
00631 file->getShare()->delete_on_close= 1;
00632 close();
00633 }
00634
00635
00636 int HeapEngine::doRenameTable(Session &session, const identifier::Table &from, const identifier::Table &to)
00637 {
00638 session.getMessageCache().renameTableMessage(from, to);
00639 return heap_rename(from.getPath().c_str(), to.getPath().c_str());
00640 }
00641
00642
00643 ha_rows ha_heap::records_in_range(uint32_t inx, key_range *min_key,
00644 key_range *max_key)
00645 {
00646 KeyInfo *key= &getTable()->key_info[inx];
00647
00648 if (!min_key || !max_key ||
00649 min_key->length != max_key->length ||
00650 min_key->length != key->key_length ||
00651 min_key->flag != HA_READ_KEY_EXACT ||
00652 max_key->flag != HA_READ_AFTER_KEY)
00653 return HA_POS_ERROR;
00654
00655 if (stats.records <= 1)
00656 return stats.records;
00657
00658
00659 assert(key_stat_version == file->getShare()->key_stat_version);
00660 return key->rec_per_key[key->key_parts-1];
00661 }
00662
00663 int HeapEngine::doCreateTable(Session &session,
00664 Table &table_arg,
00665 const identifier::Table &identifier,
00666 const message::Table& create_proto)
00667 {
00668 int error;
00669 HP_SHARE *internal_share;
00670 const char *table_name= identifier.getPath().c_str();
00671
00672 error= heap_create_table(&session, table_name, &table_arg,
00673 false,
00674 create_proto,
00675 &internal_share);
00676
00677 if (error == 0)
00678 {
00679 session.getMessageCache().storeTableMessage(identifier, create_proto);
00680 }
00681
00682 return error;
00683 }
00684
00685
00686 int HeapEngine::heap_create_table(Session *session, const char *table_name,
00687 Table *table_arg,
00688 bool internal_table,
00689 const message::Table &create_proto,
00690 HP_SHARE **internal_share)
00691 {
00692 uint32_t key, parts, mem_per_row_keys= 0;
00693 uint32_t keys= table_arg->getShare()->sizeKeys();
00694 uint32_t auto_key= 0, auto_key_type= 0;
00695 uint32_t max_key_fieldnr = 0, key_part_size = 0, next_field_pos = 0;
00696 uint32_t column_count= table_arg->getShare()->sizeFields();
00697 std::vector<HP_KEYDEF> keydef;
00698 int error;
00699 bool found_real_auto_increment= 0;
00700
00701
00702
00703
00704
00705
00706
00707 uint64_t num_rows= table_arg->getShare()->getMaxRows();
00708 if (num_rows > UINT32_MAX)
00709 return -1;
00710
00711 for (key= parts= 0; key < keys; key++)
00712 parts+= table_arg->key_info[key].key_parts;
00713
00714 keydef.resize(keys);
00715 std::vector<HA_KEYSEG> seg_buffer;
00716 seg_buffer.resize(parts);
00717 HA_KEYSEG *seg= &seg_buffer[0];
00718
00719 for (key= 0; key < keys; key++)
00720 {
00721 KeyInfo *pos= &table_arg->key_info[key];
00722 KeyPartInfo *key_part= pos->key_part;
00723 KeyPartInfo *key_part_end= key_part + pos->key_parts;
00724
00725 keydef[key].keysegs= (uint) pos->key_parts;
00726 keydef[key].flag= (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL));
00727 keydef[key].seg= seg;
00728
00729 mem_per_row_keys+= sizeof(char*) * 2;
00730
00731 for (; key_part != key_part_end; key_part++, seg++)
00732 {
00733 Field *field= key_part->field;
00734
00735 {
00736 if ((seg->type = field->key_type()) != (int) HA_KEYTYPE_TEXT &&
00737 seg->type != HA_KEYTYPE_VARTEXT1 &&
00738 seg->type != HA_KEYTYPE_VARTEXT2 &&
00739 seg->type != HA_KEYTYPE_VARBINARY1 &&
00740 seg->type != HA_KEYTYPE_VARBINARY2)
00741 seg->type= HA_KEYTYPE_BINARY;
00742 }
00743 seg->start= (uint) key_part->offset;
00744 seg->length= (uint) key_part->length;
00745 seg->flag= key_part->key_part_flag;
00746
00747 next_field_pos= seg->start + seg->length;
00748 if (field->type() == DRIZZLE_TYPE_VARCHAR)
00749 {
00750 next_field_pos+= (uint8_t)(((Field_varstring*)field)->pack_length_no_ptr());
00751 }
00752
00753 if (next_field_pos > key_part_size) {
00754 key_part_size= next_field_pos;
00755 }
00756
00757 if (field->flags & ENUM_FLAG)
00758 seg->charset= &my_charset_bin;
00759 else
00760 seg->charset= field->charset();
00761 if (field->null_ptr)
00762 {
00763 seg->null_bit= field->null_bit;
00764 seg->null_pos= (uint) (field->null_ptr - (unsigned char*) table_arg->getInsertRecord());
00765 }
00766 else
00767 {
00768 seg->null_bit= 0;
00769 seg->null_pos= 0;
00770 }
00771 if (field->flags & AUTO_INCREMENT_FLAG &&
00772 table_arg->found_next_number_field &&
00773 key == table_arg->getShare()->next_number_index)
00774 {
00775
00776
00777
00778
00779 auto_key= key+ 1;
00780 auto_key_type= field->key_type();
00781 }
00782 if ((uint)field->position() + 1 > max_key_fieldnr)
00783 {
00784
00785 max_key_fieldnr= field->position() + 1;
00786 }
00787 }
00788 }
00789
00790 if (key_part_size < table_arg->getShare()->null_bytes + ((table_arg->getShare()->last_null_bit_pos+7) >> 3))
00791 {
00792
00793 key_part_size = table_arg->getShare()->null_bytes + ((table_arg->getShare()->last_null_bit_pos+7) >> 3);
00794 }
00795
00796
00797
00798 if (table_arg->found_next_number_field)
00799 {
00800 keydef[table_arg->getShare()->next_number_index].flag|= HA_AUTO_KEY;
00801 found_real_auto_increment= table_arg->getShare()->next_number_key_offset == 0;
00802 }
00803 HP_CREATE_INFO hp_create_info;
00804 hp_create_info.auto_key= auto_key;
00805 hp_create_info.auto_key_type= auto_key_type;
00806 hp_create_info.auto_increment= (create_proto.options().has_auto_increment_value() ?
00807 create_proto.options().auto_increment_value() - 1 : 0);
00808 hp_create_info.max_table_size=session->variables.max_heap_table_size;
00809 hp_create_info.with_auto_increment= found_real_auto_increment;
00810 hp_create_info.internal_table= internal_table;
00811 hp_create_info.max_chunk_size= table_arg->getShare()->block_size;
00812
00813 error= heap_create(table_name,
00814 keys, &keydef[0],
00815 column_count,
00816 key_part_size,
00817 table_arg->getShare()->getRecordLength(), mem_per_row_keys,
00818 static_cast<uint32_t>(num_rows),
00819 0,
00820 &hp_create_info, internal_share);
00821
00822 return (error);
00823 }
00824
00825
00826 void ha_heap::get_auto_increment(uint64_t, uint64_t, uint64_t,
00827 uint64_t *first_value,
00828 uint64_t *nb_reserved_values)
00829 {
00830 ha_heap::info(HA_STATUS_AUTO);
00831 *first_value= stats.auto_increment_value;
00832
00833 *nb_reserved_values= UINT64_MAX;
00834 }
00835
00836
00837 int ha_heap::cmp_ref(const unsigned char *ref1, const unsigned char *ref2)
00838 {
00839 return memcmp(ref1, ref2, sizeof(HEAP_PTR));
00840 }
00841
00842
00843 DRIZZLE_DECLARE_PLUGIN
00844 {
00845 DRIZZLE_VERSION_ID,
00846 "MEMORY",
00847 "1.0",
00848 "MySQL AB",
00849 "Hash based, stored in memory, useful for temporary tables",
00850 PLUGIN_LICENSE_GPL,
00851 heap_init,
00852 NULL,
00853 NULL
00854 }
00855 DRIZZLE_DECLARE_PLUGIN_END;