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