Commit 6e8c4d69 authored by unknown's avatar unknown

MDEV-452 Add full support for auto-initialized/updated timestamp and datetime

Post-review changes according to Monty's review from 28/11/2012.
parent 69a7b04a
...@@ -128,7 +128,7 @@ enum enum_server_command ...@@ -128,7 +128,7 @@ enum enum_server_command
reserved by MySQL Cluster */ reserved by MySQL Cluster */
#define FIELD_FLAGS_COLUMN_FORMAT 24 /* Field column format, bit 24-25, #define FIELD_FLAGS_COLUMN_FORMAT 24 /* Field column format, bit 24-25,
reserved by MySQL Cluster */ reserved by MySQL Cluster */
#define HAS_EXPLICIT_DEFAULT (1 << 26) /* An INSERT/UPDATE operation supplied #define HAS_EXPLICIT_VALUE (1 << 26) /* An INSERT/UPDATE operation supplied
an explicit default value */ an explicit default value */
#define REFRESH_GRANT 1 /* Refresh grant tables */ #define REFRESH_GRANT 1 /* Refresh grant tables */
......
...@@ -1817,6 +1817,10 @@ Field *Field::new_field(MEM_ROOT *root, TABLE *new_table, ...@@ -1817,6 +1817,10 @@ Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
tmp->key_start.init(0); tmp->key_start.init(0);
tmp->part_of_key.init(0); tmp->part_of_key.init(0);
tmp->part_of_sortkey.init(0); tmp->part_of_sortkey.init(0);
/*
TODO: it is not clear why this method needs to reset unireg_check.
Try not to reset it, or explain why it needs to be reset.
*/
tmp->unireg_check= Field::NONE; tmp->unireg_check= Field::NONE;
tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
...@@ -4369,16 +4373,10 @@ void Field_double::sql_type(String &res) const ...@@ -4369,16 +4373,10 @@ void Field_double::sql_type(String &res) const
2038-01-01 00:00:00 UTC stored as number of seconds since Unix 2038-01-01 00:00:00 UTC stored as number of seconds since Unix
Epoch in UTC. Epoch in UTC.
Up to one of timestamps columns in the table can be automatically
set on row update and/or have NOW() as default value.
TABLE::timestamp_field points to Field object for such timestamp with
auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
field, and is used by handler code which performs updates required.
Actually SQL-99 says that we should allow niladic functions (like NOW()) Actually SQL-99 says that we should allow niladic functions (like NOW())
as defaults for any field. Current limitations (only NOW() and only as defaults for any field. The current limitation (only NOW() and only
for one TIMESTAMP field) are because of restricted binary .frm format for TIMESTAMP and DATETIME fields) are because of restricted binary .frm
and should go away in the future. format and should go away in the future.
Also because of this limitation of binary .frm format we use 5 different Also because of this limitation of binary .frm format we use 5 different
unireg_check values with TIMESTAMP field to distinguish various cases of unireg_check values with TIMESTAMP field to distinguish various cases of
...@@ -4422,8 +4420,8 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, ...@@ -4422,8 +4420,8 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
if (unireg_check != NONE) if (unireg_check != NONE)
{ {
/* /*
This TIMESTAMP column is hereby quietly assumed to have an insert or We mark the flag with TIMESTAMP_FLAG to indicate to the client that
update default function. this field will be automaticly updated on insert.
*/ */
flags|= TIMESTAMP_FLAG; flags|= TIMESTAMP_FLAG;
if (unireg_check != TIMESTAMP_DN_FIELD) if (unireg_check != TIMESTAMP_DN_FIELD)
...@@ -4693,19 +4691,20 @@ int Field_timestamp::set_time() ...@@ -4693,19 +4691,20 @@ int Field_timestamp::set_time()
determine if there is an explicit value for each field before performing determine if there is an explicit value for each field before performing
the data change, and call this method to mark the field. the data change, and call this method to mark the field.
If the 'value' parameter is NULL, then the field is marked unconditionally For timestamp columns, the only case where a column is not marked
as having an explicit value. If 'value' is not NULL, then it can be further as been given a value are:
analyzed to check if it really should count as a value. - It's explicitly assigned with DEFAULT
- We assign NULL to a timestamp field that is defined as NOT NULL.
This is how MySQL has worked since it's start.
*/ */
void Field_timestamp::set_explicit_default(Item *value) void Field_timestamp::set_explicit_default(Item *value)
{ {
if (value && if (((value->type() == Item::DEFAULT_VALUE_ITEM &&
((value->type() == Item::DEFAULT_VALUE_ITEM &&
!((Item_default_value*)value)->arg) || !((Item_default_value*)value)->arg) ||
(!maybe_null() && value->is_null()))) (!maybe_null() && value->is_null())))
return; return;
flags|= HAS_EXPLICIT_DEFAULT; set_has_explicit_value();
} }
void Field_timestamp_hires::sql_type(String &res) const void Field_timestamp_hires::sql_type(String &res) const
...@@ -5834,7 +5833,7 @@ void Field_datetime::sql_type(String &res) const ...@@ -5834,7 +5833,7 @@ void Field_datetime::sql_type(String &res) const
int Field_datetime::set_time() int Field_datetime::set_time()
{ {
THD *thd= current_thd; THD *thd= table->in_use;
MYSQL_TIME now_time; MYSQL_TIME now_time;
thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start()); thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start());
now_time.second_part= thd->query_start_sec_part(); now_time.second_part= thd->query_start_sec_part();
...@@ -8881,9 +8880,9 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, ...@@ -8881,9 +8880,9 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
{ {
/* There is a function default for insertions. */ /* There is a function default for insertions. */
def= NULL; def= NULL;
unireg_check= on_update_is_function ? unireg_check= (on_update_is_function ?
Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates. Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
Field::TIMESTAMP_DN_FIELD; // only for insertions. Field::TIMESTAMP_DN_FIELD); // only for insertions.
} }
else else
{ {
...@@ -8892,9 +8891,9 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, ...@@ -8892,9 +8891,9 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
if (on_update_is_function) if (on_update_is_function)
unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates
else else
unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ? unireg_check= ((fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ?
Field::NEXT_NUMBER : // Automatic increment. Field::NEXT_NUMBER : // Automatic increment.
Field::NONE; Field::NONE);
} }
decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0; decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
...@@ -9800,8 +9799,8 @@ key_map Field::get_possible_keys() ...@@ -9800,8 +9799,8 @@ key_map Field::get_possible_keys()
void Field::set_explicit_default(Item *value) void Field::set_explicit_default(Item *value)
{ {
if (value && value->type() == Item::DEFAULT_VALUE_ITEM && if (value->type() == Item::DEFAULT_VALUE_ITEM &&
!((Item_default_value*)value)->arg) !((Item_default_value*)value)->arg)
return; return;
flags|= HAS_EXPLICIT_DEFAULT; set_has_explicit_value();
} }
...@@ -342,6 +342,15 @@ class Field ...@@ -342,6 +342,15 @@ class Field
unireg_check == TIMESTAMP_DNUN_FIELD; unireg_check == TIMESTAMP_DNUN_FIELD;
} }
/*
Mark the field as having a value supplied by the client, thus it should
not be auto-updated.
*/
void set_has_explicit_value()
{
flags|= HAS_EXPLICIT_VALUE;
}
virtual void set_explicit_default(Item *value); virtual void set_explicit_default(Item *value);
/** /**
......
...@@ -8922,7 +8922,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, ...@@ -8922,7 +8922,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
Re-calculate virtual fields to cater for cases when base columns are Re-calculate virtual fields to cater for cases when base columns are
updated by the triggers. updated by the triggers.
*/ */
if (!result && triggers) if (!result && triggers && table)
{ {
List_iterator_fast<Item> f(fields); List_iterator_fast<Item> f(fields);
Item *fld; Item *fld;
...@@ -8932,7 +8932,10 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, ...@@ -8932,7 +8932,10 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
fld= (Item_field*)f++; fld= (Item_field*)f++;
item_field= fld->filed_for_view_update(); item_field= fld->filed_for_view_update();
if (item_field && item_field->field && table && table->vfield) if (item_field && item_field->field && table && table->vfield)
{
DBUG_ASSERT(table == item_field->field->table);
result= update_virtual_fields(thd, table, TRUE); result= update_virtual_fields(thd, table, TRUE);
}
} }
} }
return result; return result;
...@@ -9064,6 +9067,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, ...@@ -9064,6 +9067,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr,
*/ */
if (!result && triggers && *ptr) if (!result && triggers && *ptr)
{ {
DBUG_ASSERT(table == (*ptr)->table);
if (table->vfield) if (table->vfield)
result= update_virtual_fields(thd, table, TRUE); result= update_virtual_fields(thd, table, TRUE);
} }
......
...@@ -304,7 +304,6 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, ...@@ -304,7 +304,6 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
void mark_tmp_table_for_reuse(TABLE *table); void mark_tmp_table_for_reuse(TABLE *table);
bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists); bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists);
int update_virtual_fields(THD *thd, TABLE *table, bool ignore_stored= FALSE); int update_virtual_fields(THD *thd, TABLE *table, bool ignore_stored= FALSE);
int update_default_fields(TABLE *table);
int dynamic_column_error_message(enum_dyncol_func_result rc); int dynamic_column_error_message(enum_dyncol_func_result rc);
extern TABLE *unused_tables; extern TABLE *unused_tables;
......
...@@ -2301,7 +2301,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, ...@@ -2301,7 +2301,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
TABLE *Delayed_insert::get_local_table(THD* client_thd) TABLE *Delayed_insert::get_local_table(THD* client_thd)
{ {
my_ptrdiff_t adjust_ptrs; my_ptrdiff_t adjust_ptrs;
Field **field,**org_field, *found_next_number_field, **dfield_ptr; Field **field,**org_field, *found_next_number_field, **dfield_ptr= 0;
TABLE *copy; TABLE *copy;
TABLE_SHARE *share; TABLE_SHARE *share;
uchar *bitmap; uchar *bitmap;
...@@ -2374,6 +2374,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) ...@@ -2374,6 +2374,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
{ {
copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)* copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)*
sizeof(Field**)); sizeof(Field**));
if (!copy->default_field)
goto error;
dfield_ptr= copy->default_field; dfield_ptr= copy->default_field;
} }
/* /*
......
...@@ -836,6 +836,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -836,6 +836,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
ER_WARN_TOO_FEW_RECORDS, ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS), ER(ER_WARN_TOO_FEW_RECORDS),
thd->warning_info->current_row_for_warning()); thd->warning_info->current_row_for_warning());
/*
Timestamp fields that are NOT NULL are autoupdated if there is no
corresponding value in the data file.
*/
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP) if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
field->set_time(); field->set_time();
} }
...@@ -851,8 +855,9 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -851,8 +855,9 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
pos[length]=save_chr; pos[length]=save_chr;
if ((pos+=length) > read_info.row_end) if ((pos+=length) > read_info.row_end)
pos= read_info.row_end; /* Fills rest with space */ pos= read_info.row_end; /* Fills rest with space */
field->set_explicit_default(NULL);
} }
/* Do not auto-update this field. */
field->set_has_explicit_value();
} }
if (pos != read_info.row_end) if (pos != read_info.row_end)
{ {
...@@ -982,15 +987,20 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -982,15 +987,20 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(1); DBUG_RETURN(1);
} }
field->set_null(); field->set_null();
field->set_explicit_default(NULL);
if (!field->maybe_null()) if (!field->maybe_null())
{ {
/*
Timestamp fields that are NOT NULL are autoupdated if there is no
corresponding value in the data file.
*/
if (field->type() == MYSQL_TYPE_TIMESTAMP) if (field->type() == MYSQL_TYPE_TIMESTAMP)
field->set_time(); field->set_time();
else if (field != table->next_number_field) else if (field != table->next_number_field)
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1); ER_WARN_NULL_TO_NOTNULL, 1);
} }
/* Do not auto-update this field. */
field->set_has_explicit_value();
} }
else if (item->type() == Item::STRING_ITEM) else if (item->type() == Item::STRING_ITEM)
{ {
...@@ -1014,7 +1024,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1014,7 +1024,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field) if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE; table->auto_increment_field_not_null= TRUE;
field->store((char*) pos, length, read_info.read_charset); field->store((char*) pos, length, read_info.read_charset);
field->set_explicit_default(NULL); field->set_has_explicit_value();
} }
else if (item->type() == Item::STRING_ITEM) else if (item->type() == Item::STRING_ITEM)
{ {
...@@ -1057,6 +1067,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1057,6 +1067,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
} }
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP) if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
field->set_time(); field->set_time();
field->set_has_explicit_value();
/* /*
TODO: We probably should not throw warning for each field. TODO: We probably should not throw warning for each field.
But how about intention to always have the same number But how about intention to always have the same number
...@@ -1201,6 +1212,8 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1201,6 +1212,8 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1); ER_WARN_NULL_TO_NOTNULL, 1);
} }
/* Do not auto-update this field. */
field->set_has_explicit_value();
} }
else else
((Item_user_var_as_out_param *) item)->set_null_value(cs); ((Item_user_var_as_out_param *) item)->set_null_value(cs);
...@@ -1215,7 +1228,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -1215,7 +1228,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field) if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE; table->auto_increment_field_not_null= TRUE;
field->store((char *) tag->value.ptr(), tag->value.length(), cs); field->store((char *) tag->value.ptr(), tag->value.length(), cs);
field->set_explicit_default(NULL); field->set_has_explicit_value();
} }
else else
((Item_user_var_as_out_param *) item)->set_value( ((Item_user_var_as_out_param *) item)->set_value(
......
...@@ -2158,7 +2158,7 @@ int multi_update::do_updates() ...@@ -2158,7 +2158,7 @@ int multi_update::do_updates()
copy_field_ptr++) copy_field_ptr++)
{ {
(*copy_field_ptr->do_copy)(copy_field_ptr); (*copy_field_ptr->do_copy)(copy_field_ptr);
copy_field_ptr->to_field->set_explicit_default(NULL); copy_field_ptr->to_field->set_has_explicit_value();
} }
if (table->triggers && if (table->triggers &&
......
...@@ -2473,9 +2473,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, ...@@ -2473,9 +2473,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
/* /*
Process virtual and default columns, if any. Process virtual and default columns, if any.
*/ */
if (!share->vfields) if (share->vfields)
outparam->vfield= NULL;
else
{ {
if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root, if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->vfields+1)* (uint) ((share->vfields+1)*
...@@ -2485,9 +2483,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, ...@@ -2485,9 +2483,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
outparam->vfield= vfield_ptr; outparam->vfield= vfield_ptr;
} }
if (!share->default_fields) if (share->default_fields)
outparam->default_field= NULL;
else
{ {
if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root, if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->default_fields+1)* (uint) ((share->default_fields+1)*
...@@ -6546,7 +6542,7 @@ int TABLE::update_default_fields() ...@@ -6546,7 +6542,7 @@ int TABLE::update_default_fields()
If an explicit default value for a filed overrides the default, If an explicit default value for a filed overrides the default,
do not update the field with its automatic default value. do not update the field with its automatic default value.
*/ */
if (!(dfield->flags & HAS_EXPLICIT_DEFAULT)) if (!(dfield->flags & HAS_EXPLICIT_VALUE))
{ {
if (sql_command_flags[cmd] & CF_INSERTS_DATA) if (sql_command_flags[cmd] & CF_INSERTS_DATA)
res= dfield->evaluate_insert_default_function(); res= dfield->evaluate_insert_default_function();
...@@ -6556,7 +6552,7 @@ int TABLE::update_default_fields() ...@@ -6556,7 +6552,7 @@ int TABLE::update_default_fields()
DBUG_RETURN(res); DBUG_RETURN(res);
} }
/* Unset the explicit default flag for the next record. */ /* Unset the explicit default flag for the next record. */
dfield->flags&= ~HAS_EXPLICIT_DEFAULT; dfield->flags&= ~HAS_EXPLICIT_VALUE;
} }
DBUG_RETURN(res); DBUG_RETURN(res);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment