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