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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 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
30my_bool _ma_check_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def,
31 const uchar *record,
32 ha_checksum unique_hash, my_off_t disk_pos)
33{
34 my_off_t lastpos=info->cur_row.lastpos;
35 MARIA_KEYDEF *keyinfo= &info->s->keyinfo[def->key];
36 uchar *key_buff= info->lastkey_buff2;
37 MARIA_KEY key;
38 int error= 0;
39 DBUG_ENTER("_ma_check_unique");
40 DBUG_PRINT("enter",("unique_hash: %lu", (ulong) unique_hash));
41
42 /* We need to store the hash value as a key in the record, breaking const */
43 maria_unique_store(record+keyinfo->seg->start, unique_hash);
44 /* Can't be spatial so it's ok to call _ma_make_key directly here */
45 _ma_make_key(info, &key, def->key, key_buff, record, 0, 0);
46
47 /* The above changed info->lastkey_buff2. Inform maria_rnext_same(). */
48 info->update&= ~HA_STATE_RNEXT_SAME;
49
50 /* Setup that unique key is active key */
51 info->last_key.keyinfo= keyinfo;
52
53 /* any key pointer in data is destroyed */
54 info->lastinx= ~0;
55
56 DBUG_ASSERT(key.data_length == MARIA_UNIQUE_HASH_LENGTH);
57 if (_ma_search(info, &key, SEARCH_FIND | SEARCH_SAVE_BUFF,
58 info->s->state.key_root[def->key]))
59 {
60 info->page_changed=1; /* Can't optimize read next */
61 info->cur_row.lastpos= lastpos;
62 goto end;
63 }
64
65 for (;;)
66 {
67 if (info->cur_row.lastpos != disk_pos &&
68 !(*info->s->compare_unique)(info,def,record,info->cur_row.lastpos))
69 {
70 my_errno=HA_ERR_FOUND_DUPP_UNIQUE;
71 info->errkey= (int) def->key;
72 info->dup_key_pos= info->cur_row.lastpos;
73 info->page_changed= 1; /* Can't optimize read next */
74 info->cur_row.lastpos= lastpos;
75 DBUG_PRINT("info",("Found duplicate"));
76 error= 1; /* Found identical */
77 goto end;
78 }
79 DBUG_ASSERT(info->last_key.data_length == MARIA_UNIQUE_HASH_LENGTH);
80 if (_ma_search_next(info, &info->last_key, SEARCH_BIGGER,
81 info->s->state.key_root[def->key]) ||
82 bcmp(info->last_key.data, key_buff, MARIA_UNIQUE_HASH_LENGTH))
83 {
84 info->page_changed= 1; /* Can't optimize read next */
85 info->cur_row.lastpos= lastpos;
86 break; /* end of tree */
87 }
88 }
89
90end:
91 DBUG_RETURN(error);
92}
93
94
95/*
96 Calculate a hash for a row
97
98 TODO
99 Add support for bit fields
100*/
101
102ha_checksum _ma_unique_hash(MARIA_UNIQUEDEF *def, const uchar *record)
103{
104 const uchar *pos, *end;
105 ha_checksum crc= 0;
106 ulong seed1=0, seed2= 4;
107 HA_KEYSEG *keyseg;
108
109 for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
110 {
111 enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
112 uint length=keyseg->length;
113
114 if (keyseg->null_bit)
115 {
116 if (record[keyseg->null_pos] & keyseg->null_bit)
117 {
118 /*
119 Change crc in a way different from an empty string or 0.
120 (This is an optimisation; The code will work even if this isn't
121 done)
122 */
123 crc=((crc << 8) + 511+
124 (crc >> (8*sizeof(ha_checksum)-8)));
125 continue;
126 }
127 }
128 pos= record+keyseg->start;
129 if (keyseg->flag & HA_VAR_LENGTH_PART)
130 {
131 uint pack_length= keyseg->bit_start;
132 uint tmp_length= (pack_length == 1 ? (uint) *pos :
133 uint2korr(pos));
134 pos+= pack_length; /* Skip VARCHAR length */
135 set_if_smaller(length,tmp_length);
136 }
137 else if (keyseg->flag & HA_BLOB_PART)
138 {
139 uint tmp_length= _ma_calc_blob_length(keyseg->bit_start,pos);
140 memcpy((void*) &pos,pos+keyseg->bit_start,sizeof(char*));
141 if (!length || length > tmp_length)
142 length=tmp_length; /* The whole blob */
143 }
144 end= pos+length;
145 if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
146 type == HA_KEYTYPE_VARTEXT2)
147 {
148 keyseg->charset->coll->hash_sort(keyseg->charset,
149 (const uchar*) pos, length, &seed1,
150 &seed2);
151 crc+= seed1;
152 }
153 else
154 {
155 my_hash_sort_bin((CHARSET_INFO*) 0, pos, (size_t) (end-pos),
156 &seed1, &seed2);
157 crc+= seed1;
158 }
159 }
160 return crc;
161}
162
163
164/*
165 compare unique key for two rows
166
167 TODO
168 Add support for bit fields
169
170 RETURN
171 0 if both rows have equal unique value
172 1 Rows are different
173*/
174
175my_bool _ma_unique_comp(MARIA_UNIQUEDEF *def, const uchar *a, const uchar *b,
176 my_bool null_are_equal)
177{
178 const uchar *pos_a, *pos_b, *end;
179 HA_KEYSEG *keyseg;
180
181 for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
182 {
183 enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
184 uint a_length, b_length;
185 a_length= b_length= keyseg->length;
186
187 /* If part is NULL it's regarded as different */
188 if (keyseg->null_bit)
189 {
190 uint tmp;
191 if ((tmp=(a[keyseg->null_pos] & keyseg->null_bit)) !=
192 (uint) (b[keyseg->null_pos] & keyseg->null_bit))
193 return 1;
194 if (tmp)
195 {
196 if (!null_are_equal)
197 return 1;
198 continue;
199 }
200 }
201 pos_a= a+keyseg->start;
202 pos_b= b+keyseg->start;
203 if (keyseg->flag & HA_VAR_LENGTH_PART)
204 {
205 uint pack_length= keyseg->bit_start;
206 if (pack_length == 1)
207 {
208 a_length= (uint) *pos_a++;
209 b_length= (uint) *pos_b++;
210 }
211 else
212 {
213 a_length= uint2korr(pos_a);
214 b_length= uint2korr(pos_b);
215 pos_a+= 2; /* Skip VARCHAR length */
216 pos_b+= 2;
217 }
218 set_if_smaller(a_length, keyseg->length); /* Safety */
219 set_if_smaller(b_length, keyseg->length); /* safety */
220 }
221 else if (keyseg->flag & HA_BLOB_PART)
222 {
223 /* Only compare 'length' characters if length != 0 */
224 a_length= _ma_calc_blob_length(keyseg->bit_start,pos_a);
225 b_length= _ma_calc_blob_length(keyseg->bit_start,pos_b);
226 /* Check that a and b are of equal length */
227 if (keyseg->length)
228 {
229 /*
230 This is used in some cases when we are not interested in comparing
231 the whole length of the blob.
232 */
233 set_if_smaller(a_length, keyseg->length);
234 set_if_smaller(b_length, keyseg->length);
235 }
236 memcpy((void*) &pos_a, pos_a+keyseg->bit_start, sizeof(char*));
237 memcpy((void*) &pos_b, pos_b+keyseg->bit_start, sizeof(char*));
238 }
239 if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
240 type == HA_KEYTYPE_VARTEXT2)
241 {
242 if (ha_compare_text(keyseg->charset, pos_a, a_length,
243 pos_b, b_length, 0))
244 return 1;
245 }
246 else
247 {
248 if (a_length != b_length)
249 return 1;
250 end= pos_a+a_length;
251 while (pos_a != end)
252 {
253 if (*pos_a++ != *pos_b++)
254 return 1;
255 }
256 }
257 }
258 return 0;
259}
260