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