~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to server/field/timestamp.cc

Merge work from Toru

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
3
 *
 
4
 *  Copyright (C) 2008 MySQL
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 */
 
20
 
 
21
#ifdef USE_PRAGMA_IMPLEMENTATION
 
22
#pragma implementation                          // gcc: Class implementation
 
23
#endif
 
24
 
 
25
#include "timestamp.h"
 
26
 
 
27
/**
 
28
  TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to 
 
29
  2038-01-01 00:00:00 UTC stored as number of seconds since Unix 
 
30
  Epoch in UTC.
 
31
  
 
32
  Up to one of timestamps columns in the table can be automatically 
 
33
  set on row update and/or have NOW() as default value.
 
34
  TABLE::timestamp_field points to Field object for such timestamp with 
 
35
  auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
 
36
  field, and is used by handler code which performs updates required.
 
37
  
 
38
  Actually SQL-99 says that we should allow niladic functions (like NOW())
 
39
  as defaults for any field. Current limitations (only NOW() and only 
 
40
  for one TIMESTAMP field) are because of restricted binary .frm format 
 
41
  and should go away in the future.
 
42
  
 
43
  Also because of this limitation of binary .frm format we use 5 different
 
44
  unireg_check values with TIMESTAMP field to distinguish various cases of
 
45
  DEFAULT or ON UPDATE values. These values are:
 
46
  
 
47
  TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with
 
48
    auto-set-on-update (or now() as default) in this table before, then this 
 
49
    field has NOW() as default and is updated when row changes, else it is 
 
50
    field which has 0 as default value and is not automatically updated.
 
51
  TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
 
52
    automatically (TIMESTAMP DEFAULT NOW())
 
53
  TIMESTAMP_UN_FIELD - field which is set on update automatically but has not 
 
54
    NOW() as default (but it may has 0 or some other const timestamp as 
 
55
    default) (TIMESTAMP ON UPDATE NOW()).
 
56
  TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on 
 
57
    update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW())
 
58
  NONE - field which is not auto-set on update with some other than NOW() 
 
59
    default value (TIMESTAMP DEFAULT 0).
 
60
 
 
61
  Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are 
 
62
  left only for preserving ability to read old tables. Such fields replaced 
 
63
  with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is 
 
64
  because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for 
 
65
  "TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such 
 
66
  specification too but ignored default value for first timestamp, which of 
 
67
  course is non-standard.) In most cases user won't notice any change, only
 
68
  exception is different behavior of old/new timestamps during ALTER TABLE.
 
69
 */
 
70
 
 
71
Field_timestamp::Field_timestamp(uchar *ptr_arg,
 
72
                                 uint32 len_arg __attribute__((__unused__)),
 
73
                                 uchar *null_ptr_arg, uchar null_bit_arg,
 
74
                                 enum utype unireg_check_arg,
 
75
                                 const char *field_name_arg,
 
76
                                 TABLE_SHARE *share,
 
77
                                 CHARSET_INFO *cs)
 
78
  :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
 
79
             unireg_check_arg, field_name_arg, cs)
 
80
{
 
81
  /* For 4.0 MYD and 4.0 InnoDB compatibility */
 
82
  flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
 
83
  if (!share->timestamp_field && unireg_check != NONE)
 
84
  {
 
85
    /* This timestamp has auto-update */
 
86
    share->timestamp_field= this;
 
87
    flags|= TIMESTAMP_FLAG;
 
88
    if (unireg_check != TIMESTAMP_DN_FIELD)
 
89
      flags|= ON_UPDATE_NOW_FLAG;
 
90
  }
 
91
}
 
92
 
 
93
 
 
94
Field_timestamp::Field_timestamp(bool maybe_null_arg,
 
95
                                 const char *field_name_arg,
 
96
                                 CHARSET_INFO *cs)
 
97
  :Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
 
98
             maybe_null_arg ? (uchar*) "": 0, 0,
 
99
             NONE, field_name_arg, cs)
 
100
{
 
101
  /* For 4.0 MYD and 4.0 InnoDB compatibility */
 
102
  flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
 
103
    if (unireg_check != TIMESTAMP_DN_FIELD)
 
104
      flags|= ON_UPDATE_NOW_FLAG;
 
105
}
 
