1 /* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
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= 4;
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 0,
60 MODE_STRICT_TRANS_TABLES,
61 MODE_STRICT_ALL_TABLES,
62 MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES
63 };
64
65 const type_conversion_status FieldDateTest::nozero_expected_status[]=
66 {
67 TYPE_NOTE_TIME_TRUNCATED,
68 TYPE_ERR_BAD_VALUE,
69 TYPE_ERR_BAD_VALUE,
70 TYPE_ERR_BAD_VALUE
71 };
72
73
74 class Mock_field_date : public Field_newdate
75 {
76 private:
77 uchar buffer[PACK_LENGTH];
78 uchar null_byte;
79
initialize()80 void initialize()
81 {
82 ptr= buffer;
83 null_ptr= &null_byte;
84 memset(buffer, 0, PACK_LENGTH);
85 null_byte= '\0';
86 }
87 public:
88
Mock_field_date()89 Mock_field_date()
90 : Field_newdate(0, // ptr_arg
91 NULL, // null_ptr_arg
92 1, // null_bit_arg
93 Field::NONE, // unireg_check_arg
94 "field_name") // field_name_arg
95 {
96 initialize();
97 }
98
make_writable()99 void make_writable() { bitmap_set_bit(table->write_set, field_index); }
100 };
101
102
TEST_F(FieldDateTest,StoreLegalStringValues)103 TEST_F(FieldDateTest, StoreLegalStringValues)
104 {
105 Mock_field_date field_date;
106 Fake_TABLE table(&field_date);
107 table.in_use= thd();
108 field_date.make_writable();
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 thd()->count_cuted_fields= CHECK_FIELD_WARN;
134
135 // Truncates time
136 {
137 SCOPED_TRACE("");
138 test_store_string(&field_date, STRING_WITH_LEN("2001-01-01 00:00:01"),
139 "2001-01-01",
140 WARN_DATA_TRUNCATED, TYPE_NOTE_TIME_TRUNCATED);
141 }
142
143 // Bad year
144 {
145 SCOPED_TRACE("");
146 test_store_string(&field_date, STRING_WITH_LEN("99999-01-01"),
147 "0000-00-00",
148 WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
149 }
150
151 // Bad month
152 {
153 SCOPED_TRACE("");
154 test_store_string(&field_date, STRING_WITH_LEN("2001-13-01"), "0000-00-00",
155 WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
156 }
157
158 // Bad day
159 {
160 SCOPED_TRACE("");
161 test_store_string(&field_date, STRING_WITH_LEN("2001-01-32"), "0000-00-00",
162 WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
163 }
164
165 // Not a date
166 {
167 SCOPED_TRACE("");
168 test_store_string(&field_date, STRING_WITH_LEN("foo"), "0000-00-00",
169 WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
170 }
171 }
172
173
174
175 /**
176 Strictness mode test 1:
177
178 Try storing dates with zeroes when no zero-restrictions apply
179 (neither NO_ZERO_DATE or NO_ZERO_IN_DATE are set). There should be
180 no errors, warnings or notes.
181 */
TEST_F(FieldDateTest,StoreZeroDateSqlModeNoZeroRestrictions)182 TEST_F(FieldDateTest, StoreZeroDateSqlModeNoZeroRestrictions)
183 {
184 Mock_field_date field_date;
185 Fake_TABLE table(&field_date);
186 table.in_use= thd();
187 field_date.make_writable();
188 thd()->count_cuted_fields= CHECK_FIELD_WARN;
189
190 for (int i= 0; i < no_modes; i++)
191 {
192 SCOPED_TRACE("");
193 store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("0000-00-00"),
194 "0000-00-00", TYPE_OK, strict_modes[i], 0);
195 }
196
197 for (int i= 0; i < no_modes; i++)
198 {
199 SCOPED_TRACE("");
200 store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("0000-01-01"),
201 "0000-01-01", TYPE_OK, strict_modes[i], 0);
202
203 }
204
205 for (int i= 0; i < no_modes; i++)
206 {
207 SCOPED_TRACE("");
208 store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("2001-00-01"),
209 "2001-00-01", TYPE_OK, strict_modes[i], 0);
210
211 }
212
213 for (int i= 0; i < no_modes; i++)
214 {
215 SCOPED_TRACE("");
216 store_zero_in_sql_mode(&field_date, STRING_WITH_LEN("2001-01-00"),
217 "2001-01-00", TYPE_OK, strict_modes[i], 0);
218 }
219 }
220
221
222 /**
223 Strictness mode test 2:
224
225 Try storing dates with zeroes when NO_ZERO_DATE flag is set. There
226 should be no errors, warnings or notes unless the entire date is
227 zero: "0000-00-00"
228 */
TEST_F(FieldDateTest,StoreZeroDateSqlModeNoZeroDate)229 TEST_F(FieldDateTest, StoreZeroDateSqlModeNoZeroDate)
230 {
231 Mock_field_date field_date;
232 Fake_TABLE table(&field_date);
233 table.in_use= thd();
234 field_date.make_writable();
235 thd()->count_cuted_fields= CHECK_FIELD_WARN;
236
237 // With "MODE_NO_ZERO_DATE" set - Errors if date is all null
238 for (int i= 0; i < no_modes; i++)
239 {
240 SCOPED_TRACE("");
241 store_zero_in_sql_mode(&field_date,
242 STRING_WITH_LEN("0000-00-00"),
243 "0000-00-00",
244 nozero_expected_status[i],
245 MODE_NO_ZERO_DATE | strict_modes[i],
246 ER_WARN_DATA_OUT_OF_RANGE);
247 }
248
249 // Zero year, month or day is fine
250 for (int i= 0; i < no_modes; i++)
251 {
252 SCOPED_TRACE("");
253 store_zero_in_sql_mode(&field_date,
254 STRING_WITH_LEN("0000-01-01"),
255 "0000-01-01",
256 TYPE_OK,
257 MODE_NO_ZERO_DATE | strict_modes[i],
258 0);
259 }
260
261 for (int i= 0; i < no_modes; i++)
262 {
263 SCOPED_TRACE("");
264 store_zero_in_sql_mode(&field_date,
265 STRING_WITH_LEN("2001-00-01"),
266 "2001-00-01",
267 TYPE_OK,
268 MODE_NO_ZERO_DATE | strict_modes[i],
269 0);
270 }
271
272 for (int i= 0; i < no_modes; i++)
273 {
274 SCOPED_TRACE("");
275 store_zero_in_sql_mode(&field_date,
276 STRING_WITH_LEN("2001-01-00"),
277 "2001-01-00",
278 TYPE_OK,
279 MODE_NO_ZERO_DATE | strict_modes[i],
280 0);
281 }
282 }
283
284 /**
285 Strictness mode test 3:
286
287 Try storing dates with zeroes when NO_ZERO_IN_DATE flag is set. There
288 should be no errors unless either month or day is zero.
289 */
TEST_F(FieldDateTest,StoreZeroDateSqlModeNoZeroInDate)290 TEST_F(FieldDateTest, StoreZeroDateSqlModeNoZeroInDate)
291 {
292 Mock_field_date field_date;
293 Fake_TABLE table(&field_date);
294 table.in_use= thd();
295 field_date.make_writable();
296 thd()->count_cuted_fields= CHECK_FIELD_WARN;
297
298 // With "MODE_NO_ZERO_IN_DATE" set - Entire date zero is ok
299 for (int i= 0; i < no_modes; i++)
300 {
301 SCOPED_TRACE("");
302 store_zero_in_sql_mode(&field_date,
303 STRING_WITH_LEN("0000-00-00"),
304 "0000-00-00",
305 TYPE_OK,
306 MODE_NO_ZERO_IN_DATE | strict_modes[i],
307 0);
308 }
309
310 // Year 0 is valid in strict mode too
311 for (int i= 0; i < no_modes; i++)
312 {
313 SCOPED_TRACE("");
314 store_zero_in_sql_mode(&field_date,
315 STRING_WITH_LEN("0000-01-01"),
316 "0000-01-01",
317 TYPE_OK,
318 MODE_NO_ZERO_IN_DATE | strict_modes[i],
319 0);
320 }
321
322 // Month 0 is NOT valid in strict mode, stores all-zero date
323 for (int i= 0; i < no_modes; i++)
324 {
325 SCOPED_TRACE("");
326 store_zero_in_sql_mode(&field_date,
327 STRING_WITH_LEN("2001-00-01"),
328 "0000-00-00",
329 nozero_expected_status[i],
330 MODE_NO_ZERO_IN_DATE | strict_modes[i],
331 ER_WARN_DATA_OUT_OF_RANGE);
332 }
333
334 // Day 0 is NOT valid in strict mode, stores all-zero date
335 for (int i= 0; i < no_modes; i++)
336 {
337 SCOPED_TRACE("");
338 store_zero_in_sql_mode(&field_date,
339 STRING_WITH_LEN("2001-01-00"),
340 "0000-00-00",
341 nozero_expected_status[i],
342 MODE_NO_ZERO_IN_DATE | strict_modes[i],
343 ER_WARN_DATA_OUT_OF_RANGE);
344 }
345 }
346
347
348 }
349