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