~drizzle-trunk/drizzle/development

173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
243.1.17 by Jay Pipes
FINAL PHASE removal of mysql_priv.h (Bye, bye my friend.)
25
#include <drizzled/server_includes.h>
214 by Brian Aker
Rename of fields (fix issue with string and decimal .h clashing).
26
#include <drizzled/field/timestamp.h>
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
27
28
/**
29
  TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to 
30
  2038-01-01 00:00:00 UTC stored as number of seconds since Unix 
31
  Epoch in UTC.
32
  
33
  Up to one of timestamps columns in the table can be automatically 
34
  set on row update and/or have NOW() as default value.
35
  TABLE::timestamp_field points to Field object for such timestamp with 
36
  auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
37
  field, and is used by handler code which performs updates required.
38
  
39
  Actually SQL-99 says that we should allow niladic functions (like NOW())
40
  as defaults for any field. Current limitations (only NOW() and only 
41
  for one TIMESTAMP field) are because of restricted binary .frm format 
42
  and should go away in the future.
43
  
44
  Also because of this limitation of binary .frm format we use 5 different
45
  unireg_check values with TIMESTAMP field to distinguish various cases of
46
  DEFAULT or ON UPDATE values. These values are:
47
  
48
  TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with
49
    auto-set-on-update (or now() as default) in this table before, then this 
50
    field has NOW() as default and is updated when row changes, else it is 
51
    field which has 0 as default value and is not automatically updated.
52
  TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
53
    automatically (TIMESTAMP DEFAULT NOW())
54
  TIMESTAMP_UN_FIELD - field which is set on update automatically but has not 
55
    NOW() as default (but it may has 0 or some other const timestamp as 
56
    default) (TIMESTAMP ON UPDATE NOW()).
57
  TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on 
58
    update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW())
59
  NONE - field which is not auto-set on update with some other than NOW() 
60
    default value (TIMESTAMP DEFAULT 0).
61
62
  Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are 
63
  left only for preserving ability to read old tables. Such fields replaced 
64
  with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is 
65
  because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for 
66
  "TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such 
67
  specification too but ignored default value for first timestamp, which of 
68
  course is non-standard.) In most cases user won't notice any change, only
69
  exception is different behavior of old/new timestamps during ALTER TABLE.
70
 */
71
72
Field_timestamp::Field_timestamp(uchar *ptr_arg,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
73
                                 uint32_t len_arg __attribute__((unused)),
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
74
                                 uchar *null_ptr_arg, uchar null_bit_arg,
75
                                 enum utype unireg_check_arg,
76
                                 const char *field_name_arg,
77
                                 TABLE_SHARE *share,
264.2.6 by Andrey Hristov
Constify the usage of CHARSET_INFO almost to the last place in the code.
78
                                 const CHARSET_INFO * const cs)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
79
  :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
80
	     unireg_check_arg, field_name_arg, cs)
81
{
82
  /* For 4.0 MYD and 4.0 InnoDB compatibility */
216 by Brian Aker
Remove completely ZEROFILL
83
  flags|= UNSIGNED_FLAG;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
84
  if (!share->timestamp_field && unireg_check != NONE)
85
  {
86
    /* This timestamp has auto-update */
87
    share->timestamp_field= this;
88
    flags|= TIMESTAMP_FLAG;
89
    if (unireg_check != TIMESTAMP_DN_FIELD)
90
      flags|= ON_UPDATE_NOW_FLAG;
91
  }
92
}
93
94
95
Field_timestamp::Field_timestamp(bool maybe_null_arg,
96
                                 const char *field_name_arg,
264.2.6 by Andrey Hristov
Constify the usage of CHARSET_INFO almost to the last place in the code.
97
                                 const CHARSET_INFO * const cs)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
98
  :Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
99
             maybe_null_arg ? (uchar*) "": 0, 0,
100
	     NONE, field_name_arg, cs)
