1 /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /* Functions to check if a row is unique */
24 
25 #include "myisamdef.h"
26 #include <m_ctype.h>
27 
mi_check_unique(MI_INFO * info,MI_UNIQUEDEF * def,uchar * record,ha_checksum unique_hash,my_off_t disk_pos)28 my_bool mi_check_unique(MI_INFO *info, MI_UNIQUEDEF *def, uchar *record,
29 			ha_checksum unique_hash, my_off_t disk_pos)
30 {
31   my_off_t lastpos=info->lastpos;
32   MI_KEYDEF *key= &info->s->keyinfo[def->key];
33   uchar *key_buff=info->lastkey2;
34   DBUG_ENTER("mi_check_unique");
35 
36   mi_unique_store(record+key->seg->start, unique_hash);
37   _mi_make_key(info,def->key,key_buff,record,0);
38 
39   if (_mi_search(info,info->s->keyinfo+def->key,key_buff,MI_UNIQUE_HASH_LENGTH,
40 		 SEARCH_FIND,info->s->state.key_root[def->key]))
41   {
42     info->page_changed=1;			/* Can't optimize read next */
43     info->lastpos= lastpos;
44     DBUG_RETURN(0);				/* No matching rows */
45   }
46 
47   for (;;)
48   {
49     if (info->lastpos != disk_pos &&
50 	!(*info->s->compare_unique)(info,def,record,info->lastpos))
51     {
52       my_errno=HA_ERR_FOUND_DUPP_UNIQUE;
53       info->errkey= (int) def->key;
54       info->dupp_key_pos= info->lastpos;
55       info->page_changed=1;			/* Can't optimize read next */
56       info->lastpos=lastpos;
57       DBUG_PRINT("info",("Found duplicate"));
58       DBUG_RETURN(1);				/* Found identical  */
59     }
60     if (_mi_search_next(info,info->s->keyinfo+def->key, info->lastkey,
61 			MI_UNIQUE_HASH_LENGTH, SEARCH_BIGGER,
62 			info->s->state.key_root[def->key]) ||
63 	memcmp(info->lastkey, key_buff, MI_UNIQUE_HASH_LENGTH))
64     {
65       info->page_changed=1;			/* Can't optimize read next */
66       info->lastpos=lastpos;
67       DBUG_RETURN(0);				/* end of tree */
68     }
69   }
70 }
71 
72 
73 /*
74   Calculate a hash for a row
75 
76   TODO
77     Add support for bit fields
78 */
79 
mi_unique_hash(MI_UNIQUEDEF * def,const uchar * record)80 ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const uchar *record)
81 {
82   const uchar *pos, *end;
83   ha_checksum crc= 0;
84   ulong seed1=0, seed2= 4;
85   HA_KEYSEG *keyseg;
86 
87   for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
88   {
89     enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
90     uint length=keyseg->length;
91 
92     if (keyseg->null_bit)
93     {
94       if (record[keyseg->null_pos] & keyseg->null_bit)
95       {
96 	/*
97 	  Change crc in a way different from an empty string or 0.
98 	  (This is an optimisation;  The code will work even if this isn't
99 	  done)
100 	*/
101 	crc=((crc << 8) + 511+
102 	     (crc >> (8*sizeof(ha_checksum)-8)));
103 	continue;
104       }
105     }
106     pos= record+keyseg->start;
107     if (keyseg->flag & HA_VAR_LENGTH_PART)
108     {
109       uint pack_length=  keyseg->bit_start;
110       uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
111                         uint2korr(pos));
112       pos+= pack_length;			/* Skip VARCHAR length */
113       set_if_smaller(length,tmp_length);
114     }
115     else if (keyseg->flag & HA_BLOB_PART)
116     {
117       uint tmp_length=_mi_calc_blob_length(keyseg->bit_start,pos);
118       memcpy(&pos, pos+keyseg->bit_start, sizeof(char*));
119       if (!length || length > tmp_length)
120 	length=tmp_length;			/* The whole blob */
121     }
122     end= pos+length;
123     if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
124         type == HA_KEYTYPE_VARTEXT2)
125     {
126       keyseg->charset->coll->hash_sort(keyseg->charset,
127                                        (const uchar*) pos, length, &seed1,
128                                        &seed2);
129       crc^= seed1;
130     }
131     else
132       while (pos != end)
133 	crc=((crc << 8) +
134 	     (((uchar)  *(uchar*) pos++))) +
135 	  (crc >> (8*sizeof(ha_checksum)-8));
136   }
137   return crc;
138 }
139 
140 
141 /*
142   compare unique key for two rows
143 
144   TODO
145     Add support for bit fields
146 
147   RETURN
148     0   if both rows have equal unique value
149     #   Rows are different
150 */
151 
mi_unique_comp(MI_UNIQUEDEF * def,const uchar * a,const uchar * b,my_bool null_are_equal)152 int mi_unique_comp(MI_UNIQUEDEF *def, const uchar *a, const uchar *b,
153 		   my_bool null_are_equal)
154 {
155   const uchar *pos_a, *pos_b, *end;
156   HA_KEYSEG *keyseg;
157 
158   for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
159   {
160     enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
161     uint a_length, b_length;
162     a_length= b_length= keyseg->length;
163 
164     /* If part is NULL it's regarded as different */
165     if (keyseg->null_bit)
166     {
167       uint tmp;
168       if ((tmp=(a[keyseg->null_pos] & keyseg->null_bit)) !=
169 	  (uint) (b[keyseg->null_pos] & keyseg->null_bit))
170 	return 1;
171       if (tmp)
172       {
173 	if (!null_are_equal)
174 	  return 1;
175 	continue;
176       }
177     }
178     pos_a= a+keyseg->start;
179     pos_b= b+keyseg->start;
180     if (keyseg->flag & HA_VAR_LENGTH_PART)
181     {
182       uint pack_length= keyseg->bit_start;
183       if (pack_length == 1)
184       {
185         a_length= (uint) *(uchar*) pos_a++;
186         b_length= (uint) *(uchar*) pos_b++;
187       }
188       else
189       {
190         a_length= uint2korr(pos_a);
191         b_length= uint2korr(pos_b);
192         pos_a+= 2;				/* Skip VARCHAR length */
193         pos_b+= 2;
194       }
195       set_if_smaller(a_length, keyseg->length); /* Safety */
196       set_if_smaller(b_length, keyseg->length); /* safety */
197     }
198     else if (keyseg->flag & HA_BLOB_PART)
199     {
200       /* Only compare 'length' characters if length != 0 */
201       a_length= _mi_calc_blob_length(keyseg->bit_start,pos_a);
202       b_length= _mi_calc_blob_length(keyseg->bit_start,pos_b);
203       /* Check that a and b are of equal length */
204       if (keyseg->length)
205       {
206         /*
207           This is used in some cases when we are not interested in comparing
208           the whole length of the blob.
209         */
210         set_if_smaller(a_length, keyseg->length);
211         set_if_smaller(b_length, keyseg->length);
212       }
213       memcpy(&pos_a, pos_a+keyseg->bit_start, sizeof(char*));
214       memcpy(&pos_b, pos_b+keyseg->bit_start, sizeof(char*));
215     }
216     if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
217         type == HA_KEYTYPE_VARTEXT2)
218     {
219       if (ha_compare_text(keyseg->charset, (uchar *) pos_a, a_length,
220                                            (uchar *) pos_b, b_length, 0, 1))
221         return 1;
222     }
223     else
224     {
225       if (a_length != b_length)
226         return 1;
227       end= pos_a+a_length;
228       while (pos_a != end)
229       {
230 	if (*pos_a++ != *pos_b++)
231 	  return 1;
232       }
233     }
234   }
235   return 0;
236 }
237