MDEV-452 Add full support for auto-initialized/updated timestamp and datetime
Post-review changes according to Monty's review from 28/11/2012.
This commit is contained in:
parent
69a7b04add
commit
6e8c4d696a
@ -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 */
|
||||
|
51
sql/field.cc
51
sql/field.cc
@ -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 ?
|
||||
Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
|
||||
Field::TIMESTAMP_DN_FIELD; // only for insertions.
|
||||
unireg_check= (on_update_is_function ?
|
||||
Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
|
||||
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 ?
|
||||
Field::NEXT_NUMBER : // Automatic increment.
|
||||
Field::NONE;
|
||||
unireg_check= ((fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ?
|
||||
Field::NEXT_NUMBER : // Automatic increment.
|
||||
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,7 +8932,10 @@ 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 &&
|
||||
|
12
sql/table.cc
12
sql/table.cc
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user