101
{
102
  /* For 4.0 MYD and 4.0 InnoDB compatibility */
216 by Brian Aker
Remove completely ZEROFILL
103
  flags|= UNSIGNED_FLAG;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
104
    if (unireg_check != TIMESTAMP_DN_FIELD)
105
      flags|= ON_UPDATE_NOW_FLAG;
106
}
107
108
109
/**
110
  Get auto-set type for TIMESTAMP field.
111
112
  Returns value indicating during which operations this TIMESTAMP field
113
  should be auto-set to current timestamp.
114
*/
115
timestamp_auto_set_type Field_timestamp::get_auto_set_type() const
116
{
117
  switch (unireg_check)
118
  {
119
  case TIMESTAMP_DN_FIELD:
120
    return TIMESTAMP_AUTO_SET_ON_INSERT;
121
  case TIMESTAMP_UN_FIELD:
122
    return TIMESTAMP_AUTO_SET_ON_UPDATE;
123
  case TIMESTAMP_OLD_FIELD:
124
    /*
125
      Although we can have several such columns in legacy tables this
126
      function should be called only for first of them (i.e. the one
127
      having auto-set property).
128
    */
129
    assert(table->timestamp_field == this);
130
    /* Fall-through */
131
  case TIMESTAMP_DNUN_FIELD:
132
    return TIMESTAMP_AUTO_SET_ON_BOTH;
133
  default:
134
    /*
135
      Normally this function should not be called for TIMESTAMPs without
136
      auto-set property.
137
    */
138
    assert(0);
139
    return TIMESTAMP_NO_AUTO_SET;
140
  }
141
}
142
143
144
int Field_timestamp::store(const char *from,
145
                           uint len,
264.2.6 by Andrey Hristov
Constify the usage of CHARSET_INFO almost to the last place in the code.
146
                           const CHARSET_INFO * const cs __attribute__((unused)))
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
147
{
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
148
  DRIZZLE_TIME l_time;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
149
  my_time_t tmp= 0;
150
  int error;
151
  bool have_smth_to_conv;
152
  bool in_dst_time_gap;
153
  THD *thd= table ? table->in_use : current_thd;
154
155
  /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
156
  have_smth_to_conv= (str_to_datetime(from, len, &l_time, 1, &error) >
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
157
                      DRIZZLE_TIMESTAMP_ERROR);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
158
159
  if (error || !have_smth_to_conv)
160
  {
161
    error= 1;
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
162
    set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
163
                         from, len, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
164
  }
165
166
  /* Only convert a correct date (not a zero date) */
167
  if (have_smth_to_conv && l_time.month)
168
  {
169
    if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
170
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
171
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
172
                           ER_WARN_DATA_OUT_OF_RANGE,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
173
                           from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
174
      error= 1;
175
    }
176
    else if (in_dst_time_gap)
177
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
178
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
179
                           ER_WARN_INVALID_TIMESTAMP,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
180
                           from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
181
      error= 1;
182
    }
183
  }
184
  store_timestamp(tmp);
185
  return error;
186
}
187
188
189
int Field_timestamp::store(double nr)
190
{
191
  int error= 0;
192
  if (nr < 0 || nr > 99991231235959.0)
193
  {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
194
    set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
195
                         ER_WARN_DATA_OUT_OF_RANGE,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
196
                         nr, DRIZZLE_TIMESTAMP_DATETIME);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
197
    nr= 0;					// Avoid overflow on buff
198
    error= 1;
199
  }
200
  error|= Field_timestamp::store((int64_t) rint(nr), false);
201
  return error;
202
}
203
204
205
int Field_timestamp::store(int64_t nr,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
206
                           bool unsigned_val __attribute__((unused)))
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
207
{
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
208
  DRIZZLE_TIME l_time;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
209
  my_time_t timestamp= 0;
210
  int error;
211
  bool in_dst_time_gap;
212
  THD *thd= table ? table->in_use : current_thd;
213
214
  /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
215
  int64_t tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode &
361 by Brian Aker
One more mode down, two more left to go!
216
                                                 MODE_NO_ZERO_DATE), &error);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
226
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
227
                           ER_WARN_DATA_OUT_OF_RANGE,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
228
                           nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
229
      error= 1;
230
    }
231
    if (in_dst_time_gap)
232
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
233
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
234
                           ER_WARN_INVALID_TIMESTAMP,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
235
                           nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
236
      error= 1;
237
    }
238
  } else if (error)
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
239
    set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
212.5.42 by Monty Taylor
Ding dong include is dead.
240
                         ER_WARN_DATA_TRUNCATED,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
241
                         nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
{
205 by Brian Aker
uint32 -> uin32_t
254
  uint32_t temp;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
255
  DRIZZLE_TIME time_tmp;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
{
205 by Brian Aker
uint32 -> uin32_t
279
  uint32_t temp, temp2;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
280
  DRIZZLE_TIME time_tmp;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
349
bool Field_timestamp::get_date(DRIZZLE_TIME *ltime, uint fuzzydate)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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;
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
364
    memset(ltime, 0, sizeof(*ltime));
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
365
  }
366
  else
367
  {
368
    thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
369
  }
370
  return 0;
371
}
372
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
373
bool Field_timestamp::get_time(DRIZZLE_TIME *ltime)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
374
{
375
  return Field_timestamp::get_date(ltime,0);
376
}
377
378
379
bool Field_timestamp::send_binary(Protocol *protocol)
380
{
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
381
  DRIZZLE_TIME tm;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
{
205 by Brian Aker
uint32 -> uin32_t
389
  int32_t a,b;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
  }
205 by Brian Aker
uint32 -> uin32_t
402
  return ((uint32_t) a < (uint32_t) b) ? -1 : ((uint32_t) a > (uint32_t) b) ? 1 : 0;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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