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