~drizzle-trunk/drizzle/development

466 by Monty Taylor
Fixed modelines... these files are c++.
1
/* - mode: c++ c-basic-offset: 2; indent-tabs-mode: nil; -*-
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
243.1.17 by Jay Pipes
FINAL PHASE removal of mysql_priv.h (Bye, bye my friend.)
22
#include <drizzled/server_includes.h>
214 by Brian Aker
Rename of fields (fix issue with string and decimal .h clashing).
23
#include <drizzled/field/timestamp.h>
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
481 by Brian Aker
Remove all of uchar.
69
Field_timestamp::Field_timestamp(unsigned char *ptr_arg,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
70
                                 uint32_t len_arg __attribute__((unused)),
481 by Brian Aker
Remove all of uchar.
71
                                 unsigned char *null_ptr_arg, unsigned char null_bit_arg,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
72
                                 enum utype unireg_check_arg,
73
                                 const char *field_name_arg,
74
                                 TABLE_SHARE *share,
264.2.6 by Andrey Hristov
Constify the usage of CHARSET_INFO almost to the last place in the code.
75
                                 const CHARSET_INFO * const cs)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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 */
216 by Brian Aker
Remove completely ZEROFILL
80
  flags|= UNSIGNED_FLAG;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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,
264.2.6 by Andrey Hristov
Constify the usage of CHARSET_INFO almost to the last place in the code.
94
                                 const CHARSET_INFO * const cs)
481 by Brian Aker
Remove all of uchar.
95
  :Field_str((unsigned char*) 0, MAX_DATETIME_WIDTH,
96
             maybe_null_arg ? (unsigned char*) "": 0, 0,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
97
	     NONE, field_name_arg, cs)
98
{
99
  /* For 4.0 MYD and 4.0 InnoDB compatibility */
216 by Brian Aker
Remove completely ZEROFILL
100
  flags|= UNSIGNED_FLAG;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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,
482 by Brian Aker
Remove uint.
142
                           uint32_t len,
264.2.6 by Andrey Hristov
Constify the usage of CHARSET_INFO almost to the last place in the code.
143
                           const CHARSET_INFO * const cs __attribute__((unused)))
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
144
{
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
145
  DRIZZLE_TIME l_time;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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) >
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
154
                      DRIZZLE_TIMESTAMP_ERROR);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
155
156
  if (error || !have_smth_to_conv)
157
  {
158
    error= 1;
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
159
    set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
160
                         from, len, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
168
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
169
                           ER_WARN_DATA_OUT_OF_RANGE,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
170
                           from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
171
      error= 1;
172
    }
173
    else if (in_dst_time_gap)
174
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
175
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
176
                           ER_WARN_INVALID_TIMESTAMP,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
177
                           from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
  {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
191
    set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
192
                         ER_WARN_DATA_OUT_OF_RANGE,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
193
                         nr, DRIZZLE_TIMESTAMP_DATETIME);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
203
                           bool unsigned_val __attribute__((unused)))
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
204
{
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
205
  DRIZZLE_TIME l_time;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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 &
361 by Brian Aker
One more mode down, two more left to go!
213
                                                 MODE_NO_ZERO_DATE), &error);
422 by Monty
Various int64 constant fixes.
214
  if (tmp == INT64_C(-1))
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
223
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
224
                           ER_WARN_DATA_OUT_OF_RANGE,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
225
                           nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
226
      error= 1;
227
    }
228
    if (in_dst_time_gap)
229
    {
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
230
      set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
231
                           ER_WARN_INVALID_TIMESTAMP,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
232
                           nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
233
      error= 1;
234
    }
235
  } else if (error)
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
236
    set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
212.5.42 by Monty Taylor
Ding dong include is dead.
237
                         ER_WARN_DATA_TRUNCATED,
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
238
                         nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
{
205 by Brian Aker
uint32 -> uin32_t
251
  uint32_t temp;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
252
  DRIZZLE_TIME time_tmp;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
  
422 by Monty
Various int64 constant fixes.
268
  return time_tmp.year * INT64_C(10000000000) +
269
         time_tmp.month * INT64_C(100000000) +
270
         time_tmp.day * 1000000 + time_tmp.hour * 10000 +
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
271
         time_tmp.minute * 100 + time_tmp.second;
272
}
273
274
275
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
276
{
205 by Brian Aker
uint32 -> uin32_t
277
  uint32_t temp, temp2;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
278
  DRIZZLE_TIME time_tmp;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
482 by Brian Aker
Remove uint.
347
bool Field_timestamp::get_date(DRIZZLE_TIME *ltime, uint32_t fuzzydate)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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;
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
362
    memset(ltime, 0, sizeof(*ltime));
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
363
  }
364
  else
365
  {
366
    thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
367
  }
368
  return 0;
369
}
370
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
371
bool Field_timestamp::get_time(DRIZZLE_TIME *ltime)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
372
{
373
  return Field_timestamp::get_date(ltime,0);
374
}
375
376
377
bool Field_timestamp::send_binary(Protocol *protocol)
378
{
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
379
  DRIZZLE_TIME tm;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
380
  Field_timestamp::get_date(&tm, 0);
381
  return protocol->store(&tm);
382
}
383
384
481 by Brian Aker
Remove all of uchar.
385
int Field_timestamp::cmp(const unsigned char *a_ptr, const unsigned char *b_ptr)
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
386
{
205 by Brian Aker
uint32 -> uin32_t
387
  int32_t a,b;
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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
  }
205 by Brian Aker
uint32 -> uin32_t
400
  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/
401
}
402
403
482 by Brian Aker
Remove uint.
404
void Field_timestamp::sort_string(unsigned char *to,uint32_t length __attribute__((unused)))
173.1.6 by Toru Maesaka
ripped out TIMESTAMP and move to field/
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