~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/field/timestamp.cc

Removed/replaced DBUG symbols and standardized TRUE/FALSE

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