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
reserved by MySQL Cluster */
#define FIELD_FLAGS_COLUMN_FORMAT 24 /* Field column format, bit 24-25,
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 */
#define REFRESH_GRANT 1 /* Refresh grant tables */
......
......@@ -1817,6 +1817,10 @@ Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
tmp->key_start.init(0);
tmp->part_of_key.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->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
......@@ -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
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())
as defaults for any field. Current limitations (only NOW() and only
for one TIMESTAMP field) are because of restricted binary .frm format
and should go away in the future.
as defaults for any field. The current limitation (only NOW() and only
for TIMESTAMP and DATETIME fields) are because of restricted binary .frm
format and should go away in the future.
Also because of this limitation of binary .frm format we use 5 different
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,
if (unireg_check != NONE)
{
/*
This TIMESTAMP column is hereby quietly assumed to have an insert or
update default function.
We mark the flag with TIMESTAMP_FLAG to indicate to the client that
this field will be automaticly updated on insert.
*/
flags|= TIMESTAMP_FLAG;
if (unireg_check != TIMESTAMP_DN_FIELD)
......@@ -4693,19 +4691,20 @@ int Field_timestamp::set_time()
determine if there is an explicit value for each field before performing
the data change, and call this method to mark the field.
If the 'value' parameter is NULL, then the field is marked unconditionally
as having an explicit value. If 'value' is not NULL, then it can be further
analyzed to check if it really should count as a value.
For timestamp columns, the only case where a column is not marked
as been given a value are:
- 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)
{
if (value &&
((value->type() == Item::DEFAULT_VALUE_ITEM &&
if (((value->type() == Item::DEFAULT_VALUE_ITEM &&
!((Item_default_value*)value)->arg) ||
(!maybe_null() && value->is_null())))
return;
flags|= HAS_EXPLICIT_DEFAULT;
set_has_explicit_value();
}
void Field_timestamp_hires::sql_type(String &res) const
......@@ -5834,7 +5833,7 @@ void Field_datetime::sql_type(String &res) const
int Field_datetime::set_time()
{
THD *thd= current_thd;
THD *thd= table->in_use;
MYSQL_TIME now_time;
thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start());
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,
{
/* There is a function default for insertions. */
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_DN_FIELD; // only for insertions.
Field::TIMESTAMP_DN_FIELD); // only for insertions.
}
else
{
......@@ -8892,9 +8891,9 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
if (on_update_is_function)
unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates
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::NONE;
Field::NONE);
}
decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
......@@ -9800,8 +9799,8 @@ key_map Field::get_possible_keys()
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)
return;
flags|= HAS_EXPLICIT_DEFAULT;
set_has_explicit_value();
}
......@@ -342,6 +342,15 @@ public:
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);
/**
......
......@@ -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
updated by the triggers.
*/
if (!result && triggers)
if (!result && triggers && table)
{
List_iterator_fast<Item> f(fields);
Item *fld;
......@@ -8932,9 +8932,12 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
fld= (Item_field*)f++;
item_field= fld->filed_for_view_update();
if (item_field && item_field->field && table && table->vfield)
{
DBUG_ASSERT(table == item_field->field->table);
result= update_virtual_fields(thd, table, TRUE);
}
}
}
return result;
}
......@@ -9064,6 +9067,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr,
*/
if (!result && triggers && *ptr)
{
DBUG_ASSERT(table == (*ptr)->table);
if (table->vfield)
result= update_virtual_fields(thd, table, TRUE);
}
......
......@@ -304,7 +304,6 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
void mark_tmp_table_for_reuse(TABLE *table);
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_default_fields(TABLE *table);
int dynamic_column_error_message(enum_dyncol_func_result rc);
extern TABLE *unused_tables;
......
......@@ -2301,7 +2301,7 @@ end_create:
TABLE *Delayed_insert::get_local_table(THD* client_thd)
{
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_SHARE *share;
uchar *bitmap;
......@@ -2374,6 +2374,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
{
copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)*
sizeof(Field**));
if (!copy->default_field)
goto error;
dfield_ptr= copy->default_field;
}
/*
......
......@@ -836,6 +836,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
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)
field->set_time();
}
......@@ -851,8 +855,9 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
pos[length]=save_chr;
if ((pos+=length) > read_info.row_end)
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)
{
......@@ -982,15 +987,20 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(1);
}
field->set_null();
field->set_explicit_default(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)
field->set_time();
else if (field != table->next_number_field)
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
/* Do not auto-update this field. */
field->set_has_explicit_value();
}
else if (item->type() == Item::STRING_ITEM)
{
......@@ -1014,7 +1024,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
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)
{
......@@ -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)
field->set_time();
field->set_has_explicit_value();
/*
TODO: We probably should not throw warning for each field.
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,
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
/* Do not auto-update this field. */
field->set_has_explicit_value();
}
else
((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,
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char *) tag->value.ptr(), tag->value.length(), cs);
field->set_explicit_default(NULL);
field->set_has_explicit_value();
}
else
((Item_user_var_as_out_param *) item)->set_value(
......
......@@ -2158,7 +2158,7 @@ int multi_update::do_updates()
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 &&
......
......@@ -2473,9 +2473,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
/*
Process virtual and default columns, if any.
*/
if (!share->vfields)
outparam->vfield= NULL;
else
if (share->vfields)
{
if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->vfields+1)*
......@@ -2485,9 +2483,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
outparam->vfield= vfield_ptr;
}
if (!share->default_fields)
outparam->default_field= NULL;
else
if (share->default_fields)
{
if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->default_fields+1)*
......@@ -6546,7 +6542,7 @@ int TABLE::update_default_fields()
If an explicit default value for a filed overrides the default,
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)
res= dfield->evaluate_insert_default_function();
......@@ -6556,7 +6552,7 @@ int TABLE::update_default_fields()
DBUG_RETURN(res);
}
/* Unset the explicit default flag for the next record. */
dfield->flags&= ~HAS_EXPLICIT_DEFAULT;
dfield->flags&= ~HAS_EXPLICIT_VALUE;
}
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