hp_write.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:11k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. /* Write a record to heap-databas */
  14. #include "heapdef.h"
  15. #ifdef __WIN__
  16. #include <fcntl.h>
  17. #endif
  18. #define LOWFIND 1
  19. #define LOWUSED 2
  20. #define HIGHFIND 4
  21. #define HIGHUSED 8
  22. static byte *next_free_record_pos(HP_SHARE *info);
  23. static HASH_INFO *hp_find_free_hash(HP_SHARE *info, HP_BLOCK *block,
  24.      ulong records);
  25. int heap_write(HP_INFO *info, const byte *record)
  26. {
  27.   HP_KEYDEF *keydef, *end;
  28.   byte *pos;
  29.   HP_SHARE *share=info->s;
  30.   DBUG_ENTER("heap_write");
  31. #ifndef DBUG_OFF
  32.   if (info->mode & O_RDONLY)
  33.   {
  34.     DBUG_RETURN(my_errno=EACCES);
  35.   }
  36. #endif
  37.   if (!(pos=next_free_record_pos(share)))
  38.     DBUG_RETURN(my_errno);
  39.   share->changed=1;
  40.   for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
  41.        keydef++)
  42.   {
  43.     if ((*keydef->write_key)(info, keydef, record, pos))
  44.       goto err;
  45.   }
  46.   memcpy(pos,record,(size_t) share->reclength);
  47.   pos[share->reclength]=1; /* Mark record as not deleted */
  48.   if (++share->records == share->blength)
  49.     share->blength+= share->blength;
  50.   info->current_ptr=pos;
  51.   info->current_hash_ptr=0;
  52.   info->update|=HA_STATE_AKTIV;
  53. #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
  54.   DBUG_EXECUTE("check_heap",heap_check_heap(info, 0););
  55. #endif
  56.   if (share->auto_key)
  57.     heap_update_auto_increment(info, record);
  58.   DBUG_RETURN(0);
  59. err:
  60.   DBUG_PRINT("info",("Duplicate key: %d", keydef - share->keydef));
  61.   info->errkey= keydef - share->keydef;
  62.   if (keydef->algorithm == HA_KEY_ALG_BTREE)
  63.   {
  64.     /* we don't need to delete non-inserted key from rb-tree */
  65.     keydef--;
  66.   }
  67.   while (keydef >= share->keydef)
  68.   {
  69.     if ((*keydef->delete_key)(info, keydef, record, pos, 0))
  70.       break;
  71.     keydef--;
  72.   } 
  73.   share->deleted++;
  74.   *((byte**) pos)=share->del_link;
  75.   share->del_link=pos;
  76.   pos[share->reclength]=0; /* Record deleted */
  77.   DBUG_RETURN(my_errno);
  78. } /* heap_write */
  79. /* 
  80.   Write a key to rb_tree-index 
  81. */
  82. int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const byte *record, 
  83.     byte *recpos)
  84. {
  85.   heap_rb_param custom_arg;
  86.   uint old_allocated;
  87.   info->last_pos= NULL; /* For heap_rnext/heap_rprev */
  88.   custom_arg.keyseg= keyinfo->seg;
  89.   custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
  90.   if (keyinfo->flag & HA_NOSAME)
  91.   {
  92.     custom_arg.search_flag= SEARCH_FIND | SEARCH_SAME;
  93.     keyinfo->rb_tree.flag= TREE_NO_DUPS;
  94.   }
  95.   else
  96.   {
  97.     custom_arg.search_flag= SEARCH_SAME;
  98.     keyinfo->rb_tree.flag= 0;
  99.   }
  100.   old_allocated= keyinfo->rb_tree.allocated;
  101.   if (!tree_insert(&keyinfo->rb_tree, (void*)info->recbuf,
  102.    custom_arg.key_length, &custom_arg))
  103.   {
  104.     my_errno= HA_ERR_FOUND_DUPP_KEY;
  105.     return 1;
  106.   }
  107.   info->s->index_length+= (keyinfo->rb_tree.allocated-old_allocated);
  108.   return 0;
  109. }
  110. /* Find where to place new record */
  111. static byte *next_free_record_pos(HP_SHARE *info)
  112. {
  113.   int block_pos;
  114.   byte *pos;
  115.   ulong length;
  116.   DBUG_ENTER("next_free_record_pos");
  117.   if (info->del_link)
  118.   {
  119.     pos=info->del_link;
  120.     info->del_link= *((byte**) pos);
  121.     info->deleted--;
  122.     DBUG_PRINT("exit",("Used old position: %lx",pos));
  123.     DBUG_RETURN(pos);
  124.   }
  125.   if (!(block_pos=(info->records % info->block.records_in_block)))
  126.   {
  127.     if ((info->records > info->max_records && info->max_records) ||
  128.         (info->data_length + info->index_length >= info->max_table_size))
  129.     {
  130.       my_errno=HA_ERR_RECORD_FILE_FULL;
  131.       DBUG_RETURN(NULL);
  132.     }
  133.     if (hp_get_new_block(&info->block,&length))
  134.       DBUG_RETURN(NULL);
  135.     info->data_length+=length;
  136.   }
  137.   DBUG_PRINT("exit",("Used new position: %lx",
  138.      (byte*) info->block.level_info[0].last_blocks+block_pos*
  139.      info->block.recbuffer));
  140.   DBUG_RETURN((byte*) info->block.level_info[0].last_blocks+
  141.       block_pos*info->block.recbuffer);
  142. }
  143. /*
  144.   Write a hash-key to the hash-index
  145.   SYNOPSIS
  146.     info     Heap table info
  147.     keyinfo  Key info
  148.     record   Table record to added
  149.     recpos   Memory buffer where the table record will be stored if added 
  150.              successfully
  151.   NOTE
  152.     Hash index uses HP_BLOCK structure as a 'growable array' of HASH_INFO 
  153.     structs. Array size == number of entries in hash index.
  154.     hp_mask(hp_rec_hashnr()) maps hash entries values to hash array positions.
  155.     If there are several hash entries with the same hash array position P,
  156.     they are connected in a linked list via HASH_INFO::next_key. The first 
  157.     list element is located at position P, next elements are located at 
  158.     positions for which there is no record that should be located at that
  159.     position. The order of elements in the list is arbitrary.
  160.   RETURN
  161.     0  - OK
  162.     -1 - Out of memory
  163.     HA_ERR_FOUND_DUPP_KEY - Duplicate record on unique key. The record was 
  164.     still added and the caller must call hp_delete_key for it.
  165. */
  166. int hp_write_key(HP_INFO *info, HP_KEYDEF *keyinfo,
  167.  const byte *record, byte *recpos)
  168. {
  169.   HP_SHARE *share = info->s;
  170.   int flag;
  171.   ulong halfbuff,hashnr,first_index;
  172.   byte *ptr_to_rec,*ptr_to_rec2;
  173.   HASH_INFO *empty,*gpos,*gpos2,*pos;
  174.   DBUG_ENTER("hp_write_key");
  175.   LINT_INIT(gpos); LINT_INIT(gpos2);
  176.   LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
  177.   flag=0;
  178.   if (!(empty= hp_find_free_hash(share,&keyinfo->block,share->records)))
  179.     DBUG_RETURN(-1); /* No more memory */
  180.   halfbuff= (long) share->blength >> 1;
  181.   pos= hp_find_hash(&keyinfo->block,(first_index=share->records-halfbuff));
  182.   
  183.   /*
  184.     We're about to add one more hash array position, with hash_mask=#records.
  185.     The number of hash positions will change and some entries might need to 
  186.     be relocated to the newly added position. Those entries are currently 
  187.     members of the list that starts at #first_index position (this is 
  188.     guaranteed by properties of hp_mask(hp_rec_hashnr(X)) mapping function)
  189.     At #first_index position currently there may be either:
  190.     a) An entry with hashnr != first_index. We don't need to move it.
  191.     or
  192.     b) A list of items with hash_mask=first_index. The list contains entries
  193.        of 2 types:
  194.        1) entries that should be relocated to the list that starts at new 
  195.           position we're adding ('uppper' list)
  196.        2) entries that should be left in the list starting at #first_index 
  197.           position ('lower' list)
  198.   */
  199.   if (pos != empty) /* If some records */
  200.   {
  201.     do
  202.     {
  203.       hashnr = hp_rec_hashnr(keyinfo, pos->ptr_to_rec);
  204.       if (flag == 0)
  205.       {
  206.         /* 
  207.           First loop, bail out if we're dealing with case a) from above 
  208.           comment
  209.         */
  210. if (hp_mask(hashnr, share->blength, share->records) != first_index)
  211.   break;
  212.       }
  213.       /*
  214.         flag & LOWFIND - found a record that should be put into lower position
  215.         flag & LOWUSED - lower position occupied by the record
  216.         Same for HIGHFIND and HIGHUSED and 'upper' position
  217.         gpos  - ptr to last element in lower position's list
  218.         gpos2 - ptr to last element in upper position's list
  219.         ptr_to_rec - ptr to last entry that should go into lower list.
  220.         ptr_to_rec2 - same for upper list.
  221.       */
  222.       if (!(hashnr & halfbuff))
  223.       {
  224.         /* Key should be put into 'lower' list */
  225. if (!(flag & LOWFIND))
  226. {
  227.           /* key is the first element to go into lower position */
  228.   if (flag & HIGHFIND)
  229.   {
  230.     flag=LOWFIND | HIGHFIND;
  231.     /* key shall be moved to the current empty position */
  232.     gpos=empty;
  233.     ptr_to_rec=pos->ptr_to_rec;
  234.     empty=pos; /* This place is now free */
  235.   }
  236.   else
  237.   {
  238.             /*
  239.               We can only get here at first iteration: key is at 'lower' 
  240.               position pos and should be left here.
  241.             */
  242.     flag=LOWFIND | LOWUSED;
  243.     gpos=pos;
  244.     ptr_to_rec=pos->ptr_to_rec;
  245.   }
  246. }
  247. else
  248.         {
  249.           /* Already have another key for lower position */
  250.   if (!(flag & LOWUSED))
  251.   {
  252.     /* Change link of previous lower-list key */
  253.     gpos->ptr_to_rec=ptr_to_rec;
  254.     gpos->next_key=pos;
  255.     flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
  256.   }
  257.   gpos=pos;
  258.   ptr_to_rec=pos->ptr_to_rec;
  259. }
  260.       }
  261.       else
  262.       {
  263.         /* key will be put into 'higher' list */
  264. if (!(flag & HIGHFIND))
  265. {
  266.   flag= (flag & LOWFIND) | HIGHFIND;
  267.   /* key shall be moved to the last (empty) position */
  268.   gpos2= empty;
  269.           empty= pos;
  270.   ptr_to_rec2=pos->ptr_to_rec;
  271. }
  272. else
  273. {
  274.   if (!(flag & HIGHUSED))
  275.   {
  276.     /* Change link of previous upper-list key and save */
  277.     gpos2->ptr_to_rec=ptr_to_rec2;
  278.     gpos2->next_key=pos;
  279.     flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
  280.   }
  281.   gpos2=pos;
  282.   ptr_to_rec2=pos->ptr_to_rec;
  283. }
  284.       }
  285.     }
  286.     while ((pos=pos->next_key));
  287.     
  288.     if ((flag & (LOWFIND | HIGHFIND)) == (LOWFIND | HIGHFIND))
  289.     {
  290.       /*
  291.         If both 'higher' and 'lower' list have at least one element, now
  292.         there are two hash buckets instead of one.
  293.       */
  294.       keyinfo->hash_buckets++;
  295.     }
  296.     if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
  297.     {
  298.       gpos->ptr_to_rec=ptr_to_rec;
  299.       gpos->next_key=0;
  300.     }
  301.     if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
  302.     {
  303.       gpos2->ptr_to_rec=ptr_to_rec2;
  304.       gpos2->next_key=0;
  305.     }
  306.   }
  307.   /* Check if we are at the empty position */
  308.   pos=hp_find_hash(&keyinfo->block, hp_mask(hp_rec_hashnr(keyinfo, record),
  309.  share->blength, share->records + 1));
  310.   if (pos == empty)
  311.   {
  312.     pos->ptr_to_rec=recpos;
  313.     pos->next_key=0;
  314.     keyinfo->hash_buckets++;
  315.   }
  316.   else
  317.   {
  318.     /* Check if more records in same hash-nr family */
  319.     empty[0]=pos[0];
  320.     gpos=hp_find_hash(&keyinfo->block,
  321.       hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
  322.       share->blength, share->records + 1));
  323.     if (pos == gpos)
  324.     {
  325.       pos->ptr_to_rec=recpos;
  326.       pos->next_key=empty;
  327.     }
  328.     else
  329.     {
  330.       keyinfo->hash_buckets++;
  331.       pos->ptr_to_rec=recpos;
  332.       pos->next_key=0;
  333.       hp_movelink(pos, gpos, empty);
  334.     }
  335.     /* Check if duplicated keys */
  336.     if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
  337. (!(keyinfo->flag & HA_NULL_PART_KEY) ||
  338.  !hp_if_null_in_key(keyinfo, record)))
  339.     {
  340.       pos=empty;
  341.       do
  342.       {
  343. if (! hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec))
  344. {
  345.   DBUG_RETURN(my_errno=HA_ERR_FOUND_DUPP_KEY);
  346. }
  347.       } while ((pos=pos->next_key));
  348.     }
  349.   }
  350.   DBUG_RETURN(0);
  351. }
  352. /* Returns ptr to block, and allocates block if neaded */
  353. static HASH_INFO *hp_find_free_hash(HP_SHARE *info,
  354.      HP_BLOCK *block, ulong records)
  355. {
  356.   uint block_pos;
  357.   ulong length;
  358.   if (records < block->last_allocated)
  359.     return hp_find_hash(block,records);
  360.   if (!(block_pos=(records % block->records_in_block)))
  361.   {
  362.     if (hp_get_new_block(block,&length))
  363.       return(NULL);
  364.     info->index_length+=length;
  365.   }
  366.   block->last_allocated=records+1;
  367.   return((HASH_INFO*) ((byte*) block->level_info[0].last_blocks+
  368.        block_pos*block->recbuffer));
  369. }