1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-07 00:04:31 +03:00

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

Generalized support for auto-updated and/or auto-initialized timestamp
and datetime columns. This patch is a reimplementation of MySQL's
"WL#5874: CURRENT_TIMESTAMP as DEFAULT for DATETIME columns". In order to
ease future merges, this implementation reused few function and variable
names from MySQL's patch, however the implementation is quite different.

TODO:
The only unresolved problem in this patch is the semantics of LOAD DATA for
TIMESTAMP and DATETIME columns in the cases when there are missing or NULL
columns. I couldn't fully comprehend the logic behind MySQL's behavior and
its relationship with their own documentation, so I left the results to be
more consistent with all other LOAD cases.

The problematic test cases can be seen by running the test file function_defaults,
and observing the test case differences. Those were left on purpose for discussion.
This commit is contained in:
unknown
2012-10-17 15:43:56 +03:00
parent 620d14f8c3
commit bc4a456758
61 changed files with 5375 additions and 540 deletions

View File

@@ -191,11 +191,6 @@ error:
different table maps, like on select ... insert
map Store here table map for used fields
NOTE
Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type
or leaves it as is, depending on if timestamp should be updated or
not.
RETURN
0 OK
-1 Error
@@ -234,8 +229,6 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (check_grant_all_columns(thd, INSERT_ACL, &field_it))
return -1;
#endif
clear_timestamp_auto_bits(table->timestamp_field_type,
TIMESTAMP_AUTO_SET_ON_INSERT);
/*
No fields are provided so all fields must be provided in the values.
Thus we set all bits in the write set.
@@ -295,18 +288,8 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name);
return -1;
}
if (table->timestamp_field) // Don't automaticly set timestamp if used
{
if (bitmap_is_set(table->write_set,
table->timestamp_field->field_index))
clear_timestamp_auto_bits(table->timestamp_field_type,
TIMESTAMP_AUTO_SET_ON_INSERT);
else
{
bitmap_set_bit(table->write_set,
table->timestamp_field->field_index);
}
}
if (table->default_field)
table->mark_default_fields_for_write();
}
/* Mark virtual columns used in the insert statement */
if (table->vfield)
@@ -339,9 +322,6 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
update_fields The update fields.
NOTE
If the update fields include the timestamp field,
remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
If the update fields include an autoinc field, set the
table->next_number_field_updated flag.
@@ -355,21 +335,9 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
List<Item> &update_values, table_map *map)
{
TABLE *table= insert_table_list->table;
my_bool timestamp_mark;
my_bool autoinc_mark;
LINT_INIT(timestamp_mark);
LINT_INIT(autoinc_mark);
if (table->timestamp_field)
{
/*
Unmark the timestamp field so that we can check if this is modified
by update_fields
*/
timestamp_mark= bitmap_test_and_clear(table->write_set,
table->timestamp_field->field_index);
}
table->next_number_field_updated= FALSE;
if (table->found_next_number_field)
@@ -393,17 +361,8 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
insert_table_list, map, false))
return -1;
if (table->timestamp_field)
{
/* Don't set timestamp column if this is modified. */
if (bitmap_is_set(table->write_set,
table->timestamp_field->field_index))
clear_timestamp_auto_bits(table->timestamp_field_type,
TIMESTAMP_AUTO_SET_ON_UPDATE);
if (timestamp_mark)
bitmap_set_bit(table->write_set,
table->timestamp_field->field_index);
}
if (table->default_field)
table->mark_default_fields_for_write();
if (table->found_next_number_field)
{
@@ -709,7 +668,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
int error, res;
bool transactional_table, joins_freed= FALSE;
bool changed;
bool was_insert_delayed= (table_list->lock_type == TL_WRITE_DELAYED);
const bool was_insert_delayed= (table_list->lock_type == TL_WRITE_DELAYED);
bool using_bulk_insert= 0;
uint value_count;
ulong counter = 1;
@@ -913,8 +872,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (fields.elements || !value_count)
{
restore_record(table,s->default_values); // Get empty record
if (fill_record_n_invoke_before_triggers(thd, fields, *values, 0,
table->triggers,
if (fill_record_n_invoke_before_triggers(thd, table, fields, *values, 0,
TRG_EVENT_INSERT))
{
if (values_list.elements != 1 && ! thd->is_error())
@@ -958,8 +916,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
share->default_values[share->null_bytes - 1];
}
}
if (fill_record_n_invoke_before_triggers(thd, table->field, *values, 0,
table->triggers,
if (fill_record_n_invoke_before_triggers(thd, table, table->field, *values, 0,
TRG_EVENT_INSERT))
{
if (values_list.elements != 1 && ! thd->is_error())
@@ -971,6 +928,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
break;
}
}
if (table->default_field && table->update_default_fields())
{
error= 1;
break;
}
if ((res= table_list->view_check_option(thd,
(values_list.elements == 1 ?
@@ -987,7 +949,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (lock_type == TL_WRITE_DELAYED)
{
LEX_STRING const st_query = { query, thd->query_length() };
DEBUG_SYNC(thd, "before_write_delayed");
error=write_delayed(thd, table, duplic, st_query, ignore, log_on);
DEBUG_SYNC(thd, "after_write_delayed");
query=0;
}
else
@@ -1696,13 +1660,32 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
restore_record(table,record[1]);
DBUG_ASSERT(info->update_fields->elements ==
info->update_values->elements);
if (fill_record_n_invoke_before_triggers(thd, *info->update_fields,
if (fill_record_n_invoke_before_triggers(thd, table, *info->update_fields,
*info->update_values,
info->ignore,
table->triggers,
TRG_EVENT_UPDATE))
goto before_trg_err;
bool different_records= (!records_are_comparable(table) ||
compare_record(table));
/*
Default fields must be updated before checking view updateability.
This branch of INSERT is executed only when a UNIQUE key was violated
with the ON DUPLICATE KEY UPDATE option. In this case the INSERT
operation is transformed to an UPDATE, and the default fields must
be updated as this is an UPDATE.
*/
if (different_records && table->default_field)
{
bool res;
enum_sql_command cmd= thd->lex->sql_command;
thd->lex->sql_command= SQLCOM_UPDATE;
res= table->update_default_fields();
thd->lex->sql_command= cmd;
if (res)
goto err;
}
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
if (info->view &&
(res= info->view->view_check_option(current_thd, info->ignore)) ==
@@ -1713,7 +1696,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->file->restore_auto_increment(prev_insert_id);
info->touched++;
if (!records_are_comparable(table) || compare_record(table))
if (different_records)
{
if ((error=table->file->ha_update_row(table->record[1],
table->record[0])) &&
@@ -1784,8 +1767,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
*/
if (last_uniq_key(table,key_nr) &&
!table->file->referenced_by_foreign_key() &&
(table->timestamp_field_type == TIMESTAMP_NO_AUTO_SET ||
table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH) &&
(!table->triggers || !table->triggers->has_delete_triggers()))
{
if ((error=table->file->ha_update_row(table->record[1],
@@ -1948,7 +1929,6 @@ public:
ulonglong forced_insert_id;
ulong auto_increment_increment;
ulong auto_increment_offset;
timestamp_auto_set_type timestamp_field_type;
LEX_STRING query;
Time_zone *time_zone;
@@ -2321,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;
Field **field,**org_field, *found_next_number_field, **dfield_ptr;
TABLE *copy;
TABLE_SHARE *share;
uchar *bitmap;
@@ -2390,6 +2370,12 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
bitmap= (uchar*) (field + share->fields + 1);
copy->record[0]= (bitmap + share->column_bitmap_size*3);
memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
if (share->default_fields)
{
copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)*
sizeof(Field**));
dfield_ptr= copy->default_field;
}
/*
Make a copy of all fields.
The copied fields need to point into the copied record. This is done
@@ -2407,18 +2393,19 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
(*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0]
if (*org_field == found_next_number_field)
(*field)->table->found_next_number_field= *field;
if (share->default_fields &&
((*org_field)->has_insert_default_function() ||
(*org_field)->has_update_default_function()))
{
/* Put the newly copied field into the set of default fields. */
*dfield_ptr= *field;
(*dfield_ptr)->unireg_check= (*org_field)->unireg_check;
dfield_ptr++;
}
}
*field=0;
/* Adjust timestamp */
if (table->timestamp_field)
{
/* Restore offset as this may have been reset in handle_inserts */
copy->timestamp_field=
(Field_timestamp*) copy->field[share->timestamp_field_offset];
copy->timestamp_field->unireg_check= table->timestamp_field->unireg_check;
copy->timestamp_field_type= copy->timestamp_field->get_auto_set_type();
}
if (share->default_fields)
*dfield_ptr= NULL;
/* Adjust in_use for pointing to client thread */
copy->in_use= client_thd;
@@ -2508,7 +2495,6 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
row->first_successful_insert_id_in_prev_stmt=
thd->first_successful_insert_id_in_prev_stmt;
row->timestamp_field_type= table->timestamp_field_type;
/* Add session variable timezone
Time_zone object will not be freed even the thread is ended.
@@ -3051,7 +3037,6 @@ bool Delayed_insert::handle_inserts(void)
row->first_successful_insert_id_in_prev_stmt;
thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt=
row->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
table->timestamp_field_type= row->timestamp_field_type;
table->auto_increment_field_not_null= row->auto_increment_field_not_null;
/* Copy the session variables. */
@@ -3527,6 +3512,8 @@ int select_insert::send_data(List<Item> &values)
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
if (table->default_field && table->update_default_fields())
DBUG_RETURN(1);
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
if (thd->is_error())
{
@@ -3586,11 +3573,11 @@ int select_insert::send_data(List<Item> &values)
void select_insert::store_values(List<Item> &values)
{
if (fields->elements)
fill_record_n_invoke_before_triggers(thd, *fields, values, 1,
table->triggers, TRG_EVENT_INSERT);
fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1,
TRG_EVENT_INSERT);
else
fill_record_n_invoke_before_triggers(thd, table->field, values, 1,
table->triggers, TRG_EVENT_INSERT);
fill_record_n_invoke_before_triggers(thd, table, table->field, values, 1,
TRG_EVENT_INSERT);
}
void select_insert::send_error(uint errcode,const char *err)
@@ -3808,7 +3795,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
DBUG_ENTER("create_table_from_items");
tmp_table.alias= 0;
tmp_table.timestamp_field= 0;
tmp_table.s= &share;
init_tmp_table_share(thd, &share, "", 0, "", "");
@@ -3817,6 +3803,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
tmp_table.null_row= 0;
tmp_table.maybe_null= 0;
promote_first_timestamp_column(&alter_info->create_list);
while ((item=it++))
{
Create_field *cr_field;
@@ -4056,8 +4044,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
for (Field **f= field ; *f ; f++)
bitmap_set_bit(table->write_set, (*f)->field_index);
/* Don't set timestamp if used */
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
table->next_number_field=table->found_next_number_field;
restore_record(table,s->default_values); // Get empty record
@@ -4133,8 +4119,8 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
void select_create::store_values(List<Item> &values)
{
fill_record_n_invoke_before_triggers(thd, field, values, 1,
table->triggers, TRG_EVENT_INSERT);
fill_record_n_invoke_before_triggers(thd, table, field, values, 1,
TRG_EVENT_INSERT);
}