106
 
 
107
 
 
108
/**
 
109
  Get auto-set type for TIMESTAMP field.
 
110
 
 
111
  Returns value indicating during which operations this TIMESTAMP field
 
112
  should be auto-set to current timestamp.
 
113
*/
 
114
timestamp_auto_set_type Field_timestamp::get_auto_set_type() const
 
115
{
 
116
  switch (unireg_check)
 
117
  {
 
118
  case TIMESTAMP_DN_FIELD:
 
119
    return TIMESTAMP_AUTO_SET_ON_INSERT;
 
120
  case TIMESTAMP_UN_FIELD:
 
121
    return TIMESTAMP_AUTO_SET_ON_UPDATE;
 
122
  case TIMESTAMP_OLD_FIELD:
 
123
    /*
 
124
      Although we can have several such columns in legacy tables this
 
125
      function should be called only for first of them (i.e. the one
 
126
      having auto-set property).
 
127
    */
 
128
    assert(table->timestamp_field == this);
 
129
    /* Fall-through */
 
130
  case TIMESTAMP_DNUN_FIELD:
 
131
    return TIMESTAMP_AUTO_SET_ON_BOTH;
 
132
  default:
 
133
    /*
 
134
      Normally this function should not be called for TIMESTAMPs without
 
135
      auto-set property.
 
136
    */
 
137
    assert(0);
 
138
    return TIMESTAMP_NO_AUTO_SET;
 
139
  }
 
140
}
 
141
 
 
142
 
 
143
int Field_timestamp::store(const char *from,
 
144
                           uint len,
 
145
                           CHARSET_INFO *cs __attribute__((__unused__)))
 
146
{
 
147
  MYSQL_TIME l_time;
 
148
  my_time_t tmp= 0;
 
149
  int error;
 
150
  bool have_smth_to_conv;
 
151
  bool in_dst_time_gap;
 
152
  THD *thd= table ? table->in_use : current_thd;
 
153
 
 
154
  /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
 
155
  have_smth_to_conv= (str_to_datetime(from, len, &l_time, 1, &error) >
 
156
                      MYSQL_TIMESTAMP_ERROR);
 
157
 
 
158
  if (error || !have_smth_to_conv)
 
159
  {
 
160
    error= 1;
 
161
    set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
 
162
                         from, len, MYSQL_TIMESTAMP_DATETIME, 1);
 
163
  }
 
164
 
 
165
  /* Only convert a correct date (not a zero date) */
 
166
  if (have_smth_to_conv && l_time.month)
 
167
  {
 
168
    if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
 
169
    {
 
170
      set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
171
                           ER_WARN_DATA_OUT_OF_RANGE,
 
172
                           from, len, MYSQL_TIMESTAMP_DATETIME, !error);
 
173
      error= 1;
 
174
    }
 
175
    else if (in_dst_time_gap)
 
176
    {
 
177
      set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
178
                           ER_WARN_INVALID_TIMESTAMP,
 
179
                           from, len, MYSQL_TIMESTAMP_DATETIME, !error);
 
180
      error= 1;
 
181
    }
 
182
  }
 
183
  store_timestamp(tmp);
 
184
  return error;
 
185
}
 
186
 
 
187
 
 
188
int Field_timestamp::store(double nr)
 
189
{
 
190
  int error= 0;
 
191
  if (nr < 0 || nr > 99991231235959.0)
 
192
  {
 
193
    set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
194
                         ER_WARN_DATA_OUT_OF_RANGE,
 
195
                         nr, MYSQL_TIMESTAMP_DATETIME);
 
196
    nr= 0;                                      // Avoid overflow on buff
 
197
    error= 1;
 
198
  }
 
199
  error|= Field_timestamp::store((int64_t) rint(nr), false);
 
200
  return error;
 
201
}
 
202
 
 
203
 
 
204
int Field_timestamp::store(int64_t nr,
 
205
                           bool unsigned_val __attribute__((__unused__)))
 
206
{
 
207
  MYSQL_TIME l_time;
 
208
  my_time_t timestamp= 0;
 
209
  int error;
 
210
  bool in_dst_time_gap;
 
211
  THD *thd= table ? table->in_use : current_thd;
 
212
 
 
213
  /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
 
214
  int64_t tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode &
 
215
                                                 MODE_NO_ZERO_DATE) |
 
