diff options
author | unknown <timour@askmonty.org> | 2012-12-13 22:56:03 +0200 |
---|---|---|
committer | unknown <timour@askmonty.org> | 2012-12-13 22:56:03 +0200 |
commit | 6e8c4d696a60d34043ca2d1c272fda656955f393 (patch) | |
tree | 0bbe18bcb8ec3798793de5b8bf999e5fe061ddd5 | |
parent | 69a7b04add1d8102b9f5add88ae691d8b04727bc (diff) | |
download | mariadb-git-6e8c4d696a60d34043ca2d1c272fda656955f393.tar.gz |
MDEV-452 Add full support for auto-initialized/updated timestamp and datetime
Post-review changes according to Monty's review from 28/11/2012.
-rw-r--r-- | include/mysql_com.h | 2 | ||||
-rw-r--r-- | sql/field.cc | 51 | ||||
-rw-r--r-- | sql/field.h | 9 | ||||
-rw-r--r-- | sql/sql_base.cc | 6 | ||||
-rw-r--r-- | sql/sql_base.h | 1 | ||||
-rw-r--r-- | sql/sql_insert.cc | 4 | ||||
-rw-r--r-- | sql/sql_load.cc | 21 | ||||
-rw-r--r-- | sql/sql_update.cc | 2 | ||||
-rw-r--r-- | sql/table.cc | 12 |
9 files changed, 65 insertions, 43 deletions
diff --git a/include/mysql_com.h b/include/mysql_com.h index 80ce1b4b014..7fdd2c8d646 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -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 */ diff --git a/sql/field.cc b/sql/field.cc index 04f74310879..fc67b66bfd1 100644 --- a/sql/field.cc +++ b/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(); } diff --git a/sql/field.h b/sql/field.h index 7006e0bfbe8..da78a7c7674 100644 --- a/sql/field.h +++ b/sql/field.h @@ -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); /** diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 64b40e8ec4c..4baa6612c46 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -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); } diff --git a/sql/sql_base.h b/sql/sql_base.h index fbe905375bb..2d9cfa25131 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -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; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 22a75a7a99e..2b83b7afd5e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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; } /* diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 239c4b47343..4fdabef37ed 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -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( diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 4f31da92107..4f3843c989f 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -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 && diff --git a/sql/table.cc b/sql/table.cc index 09d407baad6..4b408704c63 100644 --- a/sql/table.cc +++ b/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); } |