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