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 "field_temporal_utils.h"
28 #include "test_utils.h"
29 #include "fake_table.h"
30 
31 #include "field.h"
32 
33 namespace field_date_unittests {
34 
35 using my_testing::Server_initializer;
36 using my_testing::Mock_error_handler;
37 
38 class FieldDateTest : public ::testing::Test
39 {
40 protected:
SetUp()41   virtual void SetUp() { initializer.SetUp(); }
TearDown()42   virtual void TearDown() { initializer.TearDown(); }
43 
thd()44   THD *thd() { return initializer.thd(); }
45 
46   Server_initializer initializer;
47 
48   Field_set *create_field_set(TYPELIB *tl);
49 
50   // Store zero date using different combinations of SQL modes
51   static const int no_modes= 3;
52   static const sql_mode_t strict_modes[no_modes];
53 
54   static const type_conversion_status nozero_expected_status[];
55 };
56 
57 const sql_mode_t FieldDateTest::strict_modes[no_modes]=
58 {
59   MODE_STRICT_TRANS_TABLES,
60   MODE_STRICT_ALL_TABLES,
61   MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES
62 };
63 
64 const type_conversion_status FieldDateTest::nozero_expected_status[]=
65 {
66   TYPE_ERR_BAD_VALUE,
67   TYPE_ERR_BAD_VALUE,
68   TYPE_ERR_BAD_VALUE
69 };
70 
71 
72 class Mock_field_date : public Field_newdate
73 {
74 private:
75   uchar buffer[PACK_LENGTH];
76   uchar null_byte;
77 
initialize()78   void initialize()
79   {
80     ptr= buffer;
81     memset(buffer, 0, PACK_LENGTH);
82     null_byte= '\0';
83     set_null_ptr(&null_byte, 1);
84   }
85 public:
86 
Mock_field_date()87   Mock_field_date()
88     : Field_newdate(0,                          // ptr_arg
89                     NULL,                       // null_ptr_arg
90                     1,                          // null_bit_arg
91                     Field::NONE,                // unireg_check_arg
92                     "field_name")               // field_name_arg
93   {
94     initialize();
95   }
96 
make_writable()97   void make_writable() { bitmap_set_bit(table->write_set, field_index); }
make_readable()98   void make_readable() { bitmap_set_bit(table->read_set, field_index); }
99 };
100 
101 
TEST_F(FieldDateTest,StoreLegalStringValues)102 TEST_F(FieldDateTest, StoreLegalStringValues)
103 {
104   Mock_field_date field_date;
105   Fake_TABLE table(&field_date);
106   table.in_use= thd();
107   field_date.make_writable();
108   field_date.make_readable();
109 
110   {
111     SCOPED_TRACE("");
112     test_store_string(&field_date, STRING_WITH_LEN("2001-01-01"),
113                       "2001-01-01", 0, TYPE_OK);
114   }
115   {
116     SCOPED_TRACE("");
117     test_store_string(&field_date, STRING_WITH_LEN("0000-00-00"),
118                       "0000-00-00", 0, TYPE_OK);
119   }
120   {
121     SCOPED_TRACE("");
122     test_store_string(&field_date, STRING_WITH_LEN("0001-00-00"),
123                       "0001-00-00", 0, TYPE_OK);
124   }
125 }
126 
TEST_F(FieldDateTest,StoreIllegalStringValues)127 TEST_F(FieldDateTest, StoreIllegalStringValues)
128 {
129   Mock_field_date field_date;
130   Fake_TABLE table(&field_date);
131   table.in_use= thd();
132   field_date.make_writable();
133   field_date.make_readable();
134   thd()->count_cuted_fields= CHECK_FIELD_WARN;
135 
136   // Truncates time
137   {
138     SCOPED_TRACE("");
139     test_store_string(&field_date, STRING_WITH_LEN("2001-01-01 00:00:01"),
140                       "2001-01-01",
141                       WARN_DATA_TRUNCATED, TYPE_NOTE_TIME_TRUNCATED);
142   }
143 
144   // Bad year
145   {
146     SCOPED_TRACE("");
147     test_store_string(&field_date, STRING_WITH_LEN("99999-01-01"),
148                       "0000-00-00",
149                       WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
150   }
151 
152   // Bad month
153   {
154     SCOPED_TRACE("");
155     test_store_string(&field_date, STRING_WITH_LEN("2001-13-01"), "0000-00-00",
156                       WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
157   }
158 
159   // Bad day
160   {
161     SCOPED_TRACE("");
162     test_store_string(&field_date, STRING_WITH_LEN("2001-01-32"), "0000-00-00",
163                       WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
164   }
165 
166   // Not a date
167   {
168     SCOPED_TRACE("");
169     test_store_string(&field_date, STRING_WITH_LEN("foo"), "0000-00-00",
170                       WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
171   }
172 }
173 
174 
175 
176 /**
177   Strictness mode test 1:
178 
179   Try storing dates with zeroes when no zero-restrictions apply
180   (neither NO_ZERO_DATE or NO_ZERO_IN_DATE are set). There should be
181   no errors, warnings or notes.
182 */
TEST_F(FieldDateTest,StoreZeroDateSqlModeNoZeroRestrictions)183 TEST_F(FieldDateTest, StoreZeroDateSqlModeNoZeroRestrictions)
184 {
185   Mock_field_date field_date;
186   Fake_TABLE table(&field_date);
187   table.in_use= thd();
188   field_date.make_writable();
189   field_date.make_readable();
190   thd()->count_cuted_fields= CHECK_FIELD_WARN;
191 
192   for (int i= 0; i < no_modes; i++)
193   {
194     SCOPED_TRACE("");
195     store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("0000-00-00"),
196                            "0000-00-00", TYPE_OK, strict_modes[i], 0);
197   }
198 
199   for (int i= 0; i < no_modes; i++)
200   {
201     SCOPED_TRACE("");
202     store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("0000-01-01"),
203                            "0000-01-01", TYPE_OK, strict_modes[i], 0);
204 
205   }
206 
207   for (int i= 0; i < no_modes; i++)
208   {
209     SCOPED_TRACE("");
210     store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("2001-00-01"),
211                            "2001-00-01", TYPE_OK, strict_modes[i], 0);
212 
213   }
214 
215   for (int i= 0; i < no_modes; i++)
216   {
217     SCOPED_TRACE("");
218     store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("2001-01-00"),
219                            "2001-01-00", TYPE_OK, strict_modes[i], 0);
220   }
221 }
222 
223 
224 /**
225   Strictness mode test 2:
226 
227   Try storing dates with zeroes when NO_ZERO_DATE flag is set. There
228   should be no errors, warnings or notes unless the entire date is
229   zero: "0000-00-00"
230 */
TEST_F(FieldDateTest,StoreZeroDateSqlModeNoZeroDate)231 TEST_F(FieldDateTest, StoreZeroDateSqlModeNoZeroDate)
232 {
233   Mock_field_date field_date;
234   Fake_TABLE table(&field_date);
235   table.in_use= thd();
236   field_date.make_writable();
237   field_date.make_readable();
238   thd()->count_cuted_fields= CHECK_FIELD_WARN;
239 
240   // With "MODE_NO_ZERO_DATE" set - Errors if date is all null
241   for (int i= 0; i < no_modes; i++)
242   {
243     SCOPED_TRACE("");
244     store_zero_in_sql_mode(&field_date,
245                            STRING_WITH_LEN("0000-00-00"),
246                            "0000-00-00",
247                            nozero_expected_status[i],
248                            MODE_NO_ZERO_DATE | strict_modes[i],
249                            ER_TRUNCATED_WRONG_VALUE);
250   }
251 
252   // Zero year, month or day is fine
253   for (int i= 0; i < no_modes; i++)
254   {
255     SCOPED_TRACE("");
256     store_zero_in_sql_mode(&field_date,
257                            STRING_WITH_LEN("0000-01-01"),
258                            "0000-01-01",
259                            TYPE_OK,
260                            MODE_NO_ZERO_DATE | strict_modes[i],
261                            0);
262   }
263 
264   for (int i= 0; i < no_modes; i++)
265   {
266     SCOPED_TRACE("");
267     store_zero_in_sql_mode(&field_date,
268                            STRING_WITH_LEN("2001-00-01"),
269                            "2001-00-01",
270                            TYPE_OK,
271                            MODE_NO_ZERO_DATE | strict_modes[i],
272                            0);
273   }
274 
275   for (int i= 0; i < no_modes; i++)
276   {
277     SCOPED_TRACE("");
278     store_zero_in_sql_mode(&field_date,
279                            STRING_WITH_LEN("2001-01-00"),
280                            "2001-01-00",
281                            TYPE_OK,
282                            MODE_NO_ZERO_DATE | strict_modes[i],
283                            0);
284   }
285 }
286 
287 /**
288   Strictness mode test 3:
289 
290   Try storing dates with zeroes when NO_ZERO_IN_DATE flag is set. There
291   should be no errors unless either month or day is zero.
292 */
TEST_F(FieldDateTest,StoreZeroDateSqlModeNoZeroInDate)293 TEST_F(FieldDateTest, StoreZeroDateSqlModeNoZeroInDate)
294 {
295   Mock_field_date field_date;
296   Fake_TABLE table(&field_date);
297   table.in_use= thd();
298   field_date.make_writable();
299   field_date.make_readable();
300   thd()->count_cuted_fields= CHECK_FIELD_WARN;
301 
302   // With "MODE_NO_ZERO_IN_DATE" set - Entire date zero is ok
303   for (int i= 0; i < no_modes; i++)
304   {
305     SCOPED_TRACE("");
306     store_zero_in_sql_mode(&field_date,
307                            STRING_WITH_LEN("0000-00-00"),
308                            "0000-00-00",
309                            TYPE_OK,
310                            MODE_NO_ZERO_IN_DATE | strict_modes[i],
311                            0);
312   }
313 
314   // Year 0 is valid in strict mode too
315   for (int i= 0; i < no_modes; i++)
316   {
317     SCOPED_TRACE("");
318     store_zero_in_sql_mode(&field_date,
319                            STRING_WITH_LEN("0000-01-01"),
320                            "0000-01-01",
321                            TYPE_OK,
322                            MODE_NO_ZERO_IN_DATE | strict_modes[i],
323                            0);
324   }
325 
326   // Month 0 is NOT valid in strict mode, stores all-zero date
327   for (int i= 0; i < no_modes; i++)
328   {
329     SCOPED_TRACE("");
330     store_zero_in_sql_mode(&field_date,
331                            STRING_WITH_LEN("2001-00-01"),
332                            "0000-00-00",
333                            nozero_expected_status[i],
334                            MODE_NO_ZERO_IN_DATE | strict_modes[i],
335                            ER_TRUNCATED_WRONG_VALUE);
336   }
337 
338   // Day 0 is NOT valid in strict mode, stores all-zero date
339   for (int i= 0; i < no_modes; i++)
340   {
341     SCOPED_TRACE("");
342     store_zero_in_sql_mode(&field_date,
343                            STRING_WITH_LEN("2001-01-00"),
344                            "0000-00-00",
345                            nozero_expected_status[i],
346                            MODE_NO_ZERO_IN_DATE | strict_modes[i],
347                            ER_TRUNCATED_WRONG_VALUE);
348   }
349 }
350 
351 
352 }
353