216
                                   MODE_NO_ZERO_IN_DATE, &error);
 
217
  if (tmp == -1LL)
 
218
  {
 
219
    error= 2;
 
220
  }
 
221
 
 
222
  if (!error && tmp)
 
223
  {
 
224
    if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
 
225
    {
 
226
      set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
227
                           ER_WARN_DATA_OUT_OF_RANGE,
 
228
                           nr, MYSQL_TIMESTAMP_DATETIME, 1);
 
229
      error= 1;
 
230
    }
 
231
    if (in_dst_time_gap)
 
232
    {
 
233
      set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
234
                           ER_WARN_INVALID_TIMESTAMP,
 
235
                           nr, MYSQL_TIMESTAMP_DATETIME, 1);
 
236
      error= 1;
 
237
    }
 
238
  } else if (error)
 
239
    set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
240
                         WARN_DATA_TRUNCATED,
 
241
                         nr, MYSQL_TIMESTAMP_DATETIME, 1);
 
242
 
 
243
  store_timestamp(timestamp);
 
244
  return error;
 
245
}
 
246
 
 
247
double Field_timestamp::val_real(void)
 
248
{
 
249
  return (double) Field_timestamp::val_int();
 
250
}
 
251
 
 
252
int64_t Field_timestamp::val_int(void)
 
253
{
 
254
  uint32 temp;
 
255
  MYSQL_TIME time_tmp;
 
256
  THD  *thd= table ? table->in_use : current_thd;
 
257
 
 
258
  thd->time_zone_used= 1;
 
259
#ifdef WORDS_BIGENDIAN
 
260
  if (table && table->s->db_low_byte_first)
 
261
    temp=uint4korr(ptr);
 
262
  else
 
263
#endif
 
264
    longget(temp,ptr);
 
265
 
 
266
  if (temp == 0L)                               // No time
 
267
    return(0);                                  /* purecov: inspected */
 
268
  
 
269
  thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
 
270
  
 
271
  return time_tmp.year * 10000000000LL + time_tmp.month * 100000000LL +
 
272
         time_tmp.day * 1000000L + time_tmp.hour * 10000L +
 
273
         time_tmp.minute * 100 + time_tmp.second;
 
274
}
 
275
 
 
276
 
 
277
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
 
278
{
 
279
  uint32 temp, temp2;
 
280
  MYSQL_TIME time_tmp;
 
281
  THD *thd= table ? table->in_use : current_thd;
 
282
  char *to;
 
283
 
 
284
  val_buffer->alloc(field_length+1);
 
285
  to= (char*) val_buffer->ptr();
 
286
  val_buffer->length(field_length);
 
287
 
 
288
  thd->time_zone_used= 1;
 
289
#ifdef WORDS_BIGENDIAN
 
290
  if (table && table->s->db_low_byte_first)
 
291
    temp=uint4korr(ptr);
 
292
  else
 
293
#endif
 
294
    longget(temp,ptr);
 
295
 
 
296
  if (temp == 0L)
 
297
  {                                   /* Zero time is "000000" */
 
298
    val_ptr->set(STRING_WITH_LEN("0000-00-00 00:00:00"), &my_charset_bin);
 
299
    return val_ptr;
 
300
  }
 
301
  val_buffer->set_charset(&my_charset_bin);     // Safety
 
302
  
 
303
  thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp);
 
304
 
 
305
  temp= time_tmp.year % 100;
 
306
  if (temp < YY_PART_YEAR - 1)
 
307
  {
 
308
    *to++= '2';
 
309
    *to++= '0';
 
310
  }
 
311
  else
 
312
  {
 
313
    *to++= '1';
 
314
    *to++= '9';
 
315
  }
 
316
  temp2=temp/10; temp=temp-temp2*10;
 
317
  *to++= (char) ('0'+(char) (temp2));
 
318
  *to++= (char) ('0'+(char) (temp));
 
319
  *to++= '-';
 
320
  temp=time_tmp.month;
 
321
  temp2=temp/10; temp=temp-temp2*10;
 
322
  *to++= (char) ('0'+(char) (temp2));
 
