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