18
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#ifdef USE_PRAGMA_IMPLEMENTATION
22
#pragma implementation // gcc: Class implementation
23
25
#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
28
TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to
29
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
32
Up to one of timestamps columns in the table can be automatically
44
33
set on row update and/or have NOW() as default value.
45
TABLE::timestamp_field points to Field object for such timestamp with
34
TABLE::timestamp_field points to Field object for such timestamp with
46
35
auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
47
36
field, and is used by handler code which performs updates required.
49
38
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
39
as defaults for any field. Current limitations (only NOW() and only
40
for one TIMESTAMP field) are because of restricted binary .frm format
52
41
and should go away in the future.
54
43
Also because of this limitation of binary .frm format we use 5 different
55
44
unireg_check values with TIMESTAMP field to distinguish various cases of
56
45
DEFAULT or ON UPDATE values. These values are:
58
47
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
48
auto-set-on-update (or now() as default) in this table before, then this
49
field has NOW() as default and is updated when row changes, else it is
61
50
field which has 0 as default value and is not automatically updated.
62
51
TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
63
52
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
53
TIMESTAMP_UN_FIELD - field which is set on update automatically but has not
54
NOW() as default (but it may has 0 or some other const timestamp as
66
55
default) (TIMESTAMP ON UPDATE NOW()).
67
TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on
56
TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on
68
57
update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW())
69
NONE - field which is not auto-set on update with some other than NOW()
58
NONE - field which is not auto-set on update with some other than NOW()
70
59
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
61
Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are
62
left only for preserving ability to read old tables. Such fields replaced
63
with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is
64
because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for
65
"TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such
66
specification too but ignored default value for first timestamp, which of
78
67
course is non-standard.) In most cases user won't notice any change, only
79
68
exception is different behavior of old/new timestamps during ALTER TABLE.
81
Field_timestamp::Field_timestamp(unsigned char *ptr_arg,
83
unsigned char *null_ptr_arg,
84
unsigned char null_bit_arg,
71
Field_timestamp::Field_timestamp(uchar *ptr_arg,
72
uint32_t len_arg __attribute__((unused)),
73
uchar *null_ptr_arg, uchar null_bit_arg,
85
74
enum utype unireg_check_arg,
86
75
const char *field_name_arg,
88
const CHARSET_INFO * const cs)
90
DateTime::MAX_STRING_LENGTH - 1 /* no \0 */,
78
:Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
79
unireg_check_arg, field_name_arg, cs)
96
81
/* For 4.0 MYD and 4.0 InnoDB compatibility */
97
82
flags|= UNSIGNED_FLAG;
98
unireg_check= unireg_check_arg;
99
if (! share->getTimestampField() && unireg_check != NONE)
83
if (!share->timestamp_field && unireg_check != NONE)
101
85
/* This timestamp has auto-update */
102
share->setTimestampField(this);
86
share->timestamp_field= this;
103
87
flags|= TIMESTAMP_FLAG;
104
88
if (unireg_check != TIMESTAMP_DN_FIELD)
105
89
flags|= ON_UPDATE_NOW_FLAG;
109
94
Field_timestamp::Field_timestamp(bool maybe_null_arg,
110
95
const char *field_name_arg,
111
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,
97
:Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
98
maybe_null_arg ? (uchar*) "": 0, 0,
99
NONE, field_name_arg, cs)
119
101
/* For 4.0 MYD and 4.0 InnoDB compatibility */
120
102
flags|= UNSIGNED_FLAG;
121
if (unireg_check != TIMESTAMP_DN_FIELD)
122
flags|= ON_UPDATE_NOW_FLAG;
103
if (unireg_check != TIMESTAMP_DN_FIELD)
104
flags|= ON_UPDATE_NOW_FLAG;
126
109
Get auto-set type for TIMESTAMP field.
159
143
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. */
190
ss << from; ss >> tmp;
192
my_error(ER_INVALID_UNIX_TIMESTAMP_VALUE, MYF(ME_FATALERROR), tmp.c_str());
195
return Field_timestamp::store((int64_t) rint(from), false);
198
int Field_timestamp::store(int64_t from, bool)
200
ASSERT_COLUMN_MARKED_FOR_WRITE;
203
* Try to create a DateTime from the supplied integer. Throw an error
204
* if unable to create a valid DateTime.
207
if (! temporal.from_int64_t(from))
209
/* Convert the integer to a string using stringstream */
210
std::stringstream ss;
212
ss << from; ss >> tmp;
214
my_error(ER_INVALID_UNIX_TIMESTAMP_VALUE, MYF(ME_FATALERROR), tmp.c_str());
219
temporal.to_time_t(&tmp);
221
store_timestamp(tmp);
145
CHARSET_INFO *cs __attribute__((unused)))
150
bool have_smth_to_conv;
151
bool in_dst_time_gap;
152
THD *thd= table ? table->in_use : current_thd;
154
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
155
have_smth_to_conv= (str_to_datetime(from, len, &l_time, 1, &error) >
156
DRIZZLE_TIMESTAMP_ERROR);
158
if (error || !have_smth_to_conv)
161
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
162
from, len, DRIZZLE_TIMESTAMP_DATETIME, 1);
165
/* Only convert a correct date (not a zero date) */
166
if (have_smth_to_conv && l_time.month)
168
if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
170
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
171
ER_WARN_DATA_OUT_OF_RANGE,
172
from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
175
else if (in_dst_time_gap)
177
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
178
ER_WARN_INVALID_TIMESTAMP,
179
from, len, DRIZZLE_TIMESTAMP_DATETIME, !error);
183
store_timestamp(tmp);
188
int Field_timestamp::store(double nr)
191
if (nr < 0 || nr > 99991231235959.0)
193
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
194
ER_WARN_DATA_OUT_OF_RANGE,
195
nr, DRIZZLE_TIMESTAMP_DATETIME);
196
nr= 0; // Avoid overflow on buff
199
error|= Field_timestamp::store((int64_t) rint(nr), false);
204
int Field_timestamp::store(int64_t nr,
205
bool unsigned_val __attribute__((unused)))
208
my_time_t timestamp= 0;
210
bool in_dst_time_gap;
211
THD *thd= table ? table->in_use : current_thd;
213
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
214
int64_t tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode &
216
MODE_NO_ZERO_IN_DATE, &error);
224
if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
226
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
227
ER_WARN_DATA_OUT_OF_RANGE,
228
nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
233
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
234
ER_WARN_INVALID_TIMESTAMP,
235
nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
239
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
240
ER_WARN_DATA_TRUNCATED,
241
nr, DRIZZLE_TIMESTAMP_DATETIME, 1);
243
store_timestamp(timestamp);
225
247
double Field_timestamp::val_real(void)
230
252
int64_t Field_timestamp::val_int(void)
234
ASSERT_COLUMN_MARKED_FOR_READ;
255
DRIZZLE_TIME time_tmp;
256
THD *thd= table ? table->in_use : current_thd;
258
thd->time_zone_used= 1;
236
259
#ifdef WORDS_BIGENDIAN
237
if (getTable() && getTable()->s->db_low_byte_first)
238
temp= uint8korr(ptr);
260
if (table && table->s->db_low_byte_first)
241
int64_tget(temp, ptr);
244
(void) temporal.from_time_t((time_t) temp);
246
/* We must convert into a "timestamp-formatted integer" ... */
248
temporal.to_int64_t(&result);
266
if (temp == 0L) // No time
267
return(0); /* purecov: inspected */
269
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
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;
252
String *Field_timestamp::val_str(String *val_buffer, String *)
277
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
279
uint32_t temp, temp2;
280
DRIZZLE_TIME time_tmp;
281
THD *thd= table ? table->in_use : current_thd;
256
int to_len= field_length + 1;
258
val_buffer->alloc(to_len);
259
to= (char *) val_buffer->ptr();
284
val_buffer->alloc(field_length+1);
285
to= (char*) val_buffer->ptr();
286
val_buffer->length(field_length);
288
thd->time_zone_used= 1;
261
289
#ifdef WORDS_BIGENDIAN
262
if (getTable() && getTable()->s->db_low_byte_first)
263
temp= uint8korr(ptr);
290
if (table && table->s->db_low_byte_first)
266
int64_tget(temp, ptr);
268
val_buffer->set_charset(&my_charset_bin); /* Safety */
271
(void) temporal.from_time_t((time_t) temp);
274
rlen= temporal.to_string(to, to_len);
275
assert(rlen < to_len);
277
val_buffer->length(rlen);
297
{ /* Zero time is "000000" */
298
val_ptr->set(STRING_WITH_LEN("0000-00-00 00:00:00"), &my_charset_bin);
301
val_buffer->set_charset(&my_charset_bin); // Safety
303
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp);
305
temp= time_tmp.year % 100;
306
if (temp < YY_PART_YEAR - 1)
316
temp2=temp/10; temp=temp-temp2*10;
317
*to++= (char) ('0'+(char) (temp2));
318
*to++= (char) ('0'+(char) (temp));
321
temp2=temp/10; temp=temp-temp2*10;
322
*to++= (char) ('0'+(char) (temp2));
323
*to++= (char) ('0'+(char) (temp));
326
temp2=temp/10; temp=temp-temp2*10;
327
*to++= (char) ('0'+(char) (temp2));
328
*to++= (char) ('0'+(char) (temp));
331
temp2=temp/10; temp=temp-temp2*10;
332
*to++= (char) ('0'+(char) (temp2));
333
*to++= (char) ('0'+(char) (temp));
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));
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));
278
345
return val_buffer;
281
bool Field_timestamp::get_date(DRIZZLE_TIME *ltime, uint32_t)
349
bool Field_timestamp::get_date(DRIZZLE_TIME *ltime, uint fuzzydate)
352
THD *thd= table ? table->in_use : current_thd;
353
thd->time_zone_used= 1;
285
354
#ifdef WORDS_BIGENDIAN
286
if (getTable() && getTable()->s->db_low_byte_first)
287
temp= uint8korr(ptr);
355
if (table && table->s->db_low_byte_first)
290
int64_tget(temp, ptr);
292
memset(ltime, 0, sizeof(*ltime));
295
(void) temporal.from_time_t((time_t) temp);
297
/* @TODO Goodbye the below code when DRIZZLE_TIME is finally gone.. */
299
ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME;
300
ltime->year= temporal.years();
301
ltime->month= temporal.months();
302
ltime->day= temporal.days();
303
ltime->hour= temporal.hours();
304
ltime->minute= temporal.minutes();
305
ltime->second= temporal.seconds();
361
{ /* Zero time is "000000" */
362
if (fuzzydate & TIME_NO_ZERO_DATE)
364
memset((char*) ltime, 0, sizeof(*ltime));
368
thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
312
375
return Field_timestamp::get_date(ltime,0);
315
int Field_timestamp::cmp(const unsigned char *a_ptr, const unsigned char *b_ptr)
379
bool Field_timestamp::send_binary(Protocol *protocol)
382
Field_timestamp::get_date(&tm, 0);
383
return protocol->store(&tm);
387
int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
318
390
#ifdef WORDS_BIGENDIAN
319
if (getTable() && getTable()->s->db_low_byte_first)
391
if (table && table->s->db_low_byte_first)
327
int64_tget(a, a_ptr);
328
int64_tget(b, b_ptr);
330
return ((uint64_t) a < (uint64_t) b) ? -1 : ((uint64_t) a > (uint64_t) b) ? 1 : 0;
402
return ((uint32_t) a < (uint32_t) b) ? -1 : ((uint32_t) a > (uint32_t) b) ? 1 : 0;
334
void Field_timestamp::sort_string(unsigned char *to,uint32_t )
406
void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused)))
336
408
#ifdef WORDS_BIGENDIAN
337
if (!getTable() || !getTable()->s->db_low_byte_first)
409
if (!table || !table->s->db_low_byte_first)
362
427
void Field_timestamp::sql_type(String &res) const
364
429
res.set_ascii(STRING_WITH_LEN("timestamp"));
367
433
void Field_timestamp::set_time()
369
Session *session= getTable() ? getTable()->in_use : current_session;
370
time_t tmp= session->query_start();
435
THD *thd= table ? table->in_use : current_thd;
436
long tmp= (long) thd->query_start();
372
438
store_timestamp(tmp);
375
void Field_timestamp::set_default()
377
if (getTable()->timestamp_field == this &&
378
unireg_check != TIMESTAMP_UN_FIELD)
381
Field::set_default();
384
long Field_timestamp::get_timestamp(bool *null_value)
386
if ((*null_value= is_null()))
388
#ifdef WORDS_BIGENDIAN
389
if (getTable() && getTable()->s->db_low_byte_first)
390
return sint8korr(ptr);
393
int64_tget(tmp, ptr);
397
void Field_timestamp::store_timestamp(int64_t timestamp)
399
#ifdef WORDS_BIGENDIAN
400
if (getTable() && getTable()->s->db_low_byte_first)
402
int8store(ptr, timestamp);
406
int64_tstore(ptr, timestamp);
409
} /* namespace drizzled */