Drizzled Public API Documentation

mi_unique.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 /* Functions to check if a row is unique */
00017 
00018 #include "myisam_priv.h"
00019 #include <drizzled/charset_info.h>
00020 
00021 using namespace drizzled;
00022 
00023 bool mi_check_unique(MI_INFO *info,
00024                      MI_UNIQUEDEF *def,
00025                      unsigned char *record,
00026                      internal::ha_checksum unique_hash,
00027                      internal::my_off_t disk_pos)
00028 {
00029   internal::my_off_t lastpos=info->lastpos;
00030   MI_KEYDEF *key= &info->s->keyinfo[def->key];
00031   unsigned char *key_buff=info->lastkey2;
00032 
00033   mi_unique_store(record+key->seg->start, unique_hash);
00034   _mi_make_key(info,def->key,key_buff,record,0);
00035 
00036   /* The above changed info->lastkey2. Inform mi_rnext_same(). */
00037   info->update&= ~HA_STATE_RNEXT_SAME;
00038 
00039   if (_mi_search(info,info->s->keyinfo+def->key,key_buff,MI_UNIQUE_HASH_LENGTH,
00040      SEARCH_FIND,info->s->state.key_root[def->key]))
00041   {
00042     info->page_changed=1;     /* Can't optimize read next */
00043     info->lastpos= lastpos;
00044     return(0);        /* No matching rows */
00045   }
00046 
00047   for (;;)
00048   {
00049     if (info->lastpos != disk_pos &&
00050   !(*info->s->compare_unique)(info,def,record,info->lastpos))
00051     {
00052       errno=HA_ERR_FOUND_DUPP_UNIQUE;
00053       info->errkey= (int) def->key;
00054       info->dupp_key_pos= info->lastpos;
00055       info->page_changed=1;     /* Can't optimize read next */
00056       info->lastpos=lastpos;
00057       return(1);        /* Found identical  */
00058     }
00059     if (_mi_search_next(info,info->s->keyinfo+def->key, info->lastkey,
00060       MI_UNIQUE_HASH_LENGTH, SEARCH_BIGGER,
00061       info->s->state.key_root[def->key]) ||
00062   memcmp(info->lastkey, key_buff, MI_UNIQUE_HASH_LENGTH))
00063     {
00064       info->page_changed=1;     /* Can't optimize read next */
00065       info->lastpos=lastpos;
00066       return(0);        /* end of tree */
00067     }
00068   }
00069 }
00070 
00071 
00072 /*
00073   Calculate a hash for a row
00074 
00075   TODO
00076     Add support for bit fields
00077 */
00078 
00079 internal::ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const unsigned char *record)
00080 {
00081   const unsigned char *pos, *end;
00082   internal::ha_checksum crc= 0;
00083   uint32_t seed1=0, seed2= 4;
00084   HA_KEYSEG *keyseg;
00085 
00086   for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
00087   {
00088     enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
00089     uint32_t length=keyseg->length;
00090 
00091     if (keyseg->null_bit)
00092     {
00093       if (record[keyseg->null_pos] & keyseg->null_bit)
00094       {
00095   /*
00096     Change crc in a way different from an empty string or 0.
00097     (This is an optimisation;  The code will work even if this isn't
00098     done)
00099   */
00100   crc=((crc << 8) + 511+
00101        (crc >> (8*sizeof(internal::ha_checksum)-8)));
00102   continue;
00103       }
00104     }
00105     pos= record+keyseg->start;
00106     if (keyseg->flag & HA_VAR_LENGTH_PART)
00107     {
00108       uint32_t pack_length=  keyseg->bit_start;
00109       uint32_t tmp_length= (pack_length == 1 ? (uint) *(unsigned char*) pos :
00110                         uint2korr(pos));
00111       pos+= pack_length;      /* Skip VARCHAR length */
00112       set_if_smaller(length,tmp_length);
00113     }
00114     else if (keyseg->flag & HA_BLOB_PART)
00115     {
00116       uint32_t tmp_length=_mi_calc_blob_length(keyseg->bit_start,pos);
00117       memcpy(&pos,pos+keyseg->bit_start,sizeof(char*));
00118       if (!length || length > tmp_length)
00119   length=tmp_length;      /* The whole blob */
00120     }
00121     end= pos+length;
00122     if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
00123         type == HA_KEYTYPE_VARTEXT2)
00124     {
00125       keyseg->charset->coll->hash_sort(keyseg->charset,
00126                                        (const unsigned char*) pos, length, &seed1,
00127                                        &seed2);
00128       crc^= seed1;
00129     }
00130     else
00131       while (pos != end)
00132   crc=((crc << 8) +
00133        (((unsigned char)  *(unsigned char*) pos++))) +
00134     (crc >> (8*sizeof(internal::ha_checksum)-8));
00135   }
00136   return crc;
00137 }
00138 
00139 
00140 /*
00141   compare unique key for two rows
00142 
00143   TODO
00144     Add support for bit fields
00145 
00146   RETURN
00147     0   if both rows have equal unique value
00148     #   Rows are different
00149 */
00150 
00151 int mi_unique_comp(MI_UNIQUEDEF *def, const unsigned char *a, const unsigned char *b,
00152        bool null_are_equal)
00153 {
00154   const unsigned char *pos_a, *pos_b, *end;
00155   HA_KEYSEG *keyseg;
00156 
00157   for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
00158   {
00159     enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
00160     uint16_t a_length, b_length;
00161     a_length= b_length= keyseg->length;
00162 
00163     /* If part is NULL it's regarded as different */
00164     if (keyseg->null_bit)
00165     {
00166       uint32_t tmp;
00167       if ((tmp=(a[keyseg->null_pos] & keyseg->null_bit)) !=
00168     (uint) (b[keyseg->null_pos] & keyseg->null_bit))
00169   return 1;
00170       if (tmp)
00171       {
00172   if (!null_are_equal)
00173     return 1;
00174   continue;
00175       }
00176     }
00177     pos_a= a+keyseg->start;
00178     pos_b= b+keyseg->start;
00179     if (keyseg->flag & HA_VAR_LENGTH_PART)
00180     {
00181       uint32_t pack_length= keyseg->bit_start;
00182       if (pack_length == 1)
00183       {
00184         a_length= (uint) *(unsigned char*) pos_a++;
00185         b_length= (uint) *(unsigned char*) pos_b++;
00186       }
00187       else
00188       {
00189         a_length= uint2korr(pos_a);
00190         b_length= uint2korr(pos_b);
00191         pos_a+= 2;        /* Skip VARCHAR length */
00192         pos_b+= 2;
00193       }
00194       set_if_smaller(a_length, keyseg->length); /* Safety */
00195       set_if_smaller(b_length, keyseg->length); /* safety */
00196     }
00197     else if (keyseg->flag & HA_BLOB_PART)
00198     {
00199       /* Only compare 'length' characters if length != 0 */
00200       a_length= _mi_calc_blob_length(keyseg->bit_start,pos_a);
00201       b_length= _mi_calc_blob_length(keyseg->bit_start,pos_b);
00202       /* Check that a and b are of equal length */
00203       if (keyseg->length)
00204       {
00205         /*
00206           This is used in some cases when we are not interested in comparing
00207           the whole length of the blob.
00208         */
00209         set_if_smaller(a_length, keyseg->length);
00210         set_if_smaller(b_length, keyseg->length);
00211       }
00212       memcpy(&pos_a,pos_a+keyseg->bit_start,sizeof(char*));
00213       memcpy(&pos_b,pos_b+keyseg->bit_start,sizeof(char*));
00214     }
00215     if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
00216         type == HA_KEYTYPE_VARTEXT2)
00217     {
00218       if (ha_compare_text(keyseg->charset, (unsigned char *) pos_a, a_length,
00219                                            (unsigned char *) pos_b, b_length, 0, 1))
00220         return 1;
00221     }
00222     else
00223     {
00224       if (a_length != b_length)
00225         return 1;
00226       end= pos_a+a_length;
00227       while (pos_a != end)
00228       {
00229   if (*pos_a++ != *pos_b++)
00230     return 1;
00231       }
00232     }
00233   }
00234   return 0;
00235 }