diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/event_db_repository.cc | 5 | ||||
-rw-r--r-- | sql/field.cc | 157 | ||||
-rw-r--r-- | sql/field.h | 72 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 7 | ||||
-rw-r--r-- | sql/ha_partition.cc | 22 | ||||
-rw-r--r-- | sql/log_event.cc | 19 | ||||
-rw-r--r-- | sql/log_event_old.cc | 36 | ||||
-rw-r--r-- | sql/sp.cc | 1 | ||||
-rw-r--r-- | sql/sp_head.cc | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 47 | ||||
-rw-r--r-- | sql/sql_base.h | 11 | ||||
-rw-r--r-- | sql/sql_class.h | 10 | ||||
-rw-r--r-- | sql/sql_expression_cache.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 142 | ||||
-rw-r--r-- | sql/sql_load.cc | 49 | ||||
-rw-r--r-- | sql/sql_parse.cc | 25 | ||||
-rw-r--r-- | sql/sql_partition.cc | 3 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 61 | ||||
-rw-r--r-- | sql/sql_string.cc | 18 | ||||
-rw-r--r-- | sql/sql_string.h | 1 | ||||
-rw-r--r-- | sql/sql_table.cc | 89 | ||||
-rw-r--r-- | sql/sql_table.h | 4 | ||||
-rw-r--r-- | sql/sql_union.cc | 2 | ||||
-rw-r--r-- | sql/sql_update.cc | 46 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 8 | ||||
-rw-r--r-- | sql/table.cc | 129 | ||||
-rw-r--r-- | sql/table.h | 41 |
28 files changed, 557 insertions, 456 deletions
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 21e3634f6c7..fb0887e25d1 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -829,9 +829,6 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, (int) table->field[ET_FIELD_ON_COMPLETION]->val_int())) goto end; - /* Don't update create on row update. */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - /* mysql_event_fill_row() calls my_error() in case of error so no need to handle it here @@ -1133,8 +1130,6 @@ update_timing_fields_for_event(THD *thd, goto end; store_record(table, record[1]); - /* Don't update create on row update. */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed); fields[ET_FIELD_LAST_EXECUTED]->set_notnull(); diff --git a/sql/field.cc b/sql/field.cc index a3d3d951887..9a54dbf6c49 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4419,10 +4419,12 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, { /* For 4.0 MYD and 4.0 InnoDB compatibility */ flags|= UNSIGNED_FLAG | BINARY_FLAG; - if (unireg_check != NONE && !share->timestamp_field) + if (unireg_check != NONE) { - /* This timestamp has auto-update */ - share->timestamp_field= this; + /* + This TIMESTAMP column is hereby quietly assumed to have an insert or + update default function. + */ flags|= TIMESTAMP_FLAG; if (unireg_check != TIMESTAMP_DN_FIELD) flags|= ON_UPDATE_NOW_FLAG; @@ -4430,40 +4432,6 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, } -/** - Get auto-set type for TIMESTAMP field. - - Returns value indicating during which operations this TIMESTAMP field - should be auto-set to current timestamp. -*/ -timestamp_auto_set_type Field_timestamp::get_auto_set_type() const -{ - switch (unireg_check) - { - case TIMESTAMP_DN_FIELD: - return TIMESTAMP_AUTO_SET_ON_INSERT; - case TIMESTAMP_UN_FIELD: - return TIMESTAMP_AUTO_SET_ON_UPDATE; - case TIMESTAMP_OLD_FIELD: - /* - Although we can have several such columns in legacy tables this - function should be called only for first of them (i.e. the one - having auto-set property). - */ - DBUG_ASSERT(table->timestamp_field == this); - /* Fall-through */ - case TIMESTAMP_DNUN_FIELD: - return TIMESTAMP_AUTO_SET_ON_BOTH; - default: - /* - Normally this function should not be called for TIMESTAMPs without - auto-set property. - */ - DBUG_ASSERT(0); - return TIMESTAMP_NO_AUTO_SET; - } -} - my_time_t Field_timestamp::get_timestamp(ulong *sec_part) const { ASSERT_COLUMN_MARKED_FOR_READ; @@ -4713,6 +4681,16 @@ int Field_timestamp::set_time() return 0; } +void Field_timestamp::set_explicit_default(Item *value) +{ + if (value && + ((value->type() == Item::DEFAULT_VALUE_ITEM && + !((Item_default_value*)value)->arg) || + (!maybe_null() && value->is_null()))) + return; + flags|= HAS_EXPLICIT_DEFAULT; +} + void Field_timestamp_hires::sql_type(String &res) const { CHARSET_INFO *cs=res.charset(); @@ -5836,6 +5814,20 @@ void Field_datetime::sql_type(String &res) const res.set_ascii(STRING_WITH_LEN("datetime")); } + +int Field_datetime::set_time() +{ + THD *thd= current_thd; + 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(); + set_notnull(); + store_TIME(&now_time); + thd->time_zone_used= 1; + return 0; +} + + void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime) { ulonglong packed= sec_part_shift(pack_time(ltime), dec); @@ -8857,16 +8849,37 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, { uint sign_len, allowed_type_modifier= 0; ulong max_field_charlength= MAX_FIELD_CHARLENGTH; + const bool on_update_is_function= + (fld_on_update_value != NULL && + fld_on_update_value->type() == Item::FUNC_ITEM); DBUG_ENTER("Create_field::init()"); field= 0; field_name= fld_name; - def= fld_default_value; flags= fld_type_modifier; option_list= create_opt; - unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG ? - Field::NEXT_NUMBER : Field::NONE); + + if (fld_default_value != NULL && fld_default_value->type() == Item::FUNC_ITEM) + { + /* We have 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. + } + else + { + /* No function default for insertions. Either NULL or a constant. */ + def= fld_default_value; + 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; + } + decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0; if (decimals >= NOT_FIXED_DEC) { @@ -9089,44 +9102,6 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); flags|= UNSIGNED_FLAG; - - if (fld_default_value) - { - /* Grammar allows only NOW() value for ON UPDATE clause */ - if (fld_default_value->type() == Item::FUNC_ITEM && - ((Item_func*)fld_default_value)->functype() == Item_func::NOW_FUNC) - { - unireg_check= (fld_on_update_value ? Field::TIMESTAMP_DNUN_FIELD: - Field::TIMESTAMP_DN_FIELD); - /* - We don't need default value any longer moreover it is dangerous. - Everything handled by unireg_check further. - */ - def= 0; - } - else - unireg_check= (fld_on_update_value ? Field::TIMESTAMP_UN_FIELD: - Field::NONE); - } - else - { - /* - If we have default TIMESTAMP NOT NULL column without explicit DEFAULT - or ON UPDATE values then for the sake of compatiblity we should treat - this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't - have another TIMESTAMP column with auto-set option before this one) - or DEFAULT 0 (in other cases). - So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will - replace this value by TIMESTAMP_DNUN_FIELD or NONE later when - information about all TIMESTAMP fields in table will be availiable. - - If we have TIMESTAMP NULL column without explicit DEFAULT value - we treat it as having DEFAULT NULL attribute. - */ - unireg_check= (fld_on_update_value ? Field::TIMESTAMP_UN_FIELD : - (flags & NOT_NULL_FLAG ? Field::TIMESTAMP_OLD_FIELD : - Field::NONE)); - } break; case MYSQL_TYPE_DATE: /* We don't support creation of MYSQL_TYPE_DATE anymore */ @@ -9592,11 +9567,18 @@ Create_field::Create_field(Field *old_field,Field *orig_field) def=0; char_length= length; - if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && - old_field->ptr && orig_field && - (sql_type != MYSQL_TYPE_TIMESTAMP || /* set def only if */ - old_field->table->timestamp_field != old_field || /* timestamp field */ - unireg_check == Field::TIMESTAMP_UN_FIELD)) /* has default val */ + /* + Copy the default value from the column object orig_field, if: + 1) The column has a constant default value. + 2) The column type is not a BLOB type. + 3) The original column (old_field) was properly initialized with a record + buffer pointer. + 4) The original column doesn't have a default function to auto-initialize + the column on INSERT + */ + if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && // 1) 2) + old_field->ptr && orig_field && // 3) + !old_field->has_insert_default_function()) // 4) { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff), charset); @@ -9780,3 +9762,12 @@ key_map Field::get_possible_keys() return (table->pos_in_table_list->is_materialized_derived() ? part_of_key : key_start); } + + +void Field::set_explicit_default(Item *value) +{ + if (value && value->type() == Item::DEFAULT_VALUE_ITEM && + !((Item_default_value*)value)->arg) + return; + flags|= HAS_EXPLICIT_DEFAULT; +} diff --git a/sql/field.h b/sql/field.h index f22bab0409d..7006e0bfbe8 100644 --- a/sql/field.h +++ b/sql/field.h @@ -329,6 +329,37 @@ public: *null_ptr= ((*null_ptr & (uchar) ~null_bit) | (null_ptr[l_offset] & null_bit)); } + + bool has_insert_default_function() const + { + return unireg_check == TIMESTAMP_DN_FIELD || + unireg_check == TIMESTAMP_DNUN_FIELD; + } + + bool has_update_default_function() const + { + return unireg_check == TIMESTAMP_UN_FIELD || + unireg_check == TIMESTAMP_DNUN_FIELD; + } + + virtual void set_explicit_default(Item *value); + + /** + Evaluates the @c INSERT default function and stores the result in the + field. If no such function exists for the column, or the function is not + valid for the column's data type, invoking this function has no effect. + */ + virtual int evaluate_insert_default_function() { return 0; } + + + /** + Evaluates the @c UPDATE default function, if one exists, and stores the + result in the record buffer. If no such function exists for the column, + or the function is not valid for the column's data type, invoking this + function has no effect. + */ + virtual int evaluate_update_default_function() { return 0; } + virtual bool binary() const { return 1; } virtual bool zero_pack() const { return 1; } virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } @@ -1239,12 +1270,26 @@ public: virtual int set_time(); virtual void set_default() { - if (table->timestamp_field == this && - unireg_check != TIMESTAMP_UN_FIELD) + if (has_insert_default_function()) set_time(); else Field::set_default(); } + virtual void set_explicit_default(Item *value); + virtual int evaluate_insert_default_function() + { + int res= 0; + if (has_insert_default_function()) + res= set_time(); + return res; + } + virtual int evaluate_update_default_function() + { + int res= 0; + if (has_update_default_function()) + res= set_time(); + return res; + } /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ virtual my_time_t get_timestamp(ulong *sec_part) const; virtual void store_TIME(my_time_t timestamp, ulong sec_part) @@ -1252,7 +1297,6 @@ public: int4store(ptr,timestamp); } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - timestamp_auto_set_type get_auto_set_type() const; uchar *pack(uchar *to, const uchar *from, uint max_length __attribute__((unused))) { @@ -1503,6 +1547,28 @@ public: void sql_type(String &str) const; bool zero_pack() const { return 1; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + virtual int set_time(); + virtual void set_default() + { + if (has_insert_default_function()) + set_time(); + else + Field::set_default(); + } + virtual int evaluate_insert_default_function() + { + int res= 0; + if (has_insert_default_function()) + res= set_time(); + return res; + } + virtual int evaluate_update_default_function() + { + int res= 0; + if (has_update_default_function()) + res= set_time(); + return res; + } uchar *pack(uchar* to, const uchar *from, uint max_length __attribute__((unused))) { diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 2878f25ed14..6ba4fe46441 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2906,8 +2906,6 @@ int ha_ndbcluster::write_row(uchar *record) } ha_statistic_increment(&SSV::ha_write_count); - if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) - table->timestamp_field->set_time(); if (!(op= trans->getNdbOperation(m_table))) ERR_RETURN(trans->getNdbError()); @@ -3146,11 +3144,6 @@ int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data) } ha_statistic_increment(&SSV::ha_update_count); - if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) - { - table->timestamp_field->set_time(); - bitmap_set_bit(table->write_set, table->timestamp_field->field_index); - } if (m_use_partition_function && (error= get_parts_for_update(old_data, new_data, table->record[0], diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b3f97d35033..5f58247a79d 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3451,8 +3451,8 @@ void ha_partition::try_semi_consistent_read(bool yes) ADDITIONAL INFO: - We have to set timestamp fields and auto_increment fields, because those - may be used in determining which partition the row should be written to. + We have to set auto_increment fields, because those may be used in + determining which partition the row should be written to. */ int ha_partition::write_row(uchar * buf) @@ -3463,7 +3463,6 @@ int ha_partition::write_row(uchar * buf) bool have_auto_increment= table->next_number_field && buf == table->record[0]; my_bitmap_map *old_map; THD *thd= ha_thd(); - timestamp_auto_set_type saved_timestamp_type= table->timestamp_field_type; ulonglong saved_sql_mode= thd->variables.sql_mode; bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null; #ifdef NOT_NEEDED @@ -3472,11 +3471,6 @@ int ha_partition::write_row(uchar * buf) DBUG_ENTER("ha_partition::write_row"); DBUG_ASSERT(buf == m_rec0); - /* If we have a timestamp column, update it to the current time */ - if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) - table->timestamp_field->set_time(); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - /* If we have an auto_increment column and we are writing a changed row or a new row, then update the auto_increment value in the record. @@ -3552,7 +3546,6 @@ int ha_partition::write_row(uchar * buf) exit: thd->variables.sql_mode= saved_sql_mode; table->auto_increment_field_not_null= saved_auto_inc_field_not_null; - table->timestamp_field_type= saved_timestamp_type; DBUG_RETURN(error); } @@ -3587,18 +3580,8 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data) uint32 new_part_id, old_part_id; int error= 0; longlong func_value; - timestamp_auto_set_type orig_timestamp_type= table->timestamp_field_type; DBUG_ENTER("ha_partition::update_row"); - /* - We need to set timestamp field once before we calculate - the partition. Then we disable timestamp calculations - inside m_file[*]->update_row() methods - */ - if (orig_timestamp_type & TIMESTAMP_AUTO_SET_ON_UPDATE) - table->timestamp_field->set_time(); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - if ((error= get_parts_for_update(old_data, new_data, table->record[0], m_part_info, &old_part_id, &new_part_id, &func_value))) @@ -3672,7 +3655,6 @@ exit: info(HA_STATUS_AUTO); set_auto_increment_if_higher(table->found_next_number_field); } - table->timestamp_field_type= orig_timestamp_type; DBUG_RETURN(error); } diff --git a/sql/log_event.cc b/sql/log_event.cc index 132f778ee08..bc787db95da 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -9885,23 +9885,6 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability */ } - /* - We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill - any TIMESTAMP column with data from the row but instead will use - the event's current time. - As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra - columns, we know that all TIMESTAMP columns on slave will receive explicit - data from the row, so TIMESTAMP_NO_AUTO_SET is ok. - When we allow a table without TIMESTAMP to be replicated to a table having - more columns including a TIMESTAMP column, or when we allow a TIMESTAMP - column to be replicated into a BIGINT column and the slave's table has a - TIMESTAMP column, then the slave's TIMESTAMP column will take its value - from set_time() which we called earlier (consistent with SBR). And then in - some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to - analyze if explicit data is provided for slave's TIMESTAMP columns). - */ - m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - /* Honor next number column if present */ m_table->next_number_field= m_table->found_next_number_field; /* @@ -10984,8 +10967,6 @@ Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability if ((err= find_key())) return err; - m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - return 0; } diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index d7c66af769a..bd837adc9d9 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -924,22 +924,6 @@ int Write_rows_log_event_old::do_before_row_operations(TABLE *table) from the start. */ table->file->ha_start_bulk_insert(0); - /* - We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill - any TIMESTAMP column with data from the row but instead will use - the event's current time. - As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra - columns, we know that all TIMESTAMP columns on slave will receive explicit - data from the row, so TIMESTAMP_NO_AUTO_SET is ok. - When we allow a table without TIMESTAMP to be replicated to a table having - more columns including a TIMESTAMP column, or when we allow a TIMESTAMP - column to be replicated into a BIGINT column and the slave's table has a - TIMESTAMP column, then the slave's TIMESTAMP column will take its value - from set_time() which we called earlier (consistent with SBR). And then in - some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to - analyze if explicit data is provided for slave's TIMESTAMP columns). - */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; return error; } @@ -1128,8 +1112,6 @@ int Update_rows_log_event_old::do_before_row_operations(TABLE *table) if (!m_memory) return HA_ERR_OUT_OF_MEM; - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - return error; } @@ -2589,22 +2571,6 @@ Write_rows_log_event_old::do_before_row_operations(const Slave_reporting_capabil from the start. */ m_table->file->ha_start_bulk_insert(0); - /* - We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill - any TIMESTAMP column with data from the row but instead will use - the event's current time. - As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra - columns, we know that all TIMESTAMP columns on slave will receive explicit - data from the row, so TIMESTAMP_NO_AUTO_SET is ok. - When we allow a table without TIMESTAMP to be replicated to a table having - more columns including a TIMESTAMP column, or when we allow a TIMESTAMP - column to be replicated into a BIGINT column and the slave's table has a - TIMESTAMP column, then the slave's TIMESTAMP column will take its value - from set_time() which we called earlier (consistent with SBR). And then in - some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to - analyze if explicit data is provided for slave's TIMESTAMP columns). - */ - m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; return error; } @@ -2814,8 +2780,6 @@ Update_rows_log_event_old::do_before_row_operations(const Slave_reporting_capabi return HA_ERR_OUT_OF_MEM; } - m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - return 0; } diff --git a/sql/sp.cc b/sql/sp.cc index a1ff4db375b..0d9bbb37ede 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1350,7 +1350,6 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, } store_record(table,record[1]); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); if (chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f3ba0073c69..11f962dd414 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2426,7 +2426,6 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, { LEX_STRING cmt = { 0, 0 }; uint unused1= 0; - int unused2= 0; if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec, lex->type, (Item*) 0, (Item*) 0, &cmt, 0, @@ -2443,8 +2442,7 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, sp_prepare_create_field(thd, field_def); - if (prepare_create_field(field_def, &unused1, &unused2, &unused2, - HA_CAN_GEOMETRY)) + if (prepare_create_field(field_def, &unused1, HA_CAN_GEOMETRY)) { return TRUE; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 85e9227c154..fd587640503 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8800,13 +8800,13 @@ err_no_arena: */ static bool -fill_record(THD * thd, List<Item> &fields, List<Item> &values, +fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> f(fields),v(values); Item *value, *fld; Item_field *field; - TABLE *table= 0, *vcol_table= 0; + TABLE *vcol_table= 0; bool save_abort_on_warning= thd->abort_on_warning; bool save_no_errors= thd->no_errors; DBUG_ENTER("fill_record"); @@ -8828,12 +8828,13 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name); goto err; } - table= field->field->table; - table->auto_increment_field_not_null= FALSE; + DBUG_ASSERT(field->field->table == table_arg); + table_arg->auto_increment_field_not_null= FALSE; f.rewind(); } else if (thd->lex->unit.insert_table_with_stored_vcol) vcol_table= thd->lex->unit.insert_table_with_stored_vcol; + while ((fld= f++)) { if (!(field= fld->filed_for_view_update())) @@ -8843,7 +8844,7 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, } value=v++; Field *rfield= field->field; - table= rfield->table; + TABLE* table= rfield->table; if (rfield == table->next_number_field) table->auto_increment_field_not_null= TRUE; if (rfield->vcol_info && @@ -8861,6 +8862,7 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); goto err; } + rfield->set_explicit_default(value); DBUG_ASSERT(vcol_table == 0 || vcol_table == table); vcol_table= table; } @@ -8875,8 +8877,8 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, err: thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; - if (table) - table->auto_increment_field_not_null= FALSE; + if (fields.elements) + table_arg->auto_increment_field_not_null= FALSE; DBUG_RETURN(TRUE); } @@ -8905,13 +8907,13 @@ err: */ bool -fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, +fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, List<Item> &values, bool ignore_errors, - Table_triggers_list *triggers, enum trg_event_type event) { bool result; - result= (fill_record(thd, fields, values, ignore_errors) || + Table_triggers_list *triggers= table->triggers; + result= (fill_record(thd, table, fields, values, ignore_errors) || (triggers && triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE))); /* @@ -8920,7 +8922,6 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, */ if (!result && triggers) { - TABLE *table= 0; List_iterator_fast<Item> f(fields); Item *fld; Item_field *item_field; @@ -8928,9 +8929,7 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, { fld= (Item_field*)f++; item_field= fld->filed_for_view_update(); - if (item_field && item_field->field && - (table= item_field->field->table) && - table->vfield) + if (item_field && item_field->field && table && table->vfield) result= update_virtual_fields(thd, table, TRUE); } } @@ -8960,13 +8959,12 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, */ bool -fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors, - bool use_value) +fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, + bool ignore_errors, bool use_value) { List_iterator_fast<Item> v(values); List<TABLE> tbl_list; Item *value; - TABLE *table= 0; Field *field; bool abort_on_warning_saved= thd->abort_on_warning; DBUG_ENTER("fill_record"); @@ -8981,7 +8979,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors, On INSERT or UPDATE fields are checked to be from the same table, thus we safely can take table from the first field. */ - table= (*ptr)->table; + DBUG_ASSERT((*ptr)->table == table); /* Reset the table->auto_increment_field_not_null as it is valid for @@ -9012,6 +9010,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors, else if (value->save_in_field(field, 0) < 0) goto err; + field->set_explicit_default(value); } /* Update virtual fields*/ thd->abort_on_warning= FALSE; @@ -9051,13 +9050,13 @@ err: */ bool -fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, +fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, List<Item> &values, bool ignore_errors, - Table_triggers_list *triggers, enum trg_event_type event) { bool result; - result= (fill_record(thd, ptr, values, ignore_errors, FALSE) || + Table_triggers_list *triggers= table->triggers; + result= (fill_record(thd, table, ptr, values, ignore_errors, FALSE) || (triggers && triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE))); /* @@ -9066,7 +9065,6 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, */ if (!result && triggers && *ptr) { - TABLE *table= (*ptr)->table; if (table->vfield) result= update_virtual_fields(thd, table, TRUE); } @@ -9706,11 +9704,6 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) /* Make sure all columns get assigned to a default value */ table->use_all_columns(); table->no_replicate= 1; - /* - Don't set automatic timestamps as we may want to use time of logging, - not from query start - */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; } else thd->restore_backup_open_tables_state(backup); diff --git a/sql/sql_base.h b/sql/sql_base.h index 3deb97c9730..fbe905375bb 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -170,15 +170,15 @@ TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl); TABLE *find_temporary_table(THD *thd, const char *table_key, uint table_key_length); void close_thread_tables(THD *thd); -bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, +bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, + List<Item> &fields, List<Item> &values, bool ignore_errors, - Table_triggers_list *triggers, enum trg_event_type event); -bool fill_record_n_invoke_before_triggers(THD *thd, Field **field, +bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, + Field **field, List<Item> &values, bool ignore_errors, - Table_triggers_list *triggers, enum trg_event_type event); bool insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, const char *table_name, @@ -191,7 +191,7 @@ bool setup_fields(THD *thd, Item** ref_pointer_array, List<Item> &item, enum_mark_columns mark_used_columns, List<Item> *sum_func_list, bool allow_sum_func); void unfix_fields(List<Item> &items); -bool fill_record(THD *thd, Field **field, List<Item> &values, +bool fill_record(THD *thd, TABLE *table, Field **field, List<Item> &values, bool ignore_errors, bool use_value); Field * @@ -304,6 +304,7 @@ 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_class.h b/sql/sql_class.h index 3aa0c5ae9c7..b1153edbac8 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4120,6 +4120,16 @@ public: */ #define CF_FORCE_ORIGINAL_BINLOG_FORMAT (1U << 10) +/** + Statement that inserts new rows (INSERT, REPLACE, LOAD) +*/ +#define CF_INSERTS_DATA (1U << 11) + +/** + Statement that updates existing rows (UPDATE, multi-update) +*/ +#define CF_UPDATES_DATA (1U << 12) + /* Bits in server_command_flags */ /** diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc index e65ec3c22b0..1193c7c27f4 100644 --- a/sql/sql_expression_cache.cc +++ b/sql/sql_expression_cache.cc @@ -257,7 +257,7 @@ my_bool Expression_cache_tmptable::put_value(Item *value) } *(items.head_ref())= value; - fill_record(table_thd, cache_table->field, items, TRUE, TRUE); + fill_record(table_thd, cache_table, cache_table->field, items, TRUE, TRUE); if (table_thd->is_error()) goto err;; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3c3b9f85727..5d95ac2716d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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); } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 98031c96225..18cea96dd92 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -273,7 +273,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, for (field=table->field; *field ; field++) fields_vars.push_back(new Item_field(*field)); bitmap_set_all(table->write_set); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; /* Let us also prepare SET clause, altough it is probably empty in this case. @@ -289,21 +288,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) || check_that_all_fields_are_given_values(thd, table, table_list)) DBUG_RETURN(TRUE); - /* - Check whenever TIMESTAMP field with auto-set feature specified - explicitly. - */ - if (table->timestamp_field) - { - if (bitmap_is_set(table->write_set, - table->timestamp_field->field_index)) - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - else - { - bitmap_set_bit(table->write_set, - table->timestamp_field->field_index); - } - } + /* Add all fields with default functions to table->write_set. */ + if (table->default_field) + table->mark_default_fields_for_write(); /* Fix the expressions in SET clause */ if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0)) DBUG_RETURN(TRUE); @@ -850,7 +837,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ER(ER_WARN_TOO_FEW_RECORDS), thd->warning_info->current_row_for_warning()); if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP) - ((Field_timestamp*) field)->set_time(); + field->set_time(); } else { @@ -864,6 +851,7 @@ 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); } } if (pos != read_info.row_end) @@ -876,10 +864,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } if (thd->killed || - fill_record_n_invoke_before_triggers(thd, set_fields, set_values, + fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, ignore_check_option_errors, - table->triggers, - TRG_EVENT_INSERT)) + TRG_EVENT_INSERT) || + (table->default_field && table->update_default_fields())) DBUG_RETURN(1); switch (table_list->view_check_option(thd, @@ -994,10 +982,11 @@ 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()) { if (field->type() == MYSQL_TYPE_TIMESTAMP) - ((Field_timestamp*) field)->set_time(); + field->set_time(); else if (field != table->next_number_field) field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1); @@ -1025,6 +1014,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); } else if (item->type() == Item::STRING_ITEM) { @@ -1066,7 +1056,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, DBUG_RETURN(1); } if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP) - ((Field_timestamp*) field)->set_time(); + field->set_time(); /* TODO: We probably should not throw warning for each field. But how about intention to always have the same number @@ -1093,10 +1083,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } if (thd->killed || - fill_record_n_invoke_before_triggers(thd, set_fields, set_values, + fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, ignore_check_option_errors, - table->triggers, - TRG_EVENT_INSERT)) + TRG_EVENT_INSERT) || + (table->default_field && table->update_default_fields())) DBUG_RETURN(1); switch (table_list->view_check_option(thd, @@ -1206,7 +1196,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (!field->maybe_null()) { if (field->type() == FIELD_TYPE_TIMESTAMP) - ((Field_timestamp *) field)->set_time(); + field->set_time(); else if (field != table->next_number_field) field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1); @@ -1225,6 +1215,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); } else ((Item_user_var_as_out_param *) item)->set_value( @@ -1269,10 +1260,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } if (thd->killed || - fill_record_n_invoke_before_triggers(thd, set_fields, set_values, + fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values, ignore_check_option_errors, - table->triggers, - TRG_EVENT_INSERT)) + TRG_EVENT_INSERT) || + (table->default_field && table->update_default_fields())) DBUG_RETURN(1); switch (table_list->view_check_option(thd, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 175c2c1d672..aa62e450f9d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -268,12 +268,14 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS; sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | - CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS ; + CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | + CF_INSERTS_DATA; sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS; + CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS | + CF_INSERTS_DATA; sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; @@ -290,21 +292,21 @@ void init_update_queries(void) sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | CF_UPDATES_DATA; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | CF_UPDATES_DATA; sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | CF_INSERTS_DATA; sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | CF_INSERTS_DATA; sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS; sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS; sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | CF_INSERTS_DATA; sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | CF_INSERTS_DATA; sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS; sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS; @@ -5881,11 +5883,11 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, no need fix_fields() We allow only one function as part of default value - - NOW() as default for TIMESTAMP type. + NOW() as default for TIMESTAMP and DATETIME type. */ if (default_value->type() == Item::FUNC_ITEM && !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC && - type == MYSQL_TYPE_TIMESTAMP)) + (type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_DATETIME))) { my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); DBUG_RETURN(1); @@ -5907,7 +5909,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, } } - if (on_update_value && type != MYSQL_TYPE_TIMESTAMP) + if (on_update_value && + !(type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_DATETIME)) { my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str); DBUG_RETURN(1); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 2bec12e4f66..f042f028450 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6526,9 +6526,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->pack_frm_data= NULL; lpt->pack_frm_len= 0; - /* Never update timestamp columns when alter */ - lpt->table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - if (table->file->alter_table_flags(alter_info->flags) & HA_PARTITION_ONE_PHASE) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f0c786dea86..e89d4c14c96 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9463,7 +9463,7 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) if (item->is_null()) DBUG_RETURN(NESTED_LOOP_OK); } - fill_record(thd, table->field, sjm->sjm_table_cols, TRUE, FALSE); + fill_record(thd, table, table->field, sjm->sjm_table_cols, TRUE, FALSE); if (thd->is_error()) DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ if ((error= table->file->ha_write_tmp_row(table->record[0]))) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8a106b8ec6f..a71ca73789f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1108,8 +1108,35 @@ static void append_directory(THD *thd, String *packet, const char *dir_type, #define LIST_PROCESS_HOST_LEN 64 -static bool get_field_default_value(THD *thd, Field *timestamp_field, - Field *field, String *def_value, + +/** + Print "ON UPDATE" clause of a field into a string. + + @param timestamp_field Pointer to timestamp field of a table. + @param field The field to generate ON UPDATE clause for. + @bool lcase Whether to print in lower case. + @return false on success, true on error. +*/ +static bool print_on_update_clause(Field *field, String *val, bool lcase) +{ + DBUG_ASSERT(val->charset()->mbminlen == 1); + val->length(0); + if (field->has_update_default_function()) + { + if (lcase) + val->append(STRING_WITH_LEN("on update ")); + else + val->append(STRING_WITH_LEN("ON UPDATE ")); + val->append(STRING_WITH_LEN("CURRENT_TIMESTAMP")); + if (field->decimals() > 0) + val->append_parenthesized(field->decimals()); + return true; + } + return false; +} + + +static bool get_field_default_value(THD *thd, Field *field, String *def_value, bool quoted) { bool has_default; @@ -1120,8 +1147,7 @@ static bool get_field_default_value(THD *thd, Field *timestamp_field, We are using CURRENT_TIMESTAMP instead of NOW because it is more standard */ - has_now_default= (timestamp_field == field && - field->unireg_check != Field::TIMESTAMP_UN_FIELD); + has_now_default= field->has_insert_default_function(); has_default= (field_type != FIELD_TYPE_BLOB && !(field->flags & NO_DEFAULT_VALUE_FLAG) && @@ -1133,7 +1159,11 @@ static bool get_field_default_value(THD *thd, Field *timestamp_field, if (has_default) { if (has_now_default) + { def_value->append(STRING_WITH_LEN("CURRENT_TIMESTAMP")); + if (field->decimals() > 0) + def_value->append_parenthesized(field->decimals()); + } else if (!field->is_null()) { // Not null by default char tmp[MAX_FIELD_WIDTH]; @@ -1365,16 +1395,18 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, } if (!field->vcol_info && - get_field_default_value(thd, table->timestamp_field, - field, &def_value, 1)) + get_field_default_value(thd, field, &def_value, 1)) { packet->append(STRING_WITH_LEN(" DEFAULT ")); packet->append(def_value.ptr(), def_value.length(), system_charset_info); } - if (!limited_mysql_mode && table->timestamp_field == field && - field->unireg_check != Field::TIMESTAMP_DN_FIELD) - packet->append(STRING_WITH_LEN(" ON UPDATE CURRENT_TIMESTAMP")); + if (!limited_mysql_mode && print_on_update_clause(field, &def_value, false)) + { + packet->append(STRING_WITH_LEN(" ")); + packet->append(def_value); + } + if (field->unireg_check == Field::NEXT_NUMBER && !(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS)) @@ -4758,7 +4790,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, const char *wild= lex->wild ? lex->wild->ptr() : NullS; CHARSET_INFO *cs= system_charset_info; TABLE *show_table; - Field **ptr, *field, *timestamp_field; + Field **ptr, *field; int count; DBUG_ENTER("get_schema_column_record"); @@ -4782,7 +4814,6 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, show_table= tables->table; count= 0; ptr= show_table->field; - timestamp_field= show_table->timestamp_field; show_table->use_all_columns(); // Required for default restore_record(show_table, s->default_values); @@ -4830,7 +4861,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, cs); table->field[4]->store((longlong) count, TRUE); - if (get_field_default_value(thd, timestamp_field, field, &type, 0)) + if (get_field_default_value(thd, field, &type, 0)) { table->field[5]->store(type.ptr(), type.length(), cs); table->field[5]->set_notnull(); @@ -4847,10 +4878,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, if (field->unireg_check == Field::NEXT_NUMBER) table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs); - if (timestamp_field == field && - field->unireg_check != Field::TIMESTAMP_DN_FIELD) - table->field[17]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"), - cs); + if (print_on_update_clause(field, &type, true)) + table->field[17]->store(type.ptr(), type.length(), cs); if (field->vcol_info) { if (field->stored_in_db) diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 75029a03790..89536b93feb 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -502,6 +502,24 @@ bool String::append(IO_CACHE* file, uint32 arg_length) return FALSE; } + +/** + Append a parenthesized number to String. + Used in various pieces of SHOW related code. + + @param nr Number + @param radix Radix, optional parameter, 10 by default. +*/ +bool String::append_parenthesized(long nr, int radix) +{ + char buff[64], *end; + buff[0]= '('; + end= int10_to_str(nr, buff + 1, radix); + *end++ = ')'; + return append(buff, (uint) (end - buff)); +} + + bool String::append_with_prefill(const char *s,uint32 arg_length, uint32 full_length, char fill_char) { diff --git a/sql/sql_string.h b/sql/sql_string.h index 2f0cd9103dc..633170a5e20 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -343,6 +343,7 @@ public: bool append(IO_CACHE* file, uint32 arg_length); bool append_with_prefill(const char *s, uint32 arg_length, uint32 full_length, char fill_char); + bool append_parenthesized(long nr, int radix= 10); int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 bool replace(uint32 offset,uint32 arg_length,const char *to,uint32 length); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 5a6090e5d9a..e66d4f529de 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2607,7 +2607,6 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, prepare_create_field() sql_field field to prepare for packing blob_columns count for BLOBs - timestamps count for timestamps table_flags table flags DESCRIPTION @@ -2621,7 +2620,6 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, int prepare_create_field(Create_field *sql_field, uint *blob_columns, - int *timestamps, int *timestamps_with_niladic, longlong table_flags) { unsigned int dup_val_count; @@ -2743,21 +2741,6 @@ int prepare_create_field(Create_field *sql_field, (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); break; case MYSQL_TYPE_TIMESTAMP: - /* We should replace old TIMESTAMP fields with their newer analogs */ - if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) - { - if (!*timestamps) - { - sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD; - (*timestamps_with_niladic)++; - } - else - sql_field->unireg_check= Field::NONE; - } - else if (sql_field->unireg_check != Field::NONE) - (*timestamps_with_niladic)++; - - (*timestamps)++; /* fall-through */ default: sql_field->pack_flag=(FIELDFLAG_NUMBER | @@ -2829,6 +2812,40 @@ bool check_duplicate_warning(THD *thd, char *msg, ulong length) } +/** + Modifies the first column definition whose SQL type is TIMESTAMP + by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. + + @param column_definitions The list of column definitions, in the physical + order in which they appear in the table. + */ +void promote_first_timestamp_column(List<Create_field> *column_definitions) +{ + List_iterator<Create_field> it(*column_definitions); + Create_field *column_definition; + + while ((column_definition= it++) != NULL) + { + if (column_definition->sql_type == MYSQL_TYPE_TIMESTAMP || // TIMESTAMP + column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy + { + if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, + column_definition->def == NULL && // no constant default, + column_definition->unireg_check == Field::NONE) // no function default + { + DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " + "DEFAULT CURRENT_TIMESTAMP ON UPDATE " + "CURRENT_TIMESTAMP", + column_definition->field_name + )); + column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD; + } + return; + } + } +} + + /* Preparation for table creation @@ -2869,7 +2886,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ulong record_offset= 0; KEY *key_info; KEY_PART_INFO *key_part_info; - int timestamps= 0, timestamps_with_niladic= 0; int field_no,dup_no; int select_field_pos,auto_increment=0; List_iterator<Create_field> it(alter_info->create_list); @@ -3153,7 +3169,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_ASSERT(sql_field->charset != 0); if (prepare_create_field(sql_field, &blob_columns, - ×tamps, ×tamps_with_niladic, file->ha_table_flags())) DBUG_RETURN(TRUE); if (sql_field->sql_type == MYSQL_TYPE_VARCHAR) @@ -3184,12 +3199,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, record_offset+= sql_field->pack_length; } } - if (timestamps_with_niladic > 1) - { - my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS, - ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0)); - DBUG_RETURN(TRUE); - } if (auto_increment > 1) { my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0)); @@ -4558,6 +4567,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, /* Got lock. */ DEBUG_SYNC(thd, "locked_table_name"); + promote_first_timestamp_column(&alter_info->create_list); + result= mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, create_info, alter_info, FALSE, 0, &is_trans); @@ -6371,6 +6382,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, need_copy_table= alter_info->change_level; set_table_default_charset(thd, create_info, db); + promote_first_timestamp_column(&alter_info->create_list); if (thd->variables.old_alter_table || (table->s->db_type() != create_info->db_type) @@ -6668,6 +6680,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); DBUG_EXECUTE_IF("sleep_before_create_table_no_lock", my_sleep(100000);); + /* Create a table with a temporary name. With create_info->frm_only == 1 this creates a .frm file only. @@ -6738,8 +6751,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, */ if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) { - /* We don't want update TIMESTAMP fields during ALTER TABLE. */ - new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; new_table->next_number_field=new_table->found_next_number_field; DBUG_EXECUTE_IF("abort_copy_table", { my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); @@ -7316,6 +7327,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, ulonglong prev_insert_id, time_to_report_progress; List_iterator<Create_field> it(create); Create_field *def; + Field **dfield_ptr= to->default_field; DBUG_ENTER("copy_data_between_tables"); /* Two or 3 stages; Sorting, copying data and update indexes */ @@ -7345,6 +7357,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, errpos= 3; copy_end=copy; + to->s->default_fields= 0; for (Field **ptr=to->field ; *ptr ; ptr++) { def=it++; @@ -7364,8 +7377,23 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, } (copy_end++)->set(*ptr,def->field,0); } - + else + { + /* + Update the set of auto-update fields to contain only the newly added + fields. Only these fields should be updated automatically. Old fields + keep their current values, and therefore should not be present in the + set of autoupdate fields. + */ + if ((*ptr)->has_insert_default_function()) + { + *(dfield_ptr++)= *ptr; + ++to->s->default_fields; + } + } } + if (dfield_ptr) + *dfield_ptr= NULL; if (order) { @@ -7456,6 +7484,11 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, prev_insert_id= to->file->next_insert_id; if (to->vfield) update_virtual_fields(thd, to, TRUE); + if (to->default_field && to->update_default_fields()) + { + error= 1; + break; + } if (thd->is_error()) { error= 1; diff --git a/sql/sql_table.h b/sql/sql_table.h index 00de6ed1b8d..9d5e768a5a3 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -187,7 +187,6 @@ void close_cached_table(THD *thd, TABLE *table); void sp_prepare_create_field(THD *thd, Create_field *sql_field); int prepare_create_field(Create_field *sql_field, uint *blob_columns, - int *timestamps, int *timestamps_with_niladic, longlong table_flags); CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, HA_CREATE_INFO *create_info); @@ -208,6 +207,9 @@ void execute_ddl_log_recovery(); bool execute_ddl_log_entry(THD *thd, uint first_entry); bool check_duplicate_warning(THD *thd, char *msg, ulong length); +template<typename T> class List; +void promote_first_timestamp_column(List<Create_field> *column_definitions); + /* These prototypes where under INNODB_COMPATIBILITY_HOOKS. */ diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 8dcae907926..e6c9d7e4977 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -63,7 +63,7 @@ int select_union::send_data(List<Item> &values) return 0; if (table->no_rows_with_nulls) table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT; - fill_record(thd, table->field, values, TRUE, FALSE); + fill_record(thd, table, table->field, values, TRUE, FALSE); if (thd->is_error()) return 1; if (table->no_rows_with_nulls) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index fb0951410a4..4f31da92107 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -342,19 +342,8 @@ int mysql_update(THD *thd, my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_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)) - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - else - { - if (((uint) table->timestamp_field_type) & TIMESTAMP_AUTO_SET_ON_UPDATE) - bitmap_set_bit(table->write_set, - table->timestamp_field->field_index); - } - } + if (table->default_field) + table->mark_default_fields_for_write(); #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check values */ @@ -389,7 +378,7 @@ int mysql_update(THD *thd, to compare records and detect data change. */ if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && - (((uint) table->timestamp_field_type) & TIMESTAMP_AUTO_SET_ON_UPDATE)) + table->default_field && table->has_default_function(true)) bitmap_union(table->read_set, table->write_set); // Don't count on usage of 'only index' when calculating which key to use table->covering_keys.clear_all(); @@ -686,8 +675,7 @@ int mysql_update(THD *thd, continue; /* repeat the read of the same row if it still exists */ store_record(table,record[1]); - 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_UPDATE)) break; /* purecov: inspected */ @@ -695,6 +683,11 @@ int mysql_update(THD *thd, if (!can_compare_record || compare_record(table)) { + if (table->default_field && table->update_default_fields()) + { + error= 1; + break; + } if ((res= table_list->view_check_option(thd, ignore)) != VIEW_CHECK_OK) { @@ -1241,11 +1234,6 @@ int mysql_multi_update_prepare(THD *thd) while ((tl= ti++)) { TABLE *table= tl->table; - /* Only set timestamp column if this is not modified */ - if (table->timestamp_field && - bitmap_is_set(table->write_set, - table->timestamp_field->field_index)) - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; /* if table will be updated then check that it is unique */ if (table->map & tables_for_update) @@ -1495,8 +1483,7 @@ int multi_update::prepare(List<Item> ¬_used_values, to compare records and detect data change. */ if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && - (((uint) table->timestamp_field_type) & - TIMESTAMP_AUTO_SET_ON_UPDATE)) + table->default_field && table->has_default_function(true)) bitmap_union(table->read_set, table->write_set); } } @@ -1875,10 +1862,10 @@ int multi_update::send_data(List<Item> ¬_used_values) table->status|= STATUS_UPDATED; store_record(table,record[1]); - if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset], + if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset], *values_for_table[offset], 0, - table->triggers, - TRG_EVENT_UPDATE)) + TRG_EVENT_UPDATE) || + (table->default_field && table->update_default_fields())) DBUG_RETURN(1); /* @@ -1979,7 +1966,7 @@ int multi_update::send_data(List<Item> ¬_used_values) } while ((tbl= tbl_it++)); /* Store regular updated fields in the row. */ - fill_record(thd, + fill_record(thd, tmp_table, tmp_table->field + 1 + unupdated_check_opt_tables.elements, *values_for_table[offset], TRUE, FALSE); @@ -2169,7 +2156,10 @@ int multi_update::do_updates() for (copy_field_ptr=copy_field; copy_field_ptr != copy_field_end; copy_field_ptr++) + { (*copy_field_ptr->do_copy)(copy_field_ptr); + copy_field_ptr->to_field->set_explicit_default(NULL); + } if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, @@ -2179,6 +2169,8 @@ int multi_update::do_updates() if (!can_compare_record || compare_record(table)) { int error; + if (table->default_field && (error= table->update_default_fields())) + goto err2; if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 555efaf366d..16bfa16d0a9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5873,9 +5873,9 @@ attribute: NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; } | DEFAULT now_or_signed_literal { Lex->default_value=$2; } - | ON UPDATE_SYM NOW_SYM optional_braces + | ON UPDATE_SYM NOW_SYM opt_time_precision { - Item *item= new (YYTHD->mem_root) Item_func_now_local(6); + Item *item= new (YYTHD->mem_root) Item_func_now_local($4); if (item == NULL) MYSQL_YYABORT; Lex->on_update_value= item; @@ -5967,9 +5967,9 @@ type_with_opt_collate: now_or_signed_literal: - NOW_SYM optional_braces + NOW_SYM opt_time_precision { - $$= new (YYTHD->mem_root) Item_func_now_local(6); + $$= new (YYTHD->mem_root) Item_func_now_local($2); if ($$ == NULL) MYSQL_YYABORT; } diff --git a/sql/table.cc b/sql/table.cc index 47b2cae1a04..cdf11116266 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1250,6 +1250,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, com_length= uint2korr(forminfo+284); vcol_screen_length= uint2korr(forminfo+286); share->vfields= 0; + share->default_fields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { @@ -1581,8 +1582,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (reg_field->unireg_check == Field::NEXT_NUMBER) share->found_next_number_field= field_ptr; - if (share->timestamp_field == reg_field) - share->timestamp_field_offset= i; if (use_hash) { @@ -1604,6 +1603,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (share->stored_rec_length>=recpos) share->stored_rec_length= recpos-1; } + if (reg_field->has_insert_default_function() || + reg_field->has_update_default_function()) + ++share->default_fields; } *field_ptr=0; // End marker /* Sanity checks: */ @@ -2315,7 +2317,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, uint records, i, bitmap_size; bool error_reported= FALSE; uchar *record, *bitmaps; - Field **field_ptr, **vfield_ptr; + Field **field_ptr, **vfield_ptr, **dfield_ptr; uint8 save_context_analysis_only= thd->lex->context_analysis_only; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, @@ -2419,9 +2421,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, if (share->found_next_number_field) outparam->found_next_number_field= outparam->field[(uint) (share->found_next_number_field - share->field)]; - if (share->timestamp_field) - outparam->timestamp_field= (Field_timestamp*) outparam->field[share->timestamp_field_offset]; - /* Fix key->name and key_part->field */ if (share->key_parts) @@ -2472,7 +2471,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } /* - Process virtual columns, if any. + Process virtual and default columns, if any. */ if (!share->vfields) outparam->vfield= NULL; @@ -2484,10 +2483,26 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, goto err; outparam->vfield= vfield_ptr; + } + + if (!share->default_fields) + outparam->default_field= NULL; + else + { + if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root, + (uint) ((share->default_fields+1)* + sizeof(Field*))))) + goto err; + outparam->default_field= dfield_ptr; + } + + if (share->vfields || share->default_fields) + { + /* Reuse the same loop both for virtual and default fields. */ for (field_ptr= outparam->field; *field_ptr; field_ptr++) { - if ((*field_ptr)->vcol_info) + if (share->vfields && (*field_ptr)->vcol_info) { if (unpack_vcol_info_from_frm(thd, outparam, @@ -2500,8 +2515,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } *(vfield_ptr++)= *field_ptr; } + if (share->default_fields && + ((*field_ptr)->has_insert_default_function() || + (*field_ptr)->has_update_default_function())) + *(dfield_ptr++)= *field_ptr; } - *vfield_ptr= 0; // End marker + if (share->vfields) + *vfield_ptr= 0; // End marker + if (share->default_fields) + *dfield_ptr= 0; // End marker } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -3924,9 +3946,6 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) DBUG_ASSERT(!auto_increment_field_not_null); auto_increment_field_not_null= FALSE; - if (timestamp_field) - timestamp_field_type= timestamp_field->get_auto_set_type(); - pos_in_table_list= tl; clear_column_bitmaps(); @@ -5843,6 +5862,48 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl) /** +*/ + +bool TABLE::has_default_function(bool is_update) +{ + Field **dfield_ptr, *dfield; + bool res= false; + for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + { + dfield= (*dfield_ptr); + if (is_update) + res= dfield->has_update_default_function(); + else + res= dfield->has_insert_default_function(); + if (res) + return res; + } + return res; +} + + +/** +*/ + +void TABLE::mark_default_fields_for_write() +{ + Field **dfield_ptr, *dfield; + enum_sql_command cmd= in_use->lex->sql_command; + for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + { + dfield= (*dfield_ptr); + if (((cmd == SQLCOM_INSERT || cmd == SQLCOM_INSERT_SELECT || + cmd == SQLCOM_REPLACE || cmd == SQLCOM_REPLACE_SELECT || + cmd == SQLCOM_LOAD) && + dfield->has_insert_default_function()) || + ((cmd == SQLCOM_UPDATE || cmd == SQLCOM_UPDATE_MULTI) && + dfield->has_update_default_function())) + bitmap_set_bit(write_set, dfield->field_index); + } +} + + +/** @brief Allocate space for keys @@ -6448,6 +6509,50 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) DBUG_RETURN(0); } + +/** + Update all DEFAULT and/or ON INSERT fields. + + @details + + @retval + 0 Success + @retval + >0 Error occurred when storing a virtual field value +*/ + +int TABLE::update_default_fields() +{ + DBUG_ENTER("update_default_fields"); + Field **dfield_ptr, *dfield; + int res= 0; + enum_sql_command cmd= in_use->lex->sql_command; + + DBUG_ASSERT(default_field); + /* Iterate over virtual fields in the table */ + for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + { + dfield= (*dfield_ptr); + /* + 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 (sql_command_flags[cmd] & CF_INSERTS_DATA) + res= dfield->evaluate_insert_default_function(); + if (sql_command_flags[cmd] & CF_UPDATES_DATA) + res= dfield->evaluate_update_default_function(); + if (res) + DBUG_RETURN(res); + } + /* Unset the explicit default flag for the next record. */ + dfield->flags&= ~HAS_EXPLICIT_DEFAULT; + } + DBUG_RETURN(res); +} + + /* @brief Reset const_table flag diff --git a/sql/table.h b/sql/table.h index 9e26f907920..e98f93c8bb1 100644 --- a/sql/table.h +++ b/sql/table.h @@ -350,25 +350,6 @@ public: }; -/* - Values in this enum are used to indicate how a tables TIMESTAMP field - should be treated. It can be set to the current timestamp on insert or - update or both. - WARNING: The values are used for bit operations. If you change the - enum, you must keep the bitwise relation of the values. For example: - (int) TIMESTAMP_AUTO_SET_ON_BOTH must be equal to - (int) TIMESTAMP_AUTO_SET_ON_INSERT | (int) TIMESTAMP_AUTO_SET_ON_UPDATE. - We use an enum here so that the debugger can display the value names. -*/ -enum timestamp_auto_set_type -{ - TIMESTAMP_NO_AUTO_SET= 0, TIMESTAMP_AUTO_SET_ON_INSERT= 1, - TIMESTAMP_AUTO_SET_ON_UPDATE= 2, TIMESTAMP_AUTO_SET_ON_BOTH= 3 -}; -#define clear_timestamp_auto_bits(_target_, _bits_) \ - (_target_)= (enum timestamp_auto_set_type)((int)(_target_) & ~(int)(_bits_)) - -class Field_timestamp; class Field_blob; class Table_triggers_list; @@ -608,7 +589,6 @@ struct TABLE_SHARE /* The following is copied to each TABLE on OPEN */ Field **field; Field **found_next_number_field; - Field *timestamp_field; /* Used only during open */ KEY *key_info; /* data of keys in database */ uint *blob_field; /* Index to blobs in Field arrray*/ @@ -680,7 +660,6 @@ struct TABLE_SHARE uint uniques; /* Number of UNIQUE index */ uint null_fields; /* number of null fields */ uint blob_fields; /* number of blob fields */ - uint timestamp_field_offset; /* Field number for timestamp field */ uint varchar_fields; /* number of varchar fields */ uint db_create_options; /* Create options from database */ uint db_options_in_use; /* Options in use */ @@ -695,6 +674,7 @@ struct TABLE_SHARE uint column_bitmap_size; uchar frm_version; uint vfields; /* Number of computed (virtual) fields */ + uint default_fields; /* Number of default fields in */ bool use_ext_keys; /* Extended keys can be used */ bool null_field_first; bool system; /* Set if system table (one record) */ @@ -1007,8 +987,9 @@ public: Field *next_number_field; /* Set if next_number is activated */ Field *found_next_number_field; /* Set on open */ - Field_timestamp *timestamp_field; Field **vfield; /* Pointer to virtual fields*/ + /* Fields that are updated automatically on INSERT or UPDATE. */ + Field **default_field; /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; @@ -1064,19 +1045,6 @@ public: */ ha_rows quick_condition_rows; - /* - If this table has TIMESTAMP field with auto-set property (pointed by - timestamp_field member) then this variable indicates during which - operations (insert only/on update/in both cases) we should set this - field to current timestamp. If there are no such field in this table - or we should not automatically set its value during execution of current - statement then the variable contains TIMESTAMP_NO_AUTO_SET (i.e. 0). - - Value of this variable is set for each statement in open_table() and - if needed cleared later in statement processing code (see mysql_update() - as example). - */ - timestamp_auto_set_type timestamp_field_type; table_map map; /* ID bit of table (1,2,4,8,16...) */ uint lock_position; /* Position in MYSQL_LOCK.table */ @@ -1207,6 +1175,8 @@ public: void mark_columns_needed_for_insert(void); bool mark_virtual_col(Field *field); void mark_virtual_columns_for_write(bool insert_fl); + void mark_default_fields_for_write(); + bool has_default_function(bool is_update); inline void column_bitmaps_set(MY_BITMAP *read_set_arg, MY_BITMAP *write_set_arg) { @@ -1293,6 +1263,7 @@ public: bool update_const_key_parts(COND *conds); uint actual_n_key_parts(KEY *keyinfo); ulong actual_key_flags(KEY *keyinfo); + int update_default_fields(); }; |