18
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
#include <boost/lexical_cast.hpp>
22
#include <drizzled/server_includes.h>
23
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>
33
#include "drizzled/temporal.h"
39
TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to
40
2038-01-01 00:00:00 UTC stored as number of seconds since Unix
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
43
Up to one of timestamps columns in the table can be automatically
30
Up to one of timestamps columns in the table can be automatically
44
31
set on row update and/or have NOW() as default value.
45
TABLE::timestamp_field points to Field object for such timestamp with
32
TABLE::timestamp_field points to Field object for such timestamp with
46
33
auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
47
34
field, and is used by handler code which performs updates required.
49
36
Actually SQL-99 says that we should allow niladic functions (like NOW())
50
as defaults for any field. Current limitations (only NOW() and only
51
for one TIMESTAMP field) are because of restricted binary .frm format
37
as defaults for any field. Current limitations (only NOW() and only
38
for one TIMESTAMP field) are because of restricted binary .frm format
52
39
and should go away in the future.
54
41
Also because of this limitation of binary .frm format we use 5 different
55
42
unireg_check values with TIMESTAMP field to distinguish various cases of
56
43
DEFAULT or ON UPDATE values. These values are:
58
45
TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with
59
auto-set-on-update (or now() as default) in this table before, then this
60
field has NOW() as default and is updated when row changes, else it is
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
61
48
field which has 0 as default value and is not automatically updated.
62
49
TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
63
50
automatically (TIMESTAMP DEFAULT NOW())
64
TIMESTAMP_UN_FIELD - field which is set on update automatically but has not
65
NOW() as default (but it may has 0 or some other const timestamp as
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
66
53
default) (TIMESTAMP ON UPDATE NOW()).
67
TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on
54
TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on
68
55
update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW())
69
NONE - field which is not auto-set on update with some other than NOW()
56
NONE - field which is not auto-set on update with some other than NOW()
70
57
default value (TIMESTAMP DEFAULT 0).
72
Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are
73
left only for preserving ability to read old tables. Such fields replaced
74
with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is
75
because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for
76
"TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such
77
specification too but ignored default value for first timestamp, which of
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
78
65
course is non-standard.) In most cases user won't notice any change, only
79
66
exception is different behavior of old/new timestamps during ALTER TABLE.
81
69
Field_timestamp::Field_timestamp(unsigned char *ptr_arg,
83
unsigned char *null_ptr_arg,
84
unsigned char null_bit_arg,
70
uint32_t len_arg __attribute__((unused)),
71
unsigned char *null_ptr_arg, unsigned char null_bit_arg,
85
72
enum utype unireg_check_arg,
86
73
const char *field_name_arg,
88
75
const CHARSET_INFO * const cs)
90
DateTime::MAX_STRING_LENGTH - 1 /* no \0 */,
76
:Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
77
unireg_check_arg, field_name_arg, cs)
96
79
/* For 4.0 MYD and 4.0 InnoDB compatibility */
97
80
flags|= UNSIGNED_FLAG;
98
unireg_check= unireg_check_arg;
99
if (! share->getTimestampField() && unireg_check != NONE)
81
if (!share->timestamp_field && unireg_check != NONE)
101
83
/* This timestamp has auto-update */
102
share->setTimestampField(this);
103
flags|= FUNCTION_DEFAULT_FLAG;
84
share->timestamp_field= this;
85
flags|= TIMESTAMP_FLAG;
104
86
if (unireg_check != TIMESTAMP_DN_FIELD)
105
87
flags|= ON_UPDATE_NOW_FLAG;
109
92
Field_timestamp::Field_timestamp(bool maybe_null_arg,
110
93
const char *field_name_arg,
111
94
const CHARSET_INFO * const cs)
112
:Field_str((unsigned char*) NULL,
113
DateTime::MAX_STRING_LENGTH - 1 /* no \0 */,
114
maybe_null_arg ? (unsigned char*) "": 0,
95
:Field_str((unsigned char*) 0, MAX_DATETIME_WIDTH,
96
maybe_null_arg ? (unsigned char*) "": 0, 0,
97
NONE, field_name_arg, cs)
119
99
/* For 4.0 MYD and 4.0 InnoDB compatibility */
120
100
flags|= UNSIGNED_FLAG;
121
if (unireg_check != TIMESTAMP_DN_FIELD)
122
flags|= ON_UPDATE_NOW_FLAG;
101
if (unireg_check != TIMESTAMP_DN_FIELD)
102
flags|= ON_UPDATE_NOW_FLAG;
126
107
Get auto-set type for TIMESTAMP field.
159
141
int Field_timestamp::store(const char *from,
161
const CHARSET_INFO * const )
165
ASSERT_COLUMN_MARKED_FOR_WRITE;
167
if (! temporal.from_string(from, (size_t) len))
169
my_error(ER_INVALID_UNIX_TIMESTAMP_VALUE, MYF(ME_FATALERROR), from);
174
temporal.to_time_t(&tmp);
176
store_timestamp(tmp);
180
int Field_timestamp::store(double from)
182
ASSERT_COLUMN_MARKED_FOR_WRITE;
184
if (from < 0 || from > 99991231235959.0)
186
/* Convert the double to a string using stringstream */
187
std::stringstream ss;
189
ss.precision(18); /* 18 places should be fine for error display of double input. */
193
my_error(ER_INVALID_UNIX_TIMESTAMP_VALUE, MYF(ME_FATALERROR), tmp.c_str());
196
return Field_timestamp::store((int64_t) rint(from), false);
199
int Field_timestamp::store(int64_t from, bool)
201
ASSERT_COLUMN_MARKED_FOR_WRITE;
204
* Try to create a DateTime from the supplied integer. Throw an error
205
* if unable to create a valid DateTime.
208
if (! temporal.from_int64_t(from))
210
/* Convert the integer to a string using boost::lexical_cast */
211
std::string tmp(boost::lexical_cast<std::string>(from));
213
my_error(ER_INVALID_UNIX_TIMESTAMP_VALUE, MYF(ME_FATALERROR), tmp.c_str());
218
temporal.to_time_t(&tmp);
220
store_timestamp(tmp);
143
const CHARSET_INFO * const cs __attribute__((unused)))
148
bool have_smth_to_conv;
149
bool in_dst_time_gap;
150
THD *thd= table ? table->in_use : current_thd;
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);
156
if (error || !have_smth_to_conv)
159
set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
160
from, len, DRIZZLE_TIMESTAMP_DATETIME, 1);
163
/* Only convert a correct date (not a zero date) */
164
if (have_smth_to_conv && l_time.month)
166
if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
168
set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
169
ER_WARN_DATA_OUT_OF_RANGE,
170
from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
173
else if (in_dst_time_gap)
175
set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
176
ER_WARN_INVALID_TIMESTAMP,
177
from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
181
store_timestamp(tmp);
186
int Field_timestamp::store(double nr)
189
if (nr < 0 || nr > 99991231235959.0)
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
197
error|= Field_timestamp::store((int64_t) rint(nr), false);
202
int Field_timestamp::store(int64_t nr,
203
bool unsigned_val __attribute__((unused)))
206
my_time_t timestamp= 0;
208
bool in_dst_time_gap;
209
THD *thd= table ? table->in_use : current_thd;
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))
221
if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
223
set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
224
ER_WARN_DATA_OUT_OF_RANGE,
225
nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
230
set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
231
ER_WARN_INVALID_TIMESTAMP,
232
nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
236
set_datetime_warning(DRIZZLE_ERROR::WARN_LEVEL_WARN,
237
ER_WARN_DATA_TRUNCATED,
238
nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
240
store_timestamp(timestamp);
224
244
double Field_timestamp::val_real(void)
229
249
int64_t Field_timestamp::val_int(void)
233
ASSERT_COLUMN_MARKED_FOR_READ;
252
DRIZZLE_TIME time_tmp;
253
THD *thd= table ? table->in_use : current_thd;
255
thd->time_zone_used= 1;
235
256
#ifdef WORDS_BIGENDIAN
236
if (getTable() && getTable()->getShare()->db_low_byte_first)
237
temp= uint8korr(ptr);
257
if (table && table->s->db_low_byte_first)
240
int64_tget(temp, ptr);
243
(void) temporal.from_time_t((time_t) temp);
245
/* We must convert into a "timestamp-formatted integer" ... */
247
temporal.to_int64_t(&result);
263
if (temp == 0L) // No time
264
return(0); /* purecov: inspected */
266
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
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;
251
String *Field_timestamp::val_str(String *val_buffer, String *)
275
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
277
uint32_t temp, temp2;
278
DRIZZLE_TIME time_tmp;
279
THD *thd= table ? table->in_use : current_thd;
255
int to_len= field_length + 1;
257
val_buffer->alloc(to_len);
258
to= (char *) val_buffer->ptr();
282
val_buffer->alloc(field_length+1);
283
to= (char*) val_buffer->ptr();
284
val_buffer->length(field_length);
286
thd->time_zone_used= 1;
260
287
#ifdef WORDS_BIGENDIAN
261
if (getTable() && getTable()->getShare()->db_low_byte_first)
262
temp= uint8korr(ptr);
288
if (table && table->s->db_low_byte_first)
265
int64_tget(temp, ptr);
267
val_buffer->set_charset(&my_charset_bin); /* Safety */
270
(void) temporal.from_time_t((time_t) temp);
273
rlen= temporal.to_string(to, to_len);
274
assert(rlen < to_len);
276
val_buffer->length(rlen);
295
{ /* Zero time is "000000" */
296
val_ptr->set(STRING_WITH_LEN("0000-00-00 00:00:00"), &my_charset_bin);
299
val_buffer->set_charset(&my_charset_bin); // Safety
301
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp);
303
temp= time_tmp.year % 100;
304
if (temp < YY_PART_YEAR - 1)
314
temp2=temp/10; temp=temp-temp2*10;
315
*to++= (char) ('0'+(char) (temp2));
316
*to++= (char) ('0'+(char) (temp));
319
temp2=temp/10; temp=temp-temp2*10;
320
*to++= (char) ('0'+(char) (temp2));
321
*to++= (char) ('0'+(char) (temp));
324
temp2=temp/10; temp=temp-temp2*10;
325
*to++= (char) ('0'+(char) (temp2));
326
*to++= (char) ('0'+(char) (temp));
329
temp2=temp/10; temp=temp-temp2*10;
330
*to++= (char) ('0'+(char) (temp2));
331
*to++= (char) ('0'+(char) (temp));
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));
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));
277
343
return val_buffer;
280
bool Field_timestamp::get_date(DRIZZLE_TIME *ltime, uint32_t)
347
bool Field_timestamp::get_date(DRIZZLE_TIME *ltime, uint32_t fuzzydate)
350
THD *thd= table ? table->in_use : current_thd;
351
thd->time_zone_used= 1;
284
352
#ifdef WORDS_BIGENDIAN
285
if (getTable() && getTable()->getShare()->db_low_byte_first)
286
temp= uint8korr(ptr);
353
if (table && table->s->db_low_byte_first)
289
int64_tget(temp, ptr);
291
memset(ltime, 0, sizeof(*ltime));
294
(void) temporal.from_time_t((time_t) temp);
296
/* @TODO Goodbye the below code when DRIZZLE_TIME is finally gone.. */
298
ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME;
299
ltime->year= temporal.years();
300
ltime->month= temporal.months();
301
ltime->day= temporal.days();
302
ltime->hour= temporal.hours();
303
ltime->minute= temporal.minutes();
304
ltime->second= temporal.seconds();
359
{ /* Zero time is "000000" */
360
if (fuzzydate & TIME_NO_ZERO_DATE)
362
memset(ltime, 0, sizeof(*ltime));
366
thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
311
373
return Field_timestamp::get_date(ltime,0);
377
bool Field_timestamp::send_binary(Protocol *protocol)
380
Field_timestamp::get_date(&tm, 0);
381
return protocol->store(&tm);
314
385
int Field_timestamp::cmp(const unsigned char *a_ptr, const unsigned char *b_ptr)
317
388
#ifdef WORDS_BIGENDIAN
318
if (getTable() && getTable()->getShare()->db_low_byte_first)
389
if (table && table->s->db_low_byte_first)
326
int64_tget(a, a_ptr);
327
int64_tget(b, b_ptr);
329
return ((uint64_t) a < (uint64_t) b) ? -1 : ((uint64_t) a > (uint64_t) b) ? 1 : 0;
400
return ((uint32_t) a < (uint32_t) b) ? -1 : ((uint32_t) a > (uint32_t) b) ? 1 : 0;
333
void Field_timestamp::sort_string(unsigned char *to,uint32_t )
404
void Field_timestamp::sort_string(unsigned char *to,uint32_t length __attribute__((unused)))
335
406
#ifdef WORDS_BIGENDIAN
336
if (!getTable() || !getTable()->getShare()->db_low_byte_first)
407
if (!table || !table->s->db_low_byte_first)
361
425
void Field_timestamp::sql_type(String &res) const
363
427
res.set_ascii(STRING_WITH_LEN("timestamp"));
366
431
void Field_timestamp::set_time()
368
Session *session= getTable() ? getTable()->in_use : current_session;
369
time_t tmp= session->query_start();
433
THD *thd= table ? table->in_use : current_thd;
434
long tmp= (long) thd->query_start();
371
436
store_timestamp(tmp);
374
void Field_timestamp::set_default()
376
if (getTable()->timestamp_field == this &&
377
unireg_check != TIMESTAMP_UN_FIELD)
380
Field::set_default();
383
long Field_timestamp::get_timestamp(bool *null_value)
385
if ((*null_value= is_null()))
387
#ifdef WORDS_BIGENDIAN
388
if (getTable() && getTable()->getShare()->db_low_byte_first)
389
return sint8korr(ptr);
392
int64_tget(tmp, ptr);
396
void Field_timestamp::store_timestamp(int64_t timestamp)
398
#ifdef WORDS_BIGENDIAN
399
if (getTable() && getTable()->getShare()->db_low_byte_first)
401
int8store(ptr, timestamp);
405
int64_tstore(ptr, timestamp);
408
} /* namespace drizzled */