Drizzled Public API Documentation

hp_create.cc

00001 /* Copyright (C) 2000-2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #include "heap_priv.h"
00017 
00018 #include <drizzled/common.h>
00019 #include <drizzled/error.h>
00020 
00021 #include <string.h>
00022 #include <algorithm>
00023 
00024 using namespace std;
00025 using namespace drizzled;
00026 
00027 static void init_block(HP_BLOCK *block,uint32_t chunk_length, uint32_t min_records,
00028                         uint32_t max_records);
00029 
00030 static const int FIXED_REC_OVERHEAD = (sizeof(unsigned char));
00031 static const int VARIABLE_REC_OVERHEAD = (sizeof(unsigned char**) + ALIGN_SIZE(sizeof(unsigned char)));
00032 
00033 /* Minimum size that a chunk can take, 12 bytes on 32bit, 24 bytes on 64bit */
00034 static const int VARIABLE_MIN_CHUNK_SIZE =
00035         ((sizeof(unsigned char**) + VARIABLE_REC_OVERHEAD + sizeof(unsigned char**) - 1) & ~(sizeof(unsigned char**) - 1));
00036 
00037 
00038 /* Create a heap table */
00039 
00040 int heap_create(const char *name, uint32_t keys, HP_KEYDEF *keydef,
00041                 uint32_t columns,
00042                 uint32_t key_part_size,
00043                 uint32_t reclength, uint32_t keys_memory_size,
00044                 uint32_t max_records, uint32_t min_records,
00045                 HP_CREATE_INFO *create_info, HP_SHARE **res)
00046 {
00047   uint32_t i, key_segs, max_length, length;
00048   uint32_t max_rows_for_stated_memory;
00049   HP_SHARE *share= 0;
00050   HA_KEYSEG *keyseg;
00051 
00052   if (not create_info->internal_table)
00053   {
00054     THR_LOCK_heap.lock();
00055     if ((share= hp_find_named_heap(name)) && share->open_count == 0)
00056     {
00057       hp_free(share);
00058       share= 0;
00059     }
00060   }
00061 
00062   if (!share)
00063   {
00064     size_t chunk_dataspace_length;
00065     uint32_t chunk_length;
00066     uint32_t fixed_data_length, fixed_column_count;
00067     HP_KEYDEF *keyinfo;
00068 
00069     if (create_info->max_chunk_size)
00070     {
00071       uint32_t configured_chunk_size= create_info->max_chunk_size;
00072 
00073       /* User requested variable-size records, let's see if they're possible */
00074 
00075       if (configured_chunk_size < key_part_size)
00076       {
00077         /* Eventual chunk_size cannot be smaller than key data,
00078           which allows all keys to fit into the first chunk */
00079         my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "block_size");
00080         THR_LOCK_heap.unlock();
00081         return(ER_CANT_USE_OPTION_HERE);
00082       }
00083 
00084       /* max_chunk_size is near the full reclength, let's use fixed size */
00085       chunk_dataspace_length= reclength;
00086     }
00087     else
00088     {
00089       /* if max_chunk_size is not specified, put the whole record in one chunk */
00090       chunk_dataspace_length= reclength;
00091     }
00092 
00093     {
00094       fixed_data_length= reclength;
00095       fixed_column_count= columns;
00096     }
00097 
00098     /*
00099       We store unsigned char* del_link inside the data area of deleted records,
00100       so the data length should be at least sizeof(unsigned char*)
00101     */
00102     set_if_bigger(chunk_dataspace_length, sizeof (unsigned char**));
00103 
00104     {
00105       chunk_length= chunk_dataspace_length + FIXED_REC_OVERHEAD;
00106     }
00107 
00108     /* Align chunk length to the next pointer */
00109     chunk_length= (uint) (chunk_length + sizeof(unsigned char**) - 1) & ~(sizeof(unsigned char**) - 1);
00110 
00111 
00112 
00113     for (i= key_segs= max_length= 0, keyinfo= keydef; i < keys; i++, keyinfo++)
00114     {
00115       memset(&keyinfo->block, 0, sizeof(keyinfo->block));
00116       for (uint32_t j= length= 0; j < keyinfo->keysegs; j++)
00117       {
00118   length+= keyinfo->seg[j].length;
00119   if (keyinfo->seg[j].null_bit)
00120   {
00121     length++;
00122     if (!(keyinfo->flag & HA_NULL_ARE_EQUAL))
00123       keyinfo->flag|= HA_NULL_PART_KEY;
00124   }
00125   switch (keyinfo->seg[j].type) {
00126   case HA_KEYTYPE_LONG_INT:
00127   case HA_KEYTYPE_DOUBLE:
00128   case HA_KEYTYPE_ULONG_INT:
00129   case HA_KEYTYPE_LONGLONG:
00130   case HA_KEYTYPE_ULONGLONG:
00131     keyinfo->seg[j].flag|= HA_SWAP_KEY;
00132           break;
00133         case HA_KEYTYPE_VARBINARY1:
00134           /* Case-insensitiveness is handled in coll->hash_sort */
00135           keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
00136           /* fall_through */
00137         case HA_KEYTYPE_VARTEXT1:
00138           keyinfo->flag|= HA_VAR_LENGTH_KEY;
00139           length+= 2;
00140           /* Save number of bytes used to store length */
00141           keyinfo->seg[j].bit_start= 1;
00142           break;
00143         case HA_KEYTYPE_VARBINARY2:
00144           /* Case-insensitiveness is handled in coll->hash_sort */
00145           /* fall_through */
00146         case HA_KEYTYPE_VARTEXT2:
00147           keyinfo->flag|= HA_VAR_LENGTH_KEY;
00148           length+= 2;
00149           /* Save number of bytes used to store length */
00150           keyinfo->seg[j].bit_start= 2;
00151           /*
00152             Make future comparison simpler by only having to check for
00153             one type
00154           */
00155           keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
00156           break;
00157   default:
00158     break;
00159   }
00160       }
00161       keyinfo->length= length;
00162       if (length > max_length)
00163   max_length= length;
00164       key_segs+= keyinfo->keysegs;
00165     }
00166     share= new HP_SHARE;
00167 
00168     if (keys && !(share->keydef= new HP_KEYDEF[keys]))
00169       goto err;
00170     if (keys && !(share->keydef->seg= new HA_KEYSEG[key_segs]))
00171       goto err;
00172 
00173     /*
00174        Max_records is used for estimating block sizes and for enforcement.
00175        Calculate the very maximum number of rows (if everything was one chunk) and
00176        then take either that value or configured max_records (pick smallest one)
00177     */
00178     max_rows_for_stated_memory= (uint32_t)(create_info->max_table_size /
00179       (keys_memory_size + chunk_length));
00180     max_records = ((max_records && max_records < max_rows_for_stated_memory) ?
00181                       max_records : max_rows_for_stated_memory);
00182 
00183     share->key_stat_version= 1;
00184     keyseg= keys ? share->keydef->seg : NULL;
00185 
00186     init_block(&share->recordspace.block, chunk_length, min_records, max_records);
00187     /* Fix keys */
00188     memcpy(share->keydef, keydef, (size_t) (sizeof(keydef[0]) * keys));
00189     for (i= 0, keyinfo= share->keydef; i < keys; i++, keyinfo++)
00190     {
00191       keyinfo->seg= keyseg;
00192       memcpy(keyseg, keydef[i].seg,
00193        (size_t) (sizeof(keyseg[0]) * keydef[i].keysegs));
00194       keyseg+= keydef[i].keysegs;
00195       {
00196   init_block(&keyinfo->block, sizeof(HASH_INFO), min_records,
00197        max_records);
00198         keyinfo->hash_buckets= 0;
00199       }
00200       if ((keyinfo->flag & HA_AUTO_KEY) && create_info->with_auto_increment)
00201         share->auto_key= i + 1;
00202     }
00203     share->min_records= min_records;
00204     share->max_records= max_records;
00205     share->max_table_size= create_info->max_table_size;
00206     share->index_length= 0;
00207     share->blength= 1;
00208     share->keys= keys;
00209     share->max_key_length= max_length;
00210     share->column_count= columns;
00211     share->changed= 0;
00212     share->auto_key= create_info->auto_key;
00213     share->auto_key_type= create_info->auto_key_type;
00214     share->auto_increment= create_info->auto_increment;
00215 
00216     share->fixed_data_length= fixed_data_length;
00217     share->fixed_column_count= fixed_column_count;
00218 
00219     share->recordspace.chunk_length= chunk_length;
00220     share->recordspace.chunk_dataspace_length= chunk_dataspace_length;
00221     share->recordspace.total_data_length= 0;
00222 
00223     {
00224       share->recordspace.offset_link= 1<<22; /* Make it likely to fail if anyone uses this offset */
00225       share->recordspace.offset_status= chunk_dataspace_length;
00226     }
00227 
00228     /* Must be allocated separately for rename to work */
00229     share->name.append(name);
00230     if (!create_info->internal_table)
00231     {
00232       heap_share_list.push_front(share);
00233     }
00234     else
00235       share->delete_on_close= 1;
00236   }
00237   if (!create_info->internal_table)
00238     THR_LOCK_heap.unlock();
00239 
00240   *res= share;
00241   return(0);
00242 
00243 err:
00244   if (share && share->keydef && share->keydef->seg)
00245     delete [] share->keydef->seg;
00246   if (share && share->keydef)
00247     delete [] share->keydef;
00248   if (share)
00249     delete share;
00250   if (not create_info->internal_table)
00251     THR_LOCK_heap.unlock();
00252   return(1);
00253 } /* heap_create */
00254 
00255 
00256 static void init_block(HP_BLOCK *block, uint32_t chunk_length, uint32_t min_records,
00257            uint32_t max_records)
00258 {
00259   uint32_t recbuffer,records_in_block;
00260 
00261   max_records= max(min_records,max_records);
00262   if (!max_records)
00263     max_records= 1000;      /* As good as quess as anything */
00264 
00265   /* we want to start each chunk at 8 bytes boundary, round recbuffer to the next 8 */
00266   recbuffer= (uint) (chunk_length + sizeof(unsigned char**) - 1) & ~(sizeof(unsigned char**) - 1);
00267   records_in_block= max_records / 10;
00268   if (records_in_block < 10 && max_records)
00269     records_in_block= 10;
00270   if (!records_in_block || records_in_block*recbuffer >
00271       (internal::my_default_record_cache_size-sizeof(HP_PTRS)*HP_MAX_LEVELS))
00272     records_in_block= (internal::my_default_record_cache_size - sizeof(HP_PTRS) *
00273           HP_MAX_LEVELS) / recbuffer + 1;
00274   block->records_in_block= records_in_block;
00275   block->recbuffer= recbuffer;
00276   block->last_allocated= 0L;
00277 
00278   for (uint32_t i= 0; i <= HP_MAX_LEVELS; i++)
00279   {
00280     block->level_info[i].records_under_level=
00281       (!i ? 1 : i == 1 ? records_in_block :
00282        HP_PTRS_IN_NOD * block->level_info[i - 1].records_under_level);
00283   }
00284 }
00285 
00286 
00287 static inline void heap_try_free(HP_SHARE *share)
00288 {
00289   if (share->open_count == 0)
00290     hp_free(share);
00291   else
00292     share->delete_on_close= 1;
00293 }
00294 
00295 
00296 int heap_delete_table(const char *name)
00297 {
00298   int result;
00299   register HP_SHARE *share;
00300 
00301   THR_LOCK_heap.lock();
00302   if ((share= hp_find_named_heap(name)))
00303   {
00304     heap_try_free(share);
00305     result= 0;
00306   }
00307   else
00308   {
00309     result= errno=ENOENT;
00310   }
00311   THR_LOCK_heap.unlock();
00312   return(result);
00313 }
00314 
00315 
00316 void hp_free(HP_SHARE *share)
00317 {
00318   heap_share_list.remove(share);        /* If not internal table */
00319   hp_clear(share);      /* Remove blocks from memory */
00320   if (share->keydef)
00321     delete [] share->keydef->seg;
00322   delete [] share->keydef;
00323   delete share;
00324 }