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