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