1 /* Copyright (c) 2012, 2021, Oracle and/or its affiliates.
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, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22
23 // First include (the generated) my_config.h, to get correct platform defines.
24 #include "my_config.h"
25 #include <gtest/gtest.h>
26
27 #include "test_utils.h"
28 #include "fake_table.h"
29
30 #include "field.h"
31
32 type_conversion_status
33 store_internal_with_error_check(Field_new_decimal *field,
34 int conversion_err, my_decimal *value);
35 namespace field_newdecimal_unittest {
36
37 using my_testing::chars_2_decimal;
38 using my_testing::Server_initializer;
39 using my_testing::Mock_error_handler;
40
41 class FieldNewDecimalTest : public ::testing::Test
42 {
43 protected:
SetUp()44 virtual void SetUp() { initializer.SetUp(); }
TearDown()45 virtual void TearDown() { initializer.TearDown(); }
46
thd()47 THD *thd() { return initializer.thd(); }
48
49 Server_initializer initializer;
50
51 Field_set *create_field_set(TYPELIB *tl);
52 };
53
54
55 class Mock_field_new_decimal : public Field_new_decimal
56 {
57 uchar buffer[MAX_FIELD_WIDTH];
58 uchar null_byte;
initialize()59 void initialize()
60 {
61 ptr= buffer;
62 memset(buffer, 0, MAX_FIELD_WIDTH);
63 null_byte= '\0';
64 set_null_ptr(&null_byte, 1);
65 }
66
67 public:
Mock_field_new_decimal(int decimals)68 Mock_field_new_decimal(int decimals)
69 : Field_new_decimal(0, // ptr_arg
70 8, // len_arg
71 NULL, // null_ptr_arg
72 1, // null_bit_arg
73 Field::NONE, // unireg_check_arg
74 "field_name", // field_name_arg
75 decimals, // dec_arg
76 false, // zero_arg
77 false) // unsigned_arg
78 {
79 initialize();
80 }
81
make_writable()82 void make_writable() { bitmap_set_bit(table->write_set, field_index); }
make_readable()83 void make_readable() { bitmap_set_bit(table->read_set, field_index); }
84
test_store_string(const char * store_value,const int length,const char * expected_string_result,const longlong expected_int_result,const double expected_real_result,const int expected_error_no,const type_conversion_status expected_status)85 void test_store_string(const char *store_value, const int length,
86 const char *expected_string_result,
87 const longlong expected_int_result,
88 const double expected_real_result,
89 const int expected_error_no,
90 const type_conversion_status expected_status)
91 {
92 char buff[MAX_FIELD_WIDTH];
93 String str(buff, sizeof(buff), &my_charset_bin);
94 String unused;
95
96 Mock_error_handler error_handler(table->in_use, expected_error_no);
97 type_conversion_status err= store(store_value, length, &my_charset_latin1);
98 val_str(&str, &unused);
99
100 EXPECT_STREQ(expected_string_result, str.ptr());
101 EXPECT_EQ(expected_int_result, val_int());
102 EXPECT_EQ(expected_real_result, val_real());
103
104 EXPECT_FALSE(is_null());
105 EXPECT_EQ(expected_status, err);
106 EXPECT_EQ((expected_error_no == 0 ? 0 : 1), error_handler.handle_called());
107 }
108
109 };
110
111
TEST_F(FieldNewDecimalTest,StoreLegalStringValues)112 TEST_F(FieldNewDecimalTest, StoreLegalStringValues)
113 {
114 // Alows storing this range [-999.999, 999.999]
115 Mock_field_new_decimal field_dec(3);
116 Fake_TABLE table(&field_dec);
117 table.in_use= thd();
118 field_dec.make_writable();
119 field_dec.make_readable();
120 thd()->count_cuted_fields= CHECK_FIELD_WARN;
121
122 {
123 SCOPED_TRACE("");
124 field_dec.test_store_string(STRING_WITH_LEN("10.01"), "10.010", 10, 10.01,
125 0, TYPE_OK);
126 }
127 {
128 SCOPED_TRACE("");
129 field_dec.test_store_string(STRING_WITH_LEN("0"), "0.000", 0, 0,
130 0, TYPE_OK);
131 }
132 }
133
134
TEST_F(FieldNewDecimalTest,StoreIllegalStringValues)135 TEST_F(FieldNewDecimalTest, StoreIllegalStringValues)
136 {
137 // Alows storing this range [-999.999, 999.999]
138 Mock_field_new_decimal field_dec(3);
139 Fake_TABLE table(&field_dec);
140 table.in_use= thd();
141 field_dec.make_writable();
142 field_dec.make_readable();
143 thd()->count_cuted_fields= CHECK_FIELD_WARN;
144
145 // Truncated (precision beyond 3 decimals is lost)
146 {
147 SCOPED_TRACE("");
148 field_dec.test_store_string(STRING_WITH_LEN("10.0101"), "10.010",
149 10, 10.01,
150 WARN_DATA_TRUNCATED, TYPE_NOTE_TRUNCATED);
151 }
152 {
153 SCOPED_TRACE("");
154 field_dec.test_store_string(STRING_WITH_LEN("10.0109"), "10.011",
155 10, 10.011,
156 WARN_DATA_TRUNCATED, TYPE_NOTE_TRUNCATED);
157 }
158 // Values higher and lower than valid range for the decimal
159 {
160 SCOPED_TRACE("");
161 field_dec.test_store_string(STRING_WITH_LEN("10000"), "999.999",
162 1000, 999.999,
163 ER_WARN_DATA_OUT_OF_RANGE,
164 TYPE_WARN_OUT_OF_RANGE);
165 }
166
167 // Values higher and lower than valid range for the decimal
168 {
169 SCOPED_TRACE("");
170 field_dec.test_store_string(STRING_WITH_LEN("-10000"), "-999.999",
171 -1000, -999.999,
172 ER_WARN_DATA_OUT_OF_RANGE,
173 TYPE_WARN_OUT_OF_RANGE);
174 }
175 }
176
177
test_store_internal(Field_new_decimal * field,my_decimal * value,const char * expected_string_result,const longlong expected_int_result,const double expected_real_result,const int conversion_error,const int expected_error_no,const type_conversion_status expected_status)178 static void test_store_internal(Field_new_decimal *field,
179 my_decimal *value,
180 const char *expected_string_result,
181 const longlong expected_int_result,
182 const double expected_real_result,
183 const int conversion_error,
184 const int expected_error_no,
185 const type_conversion_status expected_status)
186 {
187 char buff[MAX_FIELD_WIDTH];
188 String str(buff, sizeof(buff), &my_charset_bin);
189 String unused;
190
191 Mock_error_handler error_handler(field->table->in_use, expected_error_no);
192 type_conversion_status err=
193 store_internal_with_error_check(field, conversion_error, value);
194 field->val_str(&str, &unused);
195 EXPECT_STREQ(expected_string_result, str.ptr());
196 EXPECT_EQ(expected_int_result, field->val_int());
197 EXPECT_EQ(expected_real_result, field->val_real());
198
199 EXPECT_EQ(expected_status, err);
200 }
201
202
203 /**
204 Test store_internal_with_error_check(). This is an internal store
205 function for Field_new_decimal. The function does not modify the
206 NULL value of the field so we don't test field.is_null()
207 */
TEST_F(FieldNewDecimalTest,storeInternalWithErrorCheckLegalValues)208 TEST_F(FieldNewDecimalTest, storeInternalWithErrorCheckLegalValues)
209 {
210 // Alows storing this range [-99.9999, 99.9999]
211 Mock_field_new_decimal field_dec(4);
212 Fake_TABLE table(&field_dec);
213 table.in_use= thd();
214 field_dec.make_writable();
215 field_dec.make_readable();
216 thd()->count_cuted_fields= CHECK_FIELD_WARN;
217
218 my_decimal d10_01;
219 my_decimal dMin10_01;
220 my_decimal d10_01001;
221 my_decimal d10_01009;
222 my_decimal dInsignificant;
223
224 EXPECT_EQ(0, chars_2_decimal("10.01", &d10_01));
225 EXPECT_EQ(0, chars_2_decimal("-10.01", &dMin10_01));
226 EXPECT_EQ(0, chars_2_decimal("10.01001", &d10_01001));
227 EXPECT_EQ(0, chars_2_decimal("10.01009", &d10_01009));
228 EXPECT_EQ(0, chars_2_decimal("0.00000000001", &dInsignificant));
229
230 // Legal values
231 {
232 SCOPED_TRACE("");
233 test_store_internal(&field_dec, &d10_01, "10.0100", 10, 10.01,
234 E_DEC_OK, 0, TYPE_OK);
235 }
236 {
237 SCOPED_TRACE("");
238 test_store_internal(&field_dec, &dMin10_01, "-10.0100", -10, -10.01,
239 E_DEC_OK, 0, TYPE_OK);
240 }
241
242 // Legal values, but rounded
243 {
244 SCOPED_TRACE("");
245 test_store_internal(&field_dec, &d10_01001, "10.0100", 10, 10.01,
246 E_DEC_OK, WARN_DATA_TRUNCATED, TYPE_NOTE_TRUNCATED);
247 }
248 {
249 SCOPED_TRACE("");
250 test_store_internal(&field_dec, &d10_01009, "10.0101", 10, 10.0101,
251 E_DEC_OK, WARN_DATA_TRUNCATED, TYPE_NOTE_TRUNCATED);
252 }
253 {
254 SCOPED_TRACE("");
255 test_store_internal(&field_dec, &dInsignificant, "0.0000", 0, 0, E_DEC_OK,
256 WARN_DATA_TRUNCATED, TYPE_NOTE_TRUNCATED);
257 }
258 }
259
260
261 /**
262 Test store_internal_with_error_check() - out of range valuse
263 */
TEST_F(FieldNewDecimalTest,storeInternalWithErrorCheckOutOfRange)264 TEST_F(FieldNewDecimalTest, storeInternalWithErrorCheckOutOfRange)
265 {
266 // Alows storing this range [-99.9999, 99.9999]
267 Mock_field_new_decimal field_dec(4);
268 Fake_TABLE table(&field_dec);
269 table.in_use= thd();
270 field_dec.make_writable();
271 field_dec.make_readable();
272 thd()->count_cuted_fields= CHECK_FIELD_WARN;
273
274 my_decimal dTooHigh;
275 my_decimal dTooLow;
276
277 EXPECT_EQ(0, chars_2_decimal("1000", &dTooHigh));
278 EXPECT_EQ(0, chars_2_decimal("-1000", &dTooLow));
279
280 {
281 SCOPED_TRACE("");
282 test_store_internal(&field_dec, &dTooHigh, "99.9999", 100, 99.9999,
283 E_DEC_OK, ER_WARN_DATA_OUT_OF_RANGE,
284 TYPE_WARN_OUT_OF_RANGE);
285 }
286 {
287 SCOPED_TRACE("");
288 test_store_internal(&field_dec, &dTooLow, "-99.9999", -100, -99.9999,
289 E_DEC_OK, ER_WARN_DATA_OUT_OF_RANGE,
290 TYPE_WARN_OUT_OF_RANGE);
291 }
292
293 }
294
295
296 /**
297 Test store_internal_with_error_check() - Test first parameter: the error.
298
299 When E_DEC_OVERFLOW is specified, the min/max value (depends on
300 the sign of the input value) of the field is used to overwrite the
301 input decimal value because E_DEC_OVERFLOW indicates that the decimal
302 conversion got a number that was too high/low.
303 */
TEST_F(FieldNewDecimalTest,storeInternalWithErrorCheckEDecOverflow)304 TEST_F(FieldNewDecimalTest, storeInternalWithErrorCheckEDecOverflow)
305 {
306 // Alows storing this range [-99.9999, 99.9999]
307 Mock_field_new_decimal field_dec(4);
308 Fake_TABLE table(&field_dec);
309 table.in_use= thd();
310 field_dec.make_writable();
311 field_dec.make_readable();
312 thd()->count_cuted_fields= CHECK_FIELD_WARN;
313
314 my_decimal d10_01;
315 my_decimal dMin10_01;
316 my_decimal dInsignificant;
317 my_decimal dTooHigh;
318 my_decimal dTooLow;
319
320 EXPECT_EQ(0, chars_2_decimal("10.01", &d10_01));
321 EXPECT_EQ(0, chars_2_decimal("-10.01", &dMin10_01));
322 EXPECT_EQ(0, chars_2_decimal("0.00000000001", &dInsignificant));
323 EXPECT_EQ(0, chars_2_decimal("1000", &dTooHigh));
324 EXPECT_EQ(0, chars_2_decimal("-1000", &dTooLow));
325
326 // Positive number - the field's MAX value is used
327 {
328 SCOPED_TRACE("");
329 test_store_internal(&field_dec, &d10_01, "99.9999", 100, 99.9999,
330 E_DEC_OVERFLOW, ER_WARN_DATA_OUT_OF_RANGE,
331 TYPE_WARN_OUT_OF_RANGE);
332 }
333 {
334 SCOPED_TRACE("");
335 test_store_internal(&field_dec, &dInsignificant, "99.9999", 100, 99.9999,
336 E_DEC_OVERFLOW, ER_WARN_DATA_OUT_OF_RANGE,
337 TYPE_WARN_OUT_OF_RANGE);
338
339 }
340 {
341 SCOPED_TRACE("");
342 test_store_internal(&field_dec, &dTooHigh, "99.9999", 100, 99.9999,
343 E_DEC_OVERFLOW, ER_WARN_DATA_OUT_OF_RANGE,
344 TYPE_WARN_OUT_OF_RANGE);
345 }
346
347 // Negative number - the field's MIN value is used
348 {
349 SCOPED_TRACE("");
350 test_store_internal(&field_dec, &dMin10_01, "-99.9999", -100, -99.9999,
351 E_DEC_OVERFLOW, ER_WARN_DATA_OUT_OF_RANGE,
352 TYPE_WARN_OUT_OF_RANGE);
353
354 }
355 {
356 SCOPED_TRACE("");
357 test_store_internal(&field_dec, &dTooLow, "-99.9999", -100, -99.9999,
358 E_DEC_OVERFLOW, ER_WARN_DATA_OUT_OF_RANGE,
359 TYPE_WARN_OUT_OF_RANGE);
360 }
361 }
362
363
364 /**
365 Test store_internal_with_error_check() - Test first parameter: the error.
366
367 When E_DEC_TRUNCATED is specified, a truncation warning will
368 appear iff Field_new_decimal::store_value() would otherwise not
369 issue a warning. The rationale is that E_DEC_TRUNCATED indicates
370 that the value to store has already been truncated, something that
371 will not be noticed by store_value(). However, the value is not
372 automatically changed like in the E_DEC_OVERFLOW case tested
373 above.
374 */
TEST_F(FieldNewDecimalTest,storeInternalWithErrorCheckEDecTrunkated)375 TEST_F(FieldNewDecimalTest, storeInternalWithErrorCheckEDecTrunkated)
376 {
377 // Alows storing this range [-99.9999, 99.9999]
378 Mock_field_new_decimal field_dec(4);
379 Fake_TABLE table(&field_dec);
380 table.in_use= thd();
381 field_dec.make_writable();
382 field_dec.make_readable();
383 thd()->count_cuted_fields= CHECK_FIELD_WARN;
384
385 my_decimal d10_01;
386 my_decimal dMin10_01;
387 my_decimal dInsignificant;
388 my_decimal dTooHigh;
389 my_decimal dTooLow;
390
391 EXPECT_EQ(0, chars_2_decimal("10.01", &d10_01));
392 EXPECT_EQ(0, chars_2_decimal("-10.01", &dMin10_01));
393 EXPECT_EQ(0, chars_2_decimal("0.00000000001", &dInsignificant));
394 EXPECT_EQ(0, chars_2_decimal("1000", &dTooHigh));
395 EXPECT_EQ(0, chars_2_decimal("-1000", &dTooLow));
396
397
398 // Conversion went fine
399 {
400 SCOPED_TRACE("");
401 test_store_internal(&field_dec, &d10_01, "10.0100", 10, 10.01,
402 E_DEC_TRUNCATED, WARN_DATA_TRUNCATED,
403 TYPE_NOTE_TRUNCATED);
404 }
405 {
406 SCOPED_TRACE("");
407 test_store_internal(&field_dec, &dMin10_01, "-10.0100", -10, -10.01,
408 E_DEC_TRUNCATED, WARN_DATA_TRUNCATED,
409 TYPE_NOTE_TRUNCATED);
410 }
411
412 {
413 SCOPED_TRACE("");
414 test_store_internal(&field_dec, &dInsignificant, "0.0000", 0, 0,
415 E_DEC_TRUNCATED, WARN_DATA_TRUNCATED,
416 TYPE_NOTE_TRUNCATED);
417 }
418
419 /*
420 In what follows, the values are out of range causing warning
421 ER_WARN_DATA_OUT_OF_RANGE instead of WARN_DATA_TRUNCATED.
422 */
423 {
424 SCOPED_TRACE("");
425 test_store_internal(&field_dec, &dTooHigh, "99.9999", 100, 99.9999,
426 E_DEC_TRUNCATED, ER_WARN_DATA_OUT_OF_RANGE,
427 TYPE_WARN_OUT_OF_RANGE);
428 }
429 {
430 SCOPED_TRACE("");
431 test_store_internal(&field_dec, &dTooLow, "-99.9999", -100, -99.9999,
432 E_DEC_TRUNCATED, ER_WARN_DATA_OUT_OF_RANGE,
433 TYPE_WARN_OUT_OF_RANGE);
434 }
435 }
436
437
438 /**
439 Test store_internal_with_error_check() - Test first parameter: the error.
440
441 Any E_DEC_* value other than E_DEC_OK, E_DEC_TRUNCATED and
442 E_DEC_OVERFLOW will be ignored.
443 */
TEST_F(FieldNewDecimalTest,storeInternalWithErrorCheckRestOfParams)444 TEST_F(FieldNewDecimalTest, storeInternalWithErrorCheckRestOfParams)
445 {
446 // Alows storing this range [-99.9999, 99.9999]
447 Mock_field_new_decimal field_dec(4);
448 Fake_TABLE table(&field_dec);
449 table.in_use= thd();
450 field_dec.make_writable();
451 field_dec.make_readable();
452 thd()->count_cuted_fields= CHECK_FIELD_WARN;
453
454 my_decimal d10_01;
455 EXPECT_EQ(0, chars_2_decimal("10.01", &d10_01));
456
457 test_store_internal(&field_dec, &d10_01, "10.0100", 10, 10.01,
458 E_DEC_DIV_ZERO, 0, TYPE_OK);
459
460 test_store_internal(&field_dec, &d10_01, "10.0100", 10, 10.01,
461 E_DEC_BAD_NUM, 0, TYPE_OK);
462
463 test_store_internal(&field_dec, &d10_01, "10.0100", 10, 10.01,
464 E_DEC_OOM, 0, TYPE_OK);
465 }
466
467 }
468