summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <timour@askmonty.org>2012-12-13 22:56:03 +0200
committerunknown <timour@askmonty.org>2012-12-13 22:56:03 +0200
commit6e8c4d696a60d34043ca2d1c272fda656955f393 (patch)
tree0bbe18bcb8ec3798793de5b8bf999e5fe061ddd5
parent69a7b04add1d8102b9f5add88ae691d8b04727bc (diff)
downloadmariadb-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.h2
-rw-r--r--sql/field.cc51
-rw-r--r--sql/field.h9
-rw-r--r--sql/sql_base.cc6
-rw-r--r--sql/sql_base.h1
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_load.cc21
-rw-r--r--sql/sql_update.cc2
-rw-r--r--sql/table.cc12
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);
}