323
  *to++= (char) ('0'+(char) (temp));
 
324
  *to++= '-';
 
325
  temp=time_tmp.day;
 
326
  temp2=temp/10; temp=temp-temp2*10;
 
327
  *to++= (char) ('0'+(char) (temp2));
 
328
  *to++= (char) ('0'+(char) (temp));
 
329
  *to++= ' ';
 
330
  temp=time_tmp.hour;
 
331
  temp2=temp/10; temp=temp-temp2*10;
 
332
  *to++= (char) ('0'+(char) (temp2));
 
333
  *to++= (char) ('0'+(char) (temp));
 
334
  *to++= ':';
 
335
  temp=time_tmp.minute;
 
336
  temp2=temp/10; temp=temp-temp2*10;
 
337
  *to++= (char) ('0'+(char) (temp2));
 
338
  *to++= (char) ('0'+(char) (temp));
 
339
  *to++= ':';
 
340
  temp=time_tmp.second;
 
341
  temp2=temp/10; temp=temp-temp2*10;
 
342
  *to++= (char) ('0'+(char) (temp2));
 
343
  *to++= (char) ('0'+(char) (temp));
 
344
  *to= 0;
 
345
  return val_buffer;
 
346
}
 
347
 
 
348
 
 
349
bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate)
 
350
{
 
351
  long temp;
 
352
  THD *thd= table ? table->in_use : current_thd;
 
353
  thd->time_zone_used= 1;
 
354
#ifdef WORDS_BIGENDIAN
 
355
  if (table && table->s->db_low_byte_first)
 
356
    temp=uint4korr(ptr);
 
357
  else
 
358
#endif
 
359
    longget(temp,ptr);
 
360
  if (temp == 0L)
 
361
  {                                   /* Zero time is "000000" */
 
362
    if (fuzzydate & TIME_NO_ZERO_DATE)
 
363
      return 1;
 
364
    bzero((char*) ltime,sizeof(*ltime));
 
365
  }
 
366
  else
 
367
  {
 
368
    thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
 
369
  }
 
370
  return 0;
 
371
}
 
372
 
 
373
bool Field_timestamp::get_time(MYSQL_TIME *ltime)
 
374
{
 
375
  return Field_timestamp::get_date(ltime,0);
 
376
}
 
377
 
 
378
 
 
379
bool Field_timestamp::send_binary(Protocol *protocol)
 
380
{
 
381
  MYSQL_TIME tm;
 
382
  Field_timestamp::get_date(&tm, 0);
 
383
  return protocol->store(&tm);
 
384
}
 
385
 
 
386
 
 
387
int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
 
388
{
 
389
  int32 a,b;
 
390
#ifdef WORDS_BIGENDIAN
 
391
  if (table && table->s->db_low_byte_first)
 
392
  {
 
393
    a=sint4korr(a_ptr);
 
394
    b=sint4korr(b_ptr);
 
395
  }
 
396
  else
 
397
#endif
 
398
  {
 
399
  longget(a,a_ptr);
 
400
  longget(b,b_ptr);
 
401
  }
 
402
  return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
 
403
}
 
404
 
 
405
 
 
406
void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused)))
 
407
{
 
408
#ifdef WORDS_BIGENDIAN
 
409
  if (!table || !table->s->db_low_byte_first)
 
410
  {
 
411
    to[0] = ptr[0];
 
412
    to[1] = ptr[1];
 
413
    to[2] = ptr[2];
 
414
    to[3] = ptr[3];
 
415
  }
 
416
  else
 
417
#endif
 
418
  {
 
419
    to[0] = ptr[3];
 
420
    to[1] = ptr[2];
 
421
    to[2] = ptr[1];
 
422
    to[3] = ptr[0];
 
423
  }
 
424
}
 
425
 
 
426
 
 
427
void Field_timestamp::sql_type(String &res) const
 
428
{
 
429
  res.set_ascii(STRING_WITH_LEN("timestamp"));
 
430
}
 
431
 
 
432
 
 
433
void Field_timestamp::set_time()
 
434
{
 
435
  THD *thd= table ? table->in_use : current_thd;
 
436
  long tmp= (long) thd->query_start();
 
437
  set_notnull();
 
438
  store_timestamp(tmp);
 
439
}
 
440