~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/field/timestamp.cc

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

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