1 : /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
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 as published by
5 : the Free Software Foundation; version 2 of the License.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program; if not, write to the Free Software
14 : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
15 :
16 : /* Functions to check if a row is unique */
17 :
18 : #include "maria_def.h"
19 : #include <m_ctype.h>
20 :
21 : /**
22 : Check if there exist a row with the same hash
23 :
24 : @notes
25 : This function is not versioning safe. For the moment this is not a problem
26 : as it's only used for internal temporary tables in MySQL for which there
27 : isn't any versioning information.
28 : */
29 :
30 : my_bool _ma_check_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, uchar *record,
31 : ha_checksum unique_hash, my_off_t disk_pos)
32 0 : {
33 0 : my_off_t lastpos=info->cur_row.lastpos;
34 0 : MARIA_KEYDEF *keyinfo= &info->s->keyinfo[def->key];
35 0 : uchar *key_buff= info->lastkey_buff2;
36 : MARIA_KEY key;
37 0 : DBUG_ENTER("_ma_check_unique");
38 0 : DBUG_PRINT("enter",("unique_hash: %lu", (ulong) unique_hash));
39 :
40 0 : maria_unique_store(record+keyinfo->seg->start, unique_hash);
41 : /* Can't be spatial so it's ok to call _ma_make_key directly here */
42 0 : _ma_make_key(info, &key, def->key, key_buff, record, 0, 0);
43 :
44 : /* The above changed info->lastkey_buff2. Inform maria_rnext_same(). */
45 0 : info->update&= ~HA_STATE_RNEXT_SAME;
46 :
47 0 : DBUG_ASSERT(key.data_length == MARIA_UNIQUE_HASH_LENGTH);
48 0 : if (_ma_search(info, &key, SEARCH_FIND, info->s->state.key_root[def->key]))
49 : {
50 0 : info->page_changed=1; /* Can't optimize read next */
51 0 : info->cur_row.lastpos= lastpos;
52 0 : DBUG_RETURN(0); /* No matching rows */
53 : }
54 :
55 : for (;;)
56 : {
57 0 : if (info->cur_row.lastpos != disk_pos &&
58 : !(*info->s->compare_unique)(info,def,record,info->cur_row.lastpos))
59 : {
60 0 : my_errno=HA_ERR_FOUND_DUPP_UNIQUE;
61 0 : info->errkey= (int) def->key;
62 0 : info->dup_key_pos= info->cur_row.lastpos;
63 0 : info->page_changed= 1; /* Can't optimize read next */
64 0 : info->cur_row.lastpos= lastpos;
65 0 : DBUG_PRINT("info",("Found duplicate"));
66 0 : DBUG_RETURN(1); /* Found identical */
67 : }
68 0 : DBUG_ASSERT(info->last_key.data_length == MARIA_UNIQUE_HASH_LENGTH);
69 0 : if (_ma_search_next(info, &info->last_key, SEARCH_BIGGER,
70 : info->s->state.key_root[def->key]) ||
71 : bcmp((char*) info->last_key.data, (char*) key_buff,
72 : MARIA_UNIQUE_HASH_LENGTH))
73 : {
74 0 : info->page_changed= 1; /* Can't optimize read next */
75 0 : info->cur_row.lastpos= lastpos;
76 0 : DBUG_RETURN(0); /* end of tree */
77 : }
78 : }
79 : }
80 :
81 :
82 : /*
83 : Calculate a hash for a row
84 :
85 : TODO
86 : Add support for bit fields
87 : */
88 :
89 : ha_checksum _ma_unique_hash(MARIA_UNIQUEDEF *def, const uchar *record)
90 0 : {
91 : const uchar *pos, *end;
92 0 : ha_checksum crc= 0;
93 0 : ulong seed1=0, seed2= 4;
94 : HA_KEYSEG *keyseg;
95 :
96 0 : for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
97 : {
98 0 : enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
99 0 : uint length=keyseg->length;
100 :
101 0 : if (keyseg->null_bit)
102 : {
103 0 : if (record[keyseg->null_pos] & keyseg->null_bit)
104 : {
105 : /*
106 : Change crc in a way different from an empty string or 0.
107 : (This is an optimisation; The code will work even if this isn't
108 : done)
109 : */
110 0 : crc=((crc << 8) + 511+
111 : (crc >> (8*sizeof(ha_checksum)-8)));
112 0 : continue;
113 : }
114 : }
115 0 : pos= record+keyseg->start;
116 0 : if (keyseg->flag & HA_VAR_LENGTH_PART)
117 : {
118 0 : uint pack_length= keyseg->bit_start;
119 : uint tmp_length= (pack_length == 1 ? (uint) *pos :
120 0 : uint2korr(pos));
121 0 : pos+= pack_length; /* Skip VARCHAR length */
122 0 : set_if_smaller(length,tmp_length);
123 : }
124 0 : else if (keyseg->flag & HA_BLOB_PART)
125 : {
126 0 : uint tmp_length= _ma_calc_blob_length(keyseg->bit_start,pos);
127 0 : memcpy_fixed((uchar*) &pos,pos+keyseg->bit_start,sizeof(char*));
128 0 : if (!length || length > tmp_length)
129 0 : length=tmp_length; /* The whole blob */
130 : }
131 0 : end= pos+length;
132 0 : if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
133 : type == HA_KEYTYPE_VARTEXT2)
134 : {
135 0 : keyseg->charset->coll->hash_sort(keyseg->charset,
136 : (const uchar*) pos, length, &seed1,
137 : &seed2);
138 0 : crc^= seed1;
139 : }
140 : else
141 0 : while (pos != end)
142 0 : crc=((crc << 8) +
143 : (((uchar) *pos++))) +
144 : (crc >> (8*sizeof(ha_checksum)-8));
145 : }
146 0 : return crc;
147 : }
148 :
149 :
150 : /*
151 : compare unique key for two rows
152 :
153 : TODO
154 : Add support for bit fields
155 :
156 : RETURN
157 : 0 if both rows have equal unique value
158 : 1 Rows are different
159 : */
160 :
161 : my_bool _ma_unique_comp(MARIA_UNIQUEDEF *def, const uchar *a, const uchar *b,
162 : my_bool null_are_equal)
163 0 : {
164 : const uchar *pos_a, *pos_b, *end;
165 : HA_KEYSEG *keyseg;
166 :
167 0 : for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
168 : {
169 0 : enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
170 : uint a_length, b_length;
171 0 : a_length= b_length= keyseg->length;
172 :
173 : /* If part is NULL it's regarded as different */
174 0 : if (keyseg->null_bit)
175 : {
176 : uint tmp;
177 0 : if ((tmp=(a[keyseg->null_pos] & keyseg->null_bit)) !=
178 : (uint) (b[keyseg->null_pos] & keyseg->null_bit))
179 0 : return 1;
180 0 : if (tmp)
181 : {
182 0 : if (!null_are_equal)
183 0 : return 1;
184 : continue;
185 : }
186 : }
187 0 : pos_a= a+keyseg->start;
188 0 : pos_b= b+keyseg->start;
189 0 : if (keyseg->flag & HA_VAR_LENGTH_PART)
190 : {
191 0 : uint pack_length= keyseg->bit_start;
192 0 : if (pack_length == 1)
193 : {
194 0 : a_length= (uint) *pos_a++;
195 0 : b_length= (uint) *pos_b++;
196 : }
197 : else
198 : {
199 0 : a_length= uint2korr(pos_a);
200 0 : b_length= uint2korr(pos_b);
201 0 : pos_a+= 2; /* Skip VARCHAR length */
202 0 : pos_b+= 2;
203 : }
204 0 : set_if_smaller(a_length, keyseg->length); /* Safety */
205 0 : set_if_smaller(b_length, keyseg->length); /* safety */
206 : }
207 0 : else if (keyseg->flag & HA_BLOB_PART)
208 : {
209 : /* Only compare 'length' characters if length != 0 */
210 0 : a_length= _ma_calc_blob_length(keyseg->bit_start,pos_a);
211 0 : b_length= _ma_calc_blob_length(keyseg->bit_start,pos_b);
212 : /* Check that a and b are of equal length */
213 0 : if (keyseg->length)
214 : {
215 : /*
216 : This is used in some cases when we are not interested in comparing
217 : the whole length of the blob.
218 : */
219 0 : set_if_smaller(a_length, keyseg->length);
220 0 : set_if_smaller(b_length, keyseg->length);
221 : }
222 0 : memcpy_fixed((uchar*) &pos_a,pos_a+keyseg->bit_start,sizeof(char*));
223 0 : memcpy_fixed((uchar*) &pos_b,pos_b+keyseg->bit_start,sizeof(char*));
224 : }
225 0 : if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
226 : type == HA_KEYTYPE_VARTEXT2)
227 : {
228 0 : if (ha_compare_text(keyseg->charset, pos_a, a_length,
229 : pos_b, b_length, 0, 1))
230 0 : return 1;
231 : }
232 : else
233 : {
234 0 : if (a_length != b_length)
235 0 : return 1;
236 0 : end= pos_a+a_length;
237 0 : while (pos_a != end)
238 : {
239 0 : if (*pos_a++ != *pos_b++)
240 0 : return 1;
241 : }
242 : }
243 : }
244 0 : return 0;
245 : }
|