diff options
Diffstat (limited to 'sql')
53 files changed, 1179 insertions, 934 deletions
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 3ef1f22e2b8..6c8eae82a47 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -984,6 +984,7 @@ static bool debug_sync_eval_action(THD *thd, char *action_str) DBUG_ENTER("debug_sync_eval_action"); DBUG_ASSERT(thd); DBUG_ASSERT(action_str); + DBUG_PRINT("debug_sync", ("action_str='%s'", action_str)); /* Get debug sync point name. Or a special command. diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index d77c6e3a683..12d34580d3c 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -830,9 +830,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 @@ -1134,8 +1131,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 e0ecd6ab74f..a18c72119be 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -50,11 +50,6 @@ Instansiate templates and static variables *****************************************************************************/ -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<Create_field>; -template class List_iterator<Create_field>; -#endif - static const char *zero_timestamp="0000-00-00 00:00:00.000000"; /* number of bytes to store second_part part of the TIMESTAMP(N) */ @@ -1817,6 +1812,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 +4368,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 @@ -4419,10 +4412,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; + /* + 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) flags|= ON_UPDATE_NOW_FLAG; @@ -4430,40 +4425,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 +4674,34 @@ int Field_timestamp::set_time() return 0; } +/** + Mark the field as having an explicit default value. + + @param value if available, the value that the field is being set to + + @note + Fields that have an explicit default value should not be updated + automatically via the DEFAULT or ON UPDATE functions. The functions + that deal with data change functionality (INSERT/UPDATE/LOAD), + determine if there is an explicit value for each field before performing + the data change, and call this method to mark the field. + + 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->type() == Item::DEFAULT_VALUE_ITEM && + !((Item_default_value*)value)->arg) || + (!maybe_null() && value->is_null()))) + return; + set_has_explicit_value(); +} + void Field_timestamp_hires::sql_type(String &res) const { CHARSET_INFO *cs=res.charset(); @@ -5834,6 +5823,20 @@ void Field_datetime::sql_type(String &res) const res.set_ascii(STRING_WITH_LEN("datetime")); } + +int Field_datetime::set_time() +{ + 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(); + 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); @@ -8855,16 +8858,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) + { + /* 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. + } + 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) { @@ -9085,44 +9109,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 */ @@ -9588,11 +9574,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); @@ -9776,3 +9769,29 @@ key_map Field::get_possible_keys() return (table->pos_in_table_list->is_materialized_derived() ? part_of_key : key_start); } + + +/** + Mark the field as having an explicit default value. + + @param value if available, the value that the field is being set to + + @note + Fields that have an explicit default value should not be updated + automatically via the DEFAULT or ON UPDATE functions. The functions + that deal with data change functionality (INSERT/UPDATE/LOAD), + 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. +*/ + +void Field::set_explicit_default(Item *value) +{ + if (value->type() == Item::DEFAULT_VALUE_ITEM && + !((Item_default_value*)value)->arg) + return; + set_has_explicit_value(); +} diff --git a/sql/field.h b/sql/field.h index f22bab0409d..da78a7c7674 100644 --- a/sql/field.h +++ b/sql/field.h @@ -329,6 +329,46 @@ 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; + } + + /* + 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); + + /** + 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 +1279,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 +1306,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 +1556,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/filesort.cc b/sql/filesort.cc index ac67fb56395..c612d2c55cb 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -45,10 +45,6 @@ if (my_b_write((file),(uchar*) (from),param->ref_length)) \ DBUG_RETURN(1); -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class Bounded_queue<uchar, uchar>; -#endif - /* functions defined in this file */ static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count, 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 2804da8e7ae..a24d144c97b 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -298,13 +298,6 @@ void ha_partition::init_handler_variables() } -const char *ha_partition::table_type() const -{ - // we can do this since we only support a single engine type - return m_file[0]->table_type(); -} - - /* Destructor method @@ -3446,8 +3439,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) @@ -3458,7 +3451,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 @@ -3467,11 +3459,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. @@ -3547,7 +3534,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); } @@ -3582,18 +3568,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))) @@ -3667,7 +3643,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); } @@ -4680,8 +4655,8 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) bool reverse_order= FALSE; DBUG_ENTER("ha_partition::common_index_read"); - DBUG_PRINT("info", ("m_ordered %u m_ordered_scan_ong %u have_start_key %u", - m_ordered, m_ordered_scan_ongoing, have_start_key)); + DBUG_PRINT("info", ("m_ordered: %u have_start_key: %u", + m_ordered, have_start_key)); if (have_start_key) { diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 3a0a35fdfc0..7818e1e608f 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -637,9 +637,6 @@ public: */ virtual const char *index_type(uint inx); - /* The name of the table type that will be used for display purposes */ - virtual const char *table_type() const; - /* The name of the row type used for the underlying tables. */ virtual enum row_type get_row_type() const; diff --git a/sql/handler.cc b/sql/handler.cc index 271ed73ad53..bf84b1287d5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5363,10 +5363,6 @@ int handler::ha_write_row(uchar *buf) DBUG_ENTER("handler::ha_write_row"); DEBUG_SYNC_C("ha_write_row_start"); - /* 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(); - MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_write_count); @@ -5397,9 +5393,6 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) */ DBUG_ASSERT(new_data == table->record[0]); - if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) - table->timestamp_field->set_time(); - MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_update_count); diff --git a/sql/handler.h b/sql/handler.h index f043f1b44cd..997088b1192 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2487,7 +2487,7 @@ public: { return; } /* prepare InnoDB for HANDLER */ virtual void free_foreign_key_create_info(char* str) {} /** The following can be called without an open handler */ - virtual const char *table_type() const =0; + const char *table_type() const { return hton_name(ht)->str; } /** If frm_error() is called then we will use this to find out what file extentions exist for the storage engine. This is also used by the default diff --git a/sql/item.cc b/sql/item.cc index 5f4b34ea552..27e6d7052d6 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9652,14 +9652,3 @@ const char *dbug_print_item(Item *item) #endif /*DBUG_OFF*/ -/***************************************************************************** -** Instantiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<Item>; -template class List_iterator<Item>; -template class List_iterator_fast<Item>; -template class List_iterator_fast<Item_field>; -template class List<List_item>; -#endif diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 86e0fd32774..ce396736d6f 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -173,12 +173,3 @@ bool Cached_item_decimal::cmp() return FALSE; } - -/***************************************************************************** -** Instansiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<Cached_item>; -template class List_iterator<Cached_item>; -#endif diff --git a/sql/item_func.h b/sql/item_func.h index 07b246b3ccd..80fa0b5d634 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1572,6 +1572,15 @@ public: :Item_func(b), cached_result_type(INT_RESULT), entry(NULL), entry_thread_id(0), name(a) {} + Item_func_set_user_var(Item_func_set_user_var *item) + :Item_func(item), cached_result_type(item->cached_result_type), + entry(item->entry), entry_thread_id(item->entry_thread_id), + value(item->value), decimal_buff(item->decimal_buff), + null_item(item->null_item), save_result(item->save_result), + name(item->name) + { + //fixed= 1; + } enum Functype functype() const { return SUSERVAR_FUNC; } double val_real(); longlong val_int(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 0b21ba92558..b04fda55736 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2510,9 +2510,8 @@ Item_sum_hybrid::min_max_update_int_field() void Item_sum_hybrid::min_max_update_decimal_field() { - /* TODO: optimize: do not get result_field in case of args[0] is NULL */ my_decimal old_val, nr_val; - const my_decimal *old_nr= result_field->val_decimal(&old_val); + const my_decimal *old_nr; const my_decimal *nr= args[0]->val_decimal(&nr_val); if (!args[0]->null_value) { @@ -2520,16 +2519,17 @@ Item_sum_hybrid::min_max_update_decimal_field() old_nr=nr; else { + old_nr= result_field->val_decimal(&old_val); bool res= my_decimal_cmp(old_nr, nr) > 0; /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ if ((cmp_sign > 0) ^ (!res)) old_nr=nr; } result_field->set_notnull(); + result_field->store_decimal(old_nr); } else if (result_field->is_null(0)) result_field->set_null(); - result_field->store_decimal(old_nr); } diff --git a/sql/keycaches.cc b/sql/keycaches.cc index 26a39808c56..84ed67d00f0 100644 --- a/sql/keycaches.cc +++ b/sql/keycaches.cc @@ -159,7 +159,3 @@ bool process_key_caches(process_key_cache_t func, void *param) return res != 0; } -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class I_List_iterator<NAMED_ILINK>; -#endif - diff --git a/sql/log.cc b/sql/log.cc index 69ff68d335f..1813120dd81 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -54,6 +54,7 @@ #include "rpl_handler.h" #include "debug_sync.h" #include "sql_show.h" +#include "my_pthread.h" /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 @@ -107,6 +108,17 @@ static SHOW_VAR binlog_status_vars_detail[]= {NullS, NullS, SHOW_LONG} }; +/* + Variables for the binlog background thread. + Protected by the MYSQL_BIN_LOG::LOCK_binlog_background_thread mutex. + */ +static bool binlog_background_thread_started= false; +static bool binlog_background_thread_stop= false; +static MYSQL_BIN_LOG::xid_count_per_binlog * + binlog_background_thread_queue= NULL; + +static bool start_binlog_background_thread(); + /** purge logs, master and slave sides both, related error code @@ -1288,12 +1300,6 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, DBUG_ASSERT(thd); - lock_shared(); - if (!opt_log) - { - unlock(); - return 0; - } user_host_len= make_user_name(thd, user_host_buff); current_time= my_hrtime(); @@ -1304,15 +1310,19 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, command_name[(uint) command].length, query, query_length); - while (*current_handler) - error|= (*current_handler++)-> - log_general(thd, current_time, user_host_buff, - user_host_len, thd->thread_id, - command_name[(uint) command].str, - command_name[(uint) command].length, - query, query_length, - thd->variables.character_set_client) || error; - unlock(); + if (opt_log && log_command(thd, command)) + { + lock_shared(); + while (*current_handler) + error|= (*current_handler++)-> + log_general(thd, current_time, user_host_buff, + user_host_len, thd->thread_id, + command_name[(uint) command].str, + command_name[(uint) command].length, + query, query_length, + thd->variables.character_set_client) || error; + unlock(); + } return error; } @@ -2972,12 +2982,28 @@ void MYSQL_BIN_LOG::cleanup() my_free(b); } + /* Wait for the binlog background thread to stop. */ + if (!is_relay_log && binlog_background_thread_started) + { + mysql_mutex_lock(&LOCK_binlog_background_thread); + binlog_background_thread_stop= true; + mysql_cond_signal(&COND_binlog_background_thread); + while (binlog_background_thread_stop) + mysql_cond_wait(&COND_binlog_background_thread_end, + &LOCK_binlog_background_thread); + mysql_mutex_unlock(&LOCK_binlog_background_thread); + binlog_background_thread_started= false; + } + mysql_mutex_destroy(&LOCK_log); mysql_mutex_destroy(&LOCK_index); mysql_mutex_destroy(&LOCK_xid_list); + mysql_mutex_destroy(&LOCK_binlog_background_thread); mysql_cond_destroy(&update_cond); mysql_cond_destroy(&COND_queue_busy); mysql_cond_destroy(&COND_xid_list); + mysql_cond_destroy(&COND_binlog_background_thread); + mysql_cond_destroy(&COND_binlog_background_thread_end); } DBUG_VOID_RETURN; } @@ -3003,6 +3029,13 @@ void MYSQL_BIN_LOG::init_pthread_objects() mysql_cond_init(m_key_update_cond, &update_cond, 0); mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0); mysql_cond_init(key_BINLOG_COND_xid_list, &COND_xid_list, 0); + + mysql_mutex_init(key_BINLOG_LOCK_binlog_background_thread, + &LOCK_binlog_background_thread, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_BINLOG_COND_binlog_background_thread, + &COND_binlog_background_thread, 0); + mysql_cond_init(key_BINLOG_COND_binlog_background_thread_end, + &COND_binlog_background_thread_end, 0); } @@ -3100,6 +3133,10 @@ bool MYSQL_BIN_LOG::open(const char *log_name, DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); + if (!is_relay_log && !binlog_background_thread_started && + start_binlog_background_thread()) + DBUG_RETURN(1); + if (init_and_set_log_file_name(log_name, new_name, log_type_arg, io_cache_type_arg)) { @@ -5548,18 +5585,14 @@ bool general_log_write(THD *thd, enum enum_server_command command, const char *query, uint query_length) { /* Write the message to the log if we want to log this king of commands */ - if (logger.log_command(thd, command)) + if (logger.log_command(thd, command) || mysql_audit_general_enabled()) return logger.general_log_write(thd, command, query, query_length); return FALSE; } -/* - I would like to make this function static, but this causes compiler warnings - when it is declared as friend function in log.h. -*/ -void +static void binlog_checkpoint_callback(void *cookie) { MYSQL_BIN_LOG::xid_count_per_binlog *entry= @@ -7398,8 +7431,9 @@ int TC_LOG_MMAP::open(const char *opt_name) syncing= 0; active=pages; + DBUG_ASSERT(npages >= 2); pool=pages+1; - pool_last=pages+npages-1; + pool_last_ptr= &((pages+npages-1)->next); commit_ordered_queue= NULL; commit_ordered_queue_busy= false; @@ -7432,8 +7466,8 @@ void TC_LOG_MMAP::get_active_from_pool() do { best_p= p= &pool; - if ((*p)->waiters == 0) // can the first page be used ? - break; // yes - take it. + if ((*p)->waiters == 0 && (*p)->free > 0) // can the first page be used ? + break; // yes - take it. best_free=0; // no - trying second strategy for (p=&(*p)->next; *p; p=&(*p)->next) @@ -7450,10 +7484,10 @@ void TC_LOG_MMAP::get_active_from_pool() mysql_mutex_assert_owner(&LOCK_active); active=*best_p; - if ((*best_p)->next) // unlink the page from the pool - *best_p=(*best_p)->next; - else - pool_last=*best_p; + /* Unlink the page from the pool. */ + if (!(*best_p)->next) + pool_last_ptr= best_p; + *best_p=(*best_p)->next; mysql_mutex_unlock(&LOCK_pool); mysql_mutex_lock(&active->lock); @@ -7560,12 +7594,9 @@ int TC_LOG_MMAP::log_one_transaction(my_xid xid) mysql_mutex_unlock(&LOCK_active); mysql_mutex_lock(&p->lock); p->waiters++; - for (;;) + while (p->state == PS_DIRTY && syncing) { - int not_dirty = p->state != PS_DIRTY; mysql_mutex_unlock(&p->lock); - if (not_dirty || !syncing) - break; mysql_cond_wait(&p->cond, &LOCK_sync); mysql_mutex_lock(&p->lock); } @@ -7617,8 +7648,8 @@ int TC_LOG_MMAP::sync() /* page is synced. let's move it to the pool */ mysql_mutex_lock(&LOCK_pool); - pool_last->next=syncing; - pool_last=syncing; + (*pool_last_ptr)=syncing; + pool_last_ptr=&(syncing->next); syncing->next=0; syncing->state= err ? PS_ERROR : PS_POOL; mysql_cond_signal(&COND_pool); // in case somebody's waiting @@ -8148,9 +8179,129 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid) void TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie) { - mark_xid_done(((xid_count_per_binlog *)cookie)->binlog_id, true); + xid_count_per_binlog *entry= static_cast<xid_count_per_binlog *>(cookie); + mysql_mutex_lock(&LOCK_binlog_background_thread); + entry->next_in_queue= binlog_background_thread_queue; + binlog_background_thread_queue= entry; + mysql_cond_signal(&COND_binlog_background_thread); + mysql_mutex_unlock(&LOCK_binlog_background_thread); } +/* + Binlog background thread. + + This thread is used to log binlog checkpoints in the background, rather than + in the context of random storage engine threads that happen to call + commit_checkpoint_notify_ha() and may not like the delays while syncing + binlog to disk or may not be setup with all my_thread_init() and other + necessary stuff. + + In the future, this thread could also be used to do log rotation in the + background, which could elimiate all stalls around binlog rotations. +*/ +pthread_handler_t +binlog_background_thread(void *arg __attribute__((unused))) +{ + bool stop; + MYSQL_BIN_LOG::xid_count_per_binlog *queue, *next; + THD *thd; + + my_thread_init(); + thd= new THD; + thd->system_thread= SYSTEM_THREAD_BINLOG_BACKGROUND; + thd->thread_stack= (char*) &thd; /* Set approximate stack start */ + mysql_mutex_lock(&LOCK_thread_count); + thd->thread_id= thread_id++; + mysql_mutex_unlock(&LOCK_thread_count); + thd->store_globals(); + + for (;;) + { + /* + Wait until there is something in the queue to process, or we are asked + to shut down. + */ + THD_STAGE_INFO(thd, stage_binlog_waiting_background_tasks); + mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread); + for (;;) + { + stop= binlog_background_thread_stop; + queue= binlog_background_thread_queue; + if (stop || queue) + break; + mysql_cond_wait(&mysql_bin_log.COND_binlog_background_thread, + &mysql_bin_log.LOCK_binlog_background_thread); + } + /* Grab the queue, if any. */ + binlog_background_thread_queue= NULL; + mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread); + + /* Process any incoming commit_checkpoint_notify() calls. */ + while (queue) + { + THD_STAGE_INFO(thd, stage_binlog_processing_checkpoint_notify); + /* Grab next pointer first, as mark_xid_done() may free the element. */ + next= queue->next_in_queue; + mysql_bin_log.mark_xid_done(queue->binlog_id, true); + queue= next; + + DBUG_EXECUTE_IF("binlog_background_checkpoint_processed", + DBUG_ASSERT(!debug_sync_set_action( + thd, + STRING_WITH_LEN("now SIGNAL binlog_background_checkpoint_processed"))); + ); + } + + if (stop) + break; + } + + THD_STAGE_INFO(thd, stage_binlog_stopping_background_thread); + + mysql_mutex_lock(&LOCK_thread_count); + delete thd; + mysql_mutex_unlock(&LOCK_thread_count); + + my_thread_end(); + + /* Signal that we are (almost) stopped. */ + mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread); + binlog_background_thread_stop= false; + mysql_cond_signal(&mysql_bin_log.COND_binlog_background_thread_end); + mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread); + + return 0; +} + +#ifdef HAVE_PSI_INTERFACE +static PSI_thread_key key_thread_binlog; + +static PSI_thread_info all_binlog_threads[]= +{ + { &key_thread_binlog, "binlog_background", PSI_FLAG_GLOBAL}, +}; +#endif /* HAVE_PSI_INTERFACE */ + +static bool +start_binlog_background_thread() +{ + pthread_t th; + +#ifdef HAVE_PSI_INTERFACE + if (PSI_server) + PSI_server->register_thread("sql", all_binlog_threads, + array_elements(all_binlog_threads)); +#endif + + if (mysql_thread_create(key_thread_binlog, &th, NULL, + binlog_background_thread, NULL)) + return 1; + + binlog_background_thread_started= true; + return 0; +} + + int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log, Format_description_log_event *fdle) diff --git a/sql/log.h b/sql/log.h index 3959411d83a..80fe34b5ff2 100644 --- a/sql/log.h +++ b/sql/log.h @@ -145,7 +145,7 @@ class TC_LOG_MMAP: public TC_LOG my_off_t file_length; uint npages, inited; uchar *data; - struct st_page *pages, *syncing, *active, *pool, *pool_last; + struct st_page *pages, *syncing, *active, *pool, **pool_last_ptr; /* note that, e.g. LOCK_active is only used to protect 'active' pointer, to protect the content of the active page @@ -395,8 +395,6 @@ private: #define BINLOG_COOKIE_IS_DUMMY(c) \ ( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID ) -void binlog_checkpoint_callback(void *cookie); - class binlog_cache_mngr; class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG { @@ -451,27 +449,6 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG }; /* - A list of struct xid_count_per_binlog is used to keep track of how many - XIDs are in prepared, but not committed, state in each binlog. And how - many commit_checkpoint_request()'s are pending. - - When count drops to zero in a binlog after rotation, it means that there - are no more XIDs in prepared state, so that binlog is no longer needed - for XA crash recovery, and we can log a new binlog checkpoint event. - - The list is protected against simultaneous access from multiple - threads by LOCK_xid_list. - */ - struct xid_count_per_binlog : public ilink { - char *binlog_name; - uint binlog_name_len; - ulong binlog_id; - /* Total prepared XIDs and pending checkpoint requests in this binlog. */ - long xid_count; - xid_count_per_binlog(); /* Give link error if constructor used. */ - }; - I_List<xid_count_per_binlog> binlog_xid_count_list; - /* When this is set, a RESET MASTER is in progress. Then we should not write any binlog checkpoints into the binlog (that @@ -480,7 +457,6 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG checkpoint arrives - when all have arrived, RESET MASTER will complete. */ bool reset_master_pending; - friend void binlog_checkpoint_callback(void *cookie); /* LOCK_log and LOCK_index are inited by init_pthread_objects() */ mysql_mutex_t LOCK_index; @@ -550,10 +526,35 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG int write_transaction_or_stmt(group_commit_entry *entry); bool write_transaction_to_binlog_events(group_commit_entry *entry); void trx_group_commit_leader(group_commit_entry *leader); - void mark_xid_done(ulong cookie, bool write_checkpoint); - void mark_xids_active(ulong cookie, uint xid_count); public: + /* + A list of struct xid_count_per_binlog is used to keep track of how many + XIDs are in prepared, but not committed, state in each binlog. And how + many commit_checkpoint_request()'s are pending. + + When count drops to zero in a binlog after rotation, it means that there + are no more XIDs in prepared state, so that binlog is no longer needed + for XA crash recovery, and we can log a new binlog checkpoint event. + + The list is protected against simultaneous access from multiple + threads by LOCK_xid_list. + */ + struct xid_count_per_binlog : public ilink { + char *binlog_name; + uint binlog_name_len; + ulong binlog_id; + /* Total prepared XIDs and pending checkpoint requests in this binlog. */ + long xid_count; + /* For linking in requests to the binlog background thread. */ + xid_count_per_binlog *next_in_queue; + xid_count_per_binlog(); /* Give link error if constructor used. */ + }; + I_List<xid_count_per_binlog> binlog_xid_count_list; + mysql_mutex_t LOCK_binlog_background_thread; + mysql_cond_t COND_binlog_background_thread; + mysql_cond_t COND_binlog_background_thread_end; + using MYSQL_LOG::generate_name; using MYSQL_LOG::is_open; @@ -709,6 +710,8 @@ public: bool appendv(const char* buf,uint len,...); bool append(Log_event* ev); + void mark_xids_active(ulong cookie, uint xid_count); + void mark_xid_done(ulong cookie, bool write_checkpoint); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); bool can_purge_log(const char *log_file_name); diff --git a/sql/log_event.cc b/sql/log_event.cc index 13de7523054..fb9e6b7107f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -9881,23 +9881,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; /* @@ -10986,8 +10969,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/mysqld.cc b/sql/mysqld.cc index 28e2c43ca43..ea38d61cca2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -747,6 +747,7 @@ PSI_mutex_key key_LOCK_des_key_file; #endif /* HAVE_OPENSSL */ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, + key_BINLOG_LOCK_binlog_background_thread, key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, @@ -789,6 +790,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0}, { &key_BINLOG_LOCK_xid_list, "MYSQL_BIN_LOG::LOCK_xid_list", 0}, + { &key_BINLOG_LOCK_binlog_background_thread, "MYSQL_BIN_LOG::LOCK_binlog_background_thread", 0}, { &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0}, { &key_delayed_insert_mutex, "Delayed_insert::mutex", 0}, { &key_hash_filo_lock, "hash_filo::lock", 0}, @@ -857,6 +859,8 @@ PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; #endif /* HAVE_MMAP */ PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, + key_BINLOG_COND_binlog_background_thread, + key_BINLOG_COND_binlog_background_thread_end, key_COND_cache_status_changed, key_COND_manager, key_COND_rpl_status, key_COND_server_started, key_delayed_insert_cond, key_delayed_insert_cond_client, @@ -886,6 +890,8 @@ static PSI_cond_info all_server_conds[]= #endif /* HAVE_MMAP */ { &key_BINLOG_COND_xid_list, "MYSQL_BIN_LOG::COND_xid_list", 0}, { &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0}, + { &key_BINLOG_COND_binlog_background_thread, "MYSQL_BIN_LOG::COND_binlog_background_thread", 0}, + { &key_BINLOG_COND_binlog_background_thread_end, "MYSQL_BIN_LOG::COND_binlog_background_thread_end", 0}, { &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0}, { &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0}, { &key_RELAYLOG_COND_queue_busy, "MYSQL_RELAY_LOG::COND_queue_busy", 0}, @@ -7259,8 +7265,8 @@ SHOW_VAR status_vars[]= { {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}, {"Opened_views", (char*) offsetof(STATUS_VAR, opened_views), SHOW_LONG_STATUS}, {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_SIMPLE_FUNC}, - {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS}, {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS}, + {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS}, {"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, @@ -8820,6 +8826,9 @@ PSI_stage_info stage_slave_waiting_worker_to_release_partition= { 0, "Waiting fo PSI_stage_info stage_slave_waiting_worker_to_free_events= { 0, "Waiting for Slave Workers to free pending events", 0}; PSI_stage_info stage_slave_waiting_worker_queue= { 0, "Waiting for Slave Worker queue", 0}; PSI_stage_info stage_slave_waiting_event_from_coordinator= { 0, "Waiting for an event from Coordinator", 0}; +PSI_stage_info stage_binlog_waiting_background_tasks= { 0, "Waiting for background binlog tasks", 0}; +PSI_stage_info stage_binlog_processing_checkpoint_notify= { 0, "Processing binlog checkpoint notification", 0}; +PSI_stage_info stage_binlog_stopping_background_thread= { 0, "Stopping binlog background thread", 0}; #ifdef HAVE_PSI_INTERFACE diff --git a/sql/mysqld.h b/sql/mysqld.h index 7da96ca932e..a07efa9178f 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -230,6 +230,7 @@ extern PSI_mutex_key key_LOCK_des_key_file; #endif extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, + key_BINLOG_LOCK_binlog_background_thread, key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, @@ -261,6 +262,8 @@ extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; #endif /* HAVE_MMAP */ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, + key_BINLOG_COND_binlog_background_thread, + key_BINLOG_COND_binlog_background_thread_end, key_COND_cache_status_changed, key_COND_manager, key_COND_rpl_status, key_COND_server_started, key_delayed_insert_cond, key_delayed_insert_cond_client, @@ -402,6 +405,9 @@ extern PSI_stage_info stage_slave_waiting_worker_to_free_events; extern PSI_stage_info stage_slave_waiting_worker_queue; extern PSI_stage_info stage_slave_waiting_event_from_coordinator; extern PSI_stage_info stage_slave_waiting_workers_to_exit; +extern PSI_stage_info stage_binlog_waiting_background_tasks; +extern PSI_stage_info stage_binlog_processing_checkpoint_notify; +extern PSI_stage_info stage_binlog_stopping_background_thread; #ifdef HAVE_PSI_STATEMENT_INTERFACE /** Statement instrumentation keys (sql). diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 67bc1639f2d..8aeb89ed9ed 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -14137,11 +14137,3 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) #endif /* !DBUG_OFF */ -/***************************************************************************** -** Instantiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<QUICK_RANGE>; -template class List_iterator<QUICK_RANGE>; -#endif diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 8c01e979226..364dfb3bc20 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -59,6 +59,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) { DBUG_ENTER("Relay_log_info::Relay_log_info"); + relay_log.is_relay_log= TRUE; #ifdef HAVE_PSI_INTERFACE relay_log.set_psi_keys(key_RELAYLOG_LOCK_index, key_RELAYLOG_update_cond, @@ -217,8 +218,6 @@ a file name for --relay-log-index option", opt_relaylog_index_name); &mi->connection_name); } - rli->relay_log.is_relay_log= TRUE; - /* note, that if open() fails, we'll still have index file open but a destructor will take care of that diff --git a/sql/slave.cc b/sql/slave.cc index e1bac640e00..95818e60426 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5693,11 +5693,6 @@ bool rpl_master_erroneous_autoinc(THD *thd) return FALSE; } -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class I_List_iterator<i_string>; -template class I_List_iterator<i_string_pair>; -#endif - /** @} (end of group Replication) */ diff --git a/sql/sp.cc b/sql/sp.cc index db180bde4b5..f6f5d640bc0 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 2ab5f96d4cb..b725f6d7fcc 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2425,7 +2425,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, @@ -2442,8 +2441,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; } @@ -3112,8 +3110,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) (the order of query cache and subst_spvars calls is irrelevant because queries with SP vars can't be cached) */ - if (unlikely((thd->variables.option_bits & OPTION_LOG_OFF)==0)) - general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); + general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); if (query_cache_send_result_to_client(thd, thd->query(), thd->query_length()) <= 0) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7e8a6a93d57..723d825de17 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1634,14 +1634,20 @@ ulong acl_get(const char *host, const char *ip, acl_entry *entry; DBUG_ENTER("acl_get"); - mysql_mutex_lock(&acl_cache->lock); - end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db); + tmp_db= strmov(strmov(key, ip ? ip : "") + 1, user) + 1; + end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db); + + if (end >= key + sizeof(key)) // db name was truncated + DBUG_RETURN(0); // no privileges for an invalid db name + if (lower_case_table_names) { my_casedn_str(files_charset_info, tmp_db); db=tmp_db; } key_length= (size_t) (end-key); + + mysql_mutex_lock(&acl_cache->lock); if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key, key_length))) { @@ -4888,11 +4894,17 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash) bool check_grant_db(THD *thd,const char *db) { Security_context *sctx= thd->security_ctx; - char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2]; + char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end; uint len; bool error= TRUE; - len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1; + end= strmov(helping, sctx->priv_user) + 1; + end= strnmov(end, db, helping + sizeof(helping) - end); + + if (end >= helping + sizeof(helping)) // db name was truncated + return 1; // no privileges for an invalid db name + + len= (uint) (end - helping) + 1; mysql_rwlock_rdlock(&LOCK_grant); @@ -7013,17 +7025,6 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, } -/***************************************************************************** - Instantiate used templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List_iterator<LEX_COLUMN>; -template class List_iterator<LEX_USER>; -template class List<LEX_COLUMN>; -template class List<LEX_USER>; -#endif - /** Validate if a user can proxy as another user diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 51c695d091d..b2ce31f1d26 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -37,8 +37,16 @@ extern void mysql_audit_acquire_plugins(THD *thd, uint event_class); #ifndef EMBEDDED_LIBRARY extern void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...); + +static inline bool mysql_audit_general_enabled() +{ + return mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK; +} + #else -#define mysql_audit_notify(...) +static inline void mysql_audit_notify(THD *thd, uint event_class, + uint event_subtype, ...) { } +#define mysql_audit_general_enabled() 0 #endif extern void mysql_audit_release(THD *thd); @@ -72,8 +80,7 @@ void mysql_audit_general_log(THD *thd, time_t time, const char *cmd, uint cmdlen, const char *query, uint querylen) { -#ifndef EMBEDDED_LIBRARY - if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK) + if (mysql_audit_general_enabled()) { CHARSET_INFO *clientcs= thd ? thd->variables.character_set_client : global_system_variables.character_set_client; @@ -82,7 +89,6 @@ void mysql_audit_general_log(THD *thd, time_t time, 0, time, user, userlen, cmd, cmdlen, query, querylen, clientcs, 0); } -#endif } /** @@ -101,8 +107,7 @@ static inline void mysql_audit_general(THD *thd, uint event_subtype, int error_code, const char *msg) { -#ifndef EMBEDDED_LIBRARY - if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK) + if (mysql_audit_general_enabled()) { time_t time= my_time(0); uint msglen= msg ? strlen(msg) : 0; @@ -130,7 +135,6 @@ void mysql_audit_general(THD *thd, uint event_subtype, error_code, time, user, userlen, msg, msglen, query.str(), query.length(), query.charset(), rows); } -#endif } #define MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd) mysql_audit_notify(\ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index db926061406..834e5ac0046 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8852,34 +8852,33 @@ err_no_arena: ******************************************************************************/ -/* - Fill fields with given items. +/** + Fill the fields of a table with the values of an Item list - SYNOPSIS - fill_record() - thd thread handler - fields Item_fields list to be filled - values values to fill with - ignore_errors TRUE if we should ignore errors + @param thd thread handler + @param table_arg the table that is being modified + @param fields Item_fields list to be filled + @param values values to fill with + @param ignore_errors TRUE if we should ignore errors - NOTE + @details fill_record() may set table->auto_increment_field_not_null and a caller should make sure that it is reset after their last call to this function. - RETURN - FALSE OK - TRUE error occured + @return Status + @retval true An error occured. + @retval false OK. */ 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"); @@ -8901,12 +8900,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())) @@ -8916,7 +8916,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 && @@ -8934,13 +8934,16 @@ 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; } /* Update virtual fields*/ thd->abort_on_warning= FALSE; if (vcol_table && vcol_table->vfield && - update_virtual_fields(thd, vcol_table, TRUE)) + update_virtual_fields(thd, vcol_table, + vcol_table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; @@ -8948,8 +8951,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); } @@ -8958,42 +8961,39 @@ err: Fill fields in list with values from the list of items and invoke before triggers. - SYNOPSIS - fill_record_n_invoke_before_triggers() - thd thread context - fields Item_fields list to be filled - values values to fill with - ignore_errors TRUE if we should ignore errors - triggers object holding list of triggers to be invoked - event event type for triggers to be invoked + @param thd thread context + @param table the table that is being modified + @param fields Item_fields list to be filled + @param values values to fill with + @param ignore_errors TRUE if we should ignore errors + @param event event type for triggers to be invoked - NOTE + @detail This function assumes that fields which values will be set and triggers to be invoked belong to the same table, and that TABLE::record[0] and record[1] buffers correspond to new and old versions of row respectively. - RETURN - FALSE OK - TRUE error occured + @return Status + @retval true An error occured. + @retval false OK. */ 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))); /* Re-calculate virtual fields to cater for cases when base columns are updated by the triggers. */ - if (!result && triggers) + if (!result && triggers && table) { - TABLE *table= 0; List_iterator_fast<Item> f(fields); Item *fld; Item_field *item_field; @@ -9001,45 +9001,46 @@ 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) - result= update_virtual_fields(thd, table, TRUE); + if (item_field && item_field->field && table && table->vfield) + { + DBUG_ASSERT(table == item_field->field->table); + result= update_virtual_fields(thd, table, + table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_WRITE); + } } } return result; } -/* - Fill field buffer with values from Field list +/** + Fill the field buffer of a table with the values of an Item list - SYNOPSIS - fill_record() - thd thread handler - ptr pointer on pointer to record - values list of fields - ignore_errors TRUE if we should ignore errors - use_value forces usage of value of the items instead of result + @param thd thread handler + @param table_arg the table that is being modified + @param ptr pointer on pointer to record of fields + @param values values to fill with + @param ignore_errors TRUE if we should ignore errors + @param use_value forces usage of value of the items instead of result - NOTE + @details fill_record() may set table->auto_increment_field_not_null and a caller should make sure that it is reset after their last call to this function. - RETURN - FALSE OK - TRUE error occured + @return Status + @retval true An error occured. + @retval false OK. */ 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"); @@ -9054,7 +9055,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 @@ -9085,10 +9086,14 @@ 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; - if (table->vfield && update_virtual_fields(thd, table, TRUE)) + if (table->vfield && + update_virtual_fields(thd, table, + table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); @@ -9101,36 +9106,34 @@ err: /* - Fill fields in array with values from the list of items and invoke + Fill fields in an array with values from the list of items and invoke before triggers. - SYNOPSIS - fill_record_n_invoke_before_triggers() - thd thread context - ptr NULL-ended array of fields to be filled - values values to fill with - ignore_errors TRUE if we should ignore errors - triggers object holding list of triggers to be invoked - event event type for triggers to be invoked + @param thd thread context + @param table the table that is being modified + @param ptr the fields to be filled + @param values values to fill with + @param ignore_errors TRUE if we should ignore errors + @param event event type for triggers to be invoked - NOTE + @detail This function assumes that fields which values will be set and triggers to be invoked belong to the same table, and that TABLE::record[0] and record[1] buffers correspond to new and old versions of row respectively. - RETURN - FALSE OK - TRUE error occured + @return Status + @retval true An error occured. + @retval false OK. */ 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))); /* @@ -9139,9 +9142,11 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, */ if (!result && triggers && *ptr) { - TABLE *table= (*ptr)->table; + DBUG_ASSERT(table == (*ptr)->table); if (table->vfield) - result= update_virtual_fields(thd, table, TRUE); + result= update_virtual_fields(thd, table, + table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_WRITE); } return result; @@ -9779,11 +9784,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..af63aefa7f9 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 * @@ -303,7 +303,8 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, bool no_error); 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_virtual_fields(THD *thd, TABLE *table, + enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ); int dynamic_column_error_message(enum_dyncol_func_result rc); extern TABLE *unused_tables; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index fc5653750f3..cbb798d96a4 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -73,23 +73,6 @@ char empty_c_string[1]= {0}; /* used for not defined db */ const char * const THD::DEFAULT_WHERE= "field list"; - -/***************************************************************************** -** Instansiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -/* Used templates */ -template class List<Key>; -template class List_iterator<Key>; -template class List<Key_part_spec>; -template class List_iterator<Key_part_spec>; -template class List<Alter_drop>; -template class List_iterator<Alter_drop>; -template class List<Alter_column>; -template class List_iterator<Alter_column>; -#endif - /**************************************************************************** ** User variables ****************************************************************************/ @@ -5183,27 +5166,6 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id, DBUG_RETURN(pending); /* This is the current pending event */ } -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -/* - Instantiate the versions we need, we have -fno-implicit-template as - compiling option. -*/ -template Rows_log_event* -THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*, - size_t, size_t, bool, - Write_rows_log_event*); - -template Rows_log_event* -THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*, - size_t colcnt, size_t, bool, - Delete_rows_log_event *); - -template Rows_log_event* -THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*, - size_t colcnt, size_t, bool, - Update_rows_log_event *); -#endif - /* Declare in unnamed namespace. */ CPP_UNNAMED_NS_START /** diff --git a/sql/sql_class.h b/sql/sql_class.h index 3dbccf9b41e..8df3a2eee36 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1272,7 +1272,8 @@ enum enum_thread_type SYSTEM_THREAD_SLAVE_SQL= 4, SYSTEM_THREAD_NDBCLUSTER_BINLOG= 8, SYSTEM_THREAD_EVENT_SCHEDULER= 16, - SYSTEM_THREAD_EVENT_WORKER= 32 + SYSTEM_THREAD_EVENT_WORKER= 32, + SYSTEM_THREAD_BINLOG_BACKGROUND= 64 }; inline char const * @@ -4365,6 +4366,16 @@ public: */ #define CF_FORCE_ORIGINAL_BINLOG_FORMAT (1U << 16) +/** + Statement that inserts new rows (INSERT, REPLACE, LOAD, ALTER TABLE) +*/ +#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_delete.cc b/sql/sql_delete.cc index 0f07f01c6fd..09ddedecc75 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -330,7 +330,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ! thd->is_error()) { if (table->vfield) - update_virtual_fields(thd, table); + update_virtual_fields(thd, table, + table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); // thd->is_error() is tested to disallow delete row on error if (!select || select->skip_record(thd) > 0) 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 25c696bc476..67f3955ea61 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) { @@ -716,7 +675,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; @@ -916,8 +875,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()) @@ -961,8 +919,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()) @@ -974,6 +931,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 ? @@ -990,7 +952,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 @@ -1680,9 +1644,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } } key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0); + key_part_map keypart_map= (1 << table->key_info[key_nr].key_parts) - 1; if ((error= (table->file->ha_index_read_idx_map(table->record[1], key_nr, (uchar*) key, - HA_WHOLE_KEY, + keypart_map, HA_READ_KEY_EXACT)))) goto err; } @@ -1699,13 +1664,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 if 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)) == @@ -1716,7 +1700,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])) && @@ -1787,8 +1771,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], @@ -1951,7 +1933,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; @@ -2324,7 +2305,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= 0; TABLE *copy; TABLE_SHARE *share; uchar *bitmap; @@ -2393,6 +2374,14 @@ 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**)); + if (!copy->default_field) + goto error; + dfield_ptr= copy->default_field; + } /* Make a copy of all fields. The copied fields need to point into the copied record. This is done @@ -2410,18 +2399,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; @@ -2511,7 +2501,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. @@ -3053,7 +3042,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. */ @@ -3523,6 +3511,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()) { @@ -3582,11 +3572,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) @@ -3804,7 +3794,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, "", ""); @@ -3813,6 +3802,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; @@ -4052,8 +4043,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 @@ -4126,8 +4115,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); } @@ -4236,16 +4225,3 @@ void select_create::abort_result_set() DBUG_VOID_RETURN; } - -/***************************************************************************** - Instansiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List_iterator_fast<List_item>; -#ifndef EMBEDDED_LIBRARY -template class I_List<Delayed_insert>; -template class I_List_iterator<Delayed_insert>; -template class I_List<delayed_row>; -#endif /* EMBEDDED_LIBRARY */ -#endif /* HAVE_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 975f0c77349..2c9a55208fc 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4445,6 +4445,3 @@ void binlog_unsafe_map_init() } #endif -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class Mem_root_array<ORDER*, true>; -#endif diff --git a/sql/sql_load.cc b/sql/sql_load.cc index daa9298d52d..38853eb8b6d 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); @@ -846,8 +833,12 @@ 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_timestamp*) field)->set_time(); + field->set_time(); } else { @@ -862,6 +853,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if ((pos+=length) > read_info.row_end) pos= read_info.row_end; /* Fills rest with space */ } + /* Do not auto-update this field. */ + field->set_has_explicit_value(); } if (pos != read_info.row_end) { @@ -873,10 +866,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, @@ -993,12 +986,18 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, field->set_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_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); } + /* Do not auto-update this field. */ + field->set_has_explicit_value(); } else if (item->type() == Item::STRING_ITEM) { @@ -1022,6 +1021,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_has_explicit_value(); } else if (item->type() == Item::STRING_ITEM) { @@ -1063,7 +1063,8 @@ 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(); + 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 @@ -1090,10 +1091,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, @@ -1203,11 +1204,13 @@ 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); } + /* Do not auto-update this field. */ + field->set_has_explicit_value(); } else ((Item_user_var_as_out_param *) item)->set_null_value(cs); @@ -1222,6 +1225,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_has_explicit_value(); } else ((Item_user_var_as_out_param *) item)->set_value( @@ -1266,10 +1270,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 36447a1836d..14601f99138 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -270,12 +270,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; @@ -294,19 +296,23 @@ void init_update_queries(void) sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED; + CF_CAN_BE_EXPLAINED | + CF_UPDATES_DATA; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED; + CF_CAN_BE_EXPLAINED | + CF_UPDATES_DATA; sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED; + CF_CAN_BE_EXPLAINED | + CF_INSERTS_DATA; sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED; + CF_CAN_BE_EXPLAINED | + CF_INSERTS_DATA; sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -318,11 +324,13 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED;; + CF_CAN_BE_EXPLAINED | + CF_INSERTS_DATA;; sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED; + CF_CAN_BE_EXPLAINED | + CF_INSERTS_DATA; sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -6175,6 +6183,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, { register Create_field *new_field; LEX *lex= thd->lex; + uint8 datetime_precision= length ? atoi(length) : 0; DBUG_ENTER("add_field_to_list"); if (check_string_char_length(field_name, "", NAME_CHAR_LEN, @@ -6211,11 +6220,13 @@ 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)) + (static_cast<Item_func*>(default_value)->functype() != + Item_func::NOW_FUNC || + (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME) || + default_value->decimals < datetime_precision)) { my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); DBUG_RETURN(1); @@ -6237,7 +6248,9 @@ 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 && + (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME || + on_update_value->decimals < datetime_precision)) { my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str); DBUG_RETURN(1); @@ -7097,8 +7110,10 @@ void sql_kill(THD *thd, ulong id, killed_state state) uint error; if (!(error= kill_one_thread(thd, id, state))) { - if (! thd->killed) + if ((!thd->killed)) my_ok(thd); + else + my_error(killed_errno(thd->killed), MYF(0), id); } else my_error(error, MYF(0), id); 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_prepare.cc b/sql/sql_prepare.cc index 969e6ee4574..24dde140a8a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2508,14 +2508,24 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) */ if (sl->prep_where) { - sl->where= sl->prep_where->copy_andor_structure(thd); + /* + We need this rollback because memory allocated in + copy_andor_structure() will be freed + */ + thd->change_item_tree((Item**)&sl->where, + sl->prep_where->copy_andor_structure(thd)); sl->where->cleanup(); } else sl->where= NULL; if (sl->prep_having) { - sl->having= sl->prep_having->copy_andor_structure(thd); + /* + We need this rollback because memory allocated in + copy_andor_structure() will be freed + */ + thd->change_item_tree((Item**)&sl->having, + sl->prep_having->copy_andor_structure(thd)); sl->having->cleanup(); } else diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8f565cb3d32..90302897a49 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1291,11 +1291,9 @@ JOIN::optimize_inner() DBUG_RETURN(1); // error == -1 } if (const_table_map != found_const_table_map && - !(select_options & SELECT_DESCRIBE) && - (!conds || - !(conds->used_tables() & RAND_TABLE_BIT) || - select_lex->master_unit() == &thd->lex->unit)) // upper level SELECT + !(select_options & SELECT_DESCRIBE)) { + // There is at least one empty const table zero_result_cause= "no matching row in const table"; DBUG_PRINT("error",("Error: %s", zero_result_cause)); error= 0; @@ -2143,6 +2141,7 @@ JOIN::reinit() ULL(0)); first_record= 0; + cleaned= false; if (exec_tmp_table1) { @@ -5513,7 +5512,8 @@ best_access_path(JOIN *join, !ref_or_null_part) { /* use eq key */ max_key_part= (uint) ~0; - if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) + if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME || + test(key_flags & HA_EXT_NOSAME)) { tmp = prev_record_reads(join->positions, idx, found_ref); records=1.0; @@ -8111,18 +8111,23 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, *ref_key=0; // end_marker if (j->type == JT_FT) DBUG_RETURN(0); + ulong key_flags= j->table->actual_key_flags(keyinfo); if (j->type == JT_CONST) j->table->const_table= 1; - else if (((j->table->actual_key_flags(keyinfo) & - (HA_NOSAME | HA_NULL_PART_KEY)) - != HA_NOSAME) || + else if (((key_flags & (HA_NOSAME | HA_NULL_PART_KEY))!= HA_NOSAME) || keyparts != j->table->actual_n_key_parts(keyinfo) || null_ref_key) { - /* Must read with repeat */ - j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF; - j->ref.null_ref_key= null_ref_key; - j->ref.null_ref_part= null_ref_part; + if (test(key_flags & HA_EXT_NOSAME) && keyparts == keyinfo->ext_key_parts && + !null_ref_key) + j->type= JT_EQ_REF; + else + { + /* Must read with repeat */ + j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF; + j->ref.null_ref_key= null_ref_key; + j->ref.null_ref_part= null_ref_part; + } } else if (keyuse_uses_no_tables) { @@ -9283,7 +9288,7 @@ void JOIN::drop_unused_derived_keys() JOIN_TAB *tab; for (tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab; - tab= next_linear_tab(this, tab, WITHOUT_BUSH_ROOTS)) + tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { TABLE *table=tab->table; @@ -9571,7 +9576,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]))) @@ -10774,6 +10779,7 @@ void JOIN::cleanup(bool full) { tab->cleanup(); } + cleaned= true; } else { @@ -11247,14 +11253,6 @@ public: COND_CMP(Item *a,Item_func *b) :and_level(a),cmp_func(b) {} }; -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class I_List<COND_CMP>; -template class I_List_iterator<COND_CMP>; -template class List<Item_func_match>; -template class List_iterator<Item_func_match>; -#endif - - /** Find the multiple equality predicate containing a field. @@ -16622,6 +16620,17 @@ int safe_index_read(JOIN_TAB *tab) } +/** + Reads content of constant table + + @param tab table + @param pos position of table in query plan + + @retval 0 ok, one row was found or one NULL-complemented row was created + @retval -1 ok, no row was found and no NULL-complemented row was created + @retval 1 error +*/ + static int join_read_const_table(JOIN_TAB *tab, POSITION *pos) { @@ -16740,6 +16749,16 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) } +/** + Read a constant table when there is at most one matching row, using a table + scan. + + @param tab Table to read + + @retval 0 Row was found + @retval -1 Row was not found + @retval 1 Got an error (other than row not found) during read +*/ static int join_read_system(JOIN_TAB *tab) { @@ -16772,12 +16791,9 @@ join_read_system(JOIN_TAB *tab) @param tab Table to read - @retval - 0 Row was found - @retval - -1 Row was not found - @retval - 1 Got an error (other than row not found) during read + @retval 0 Row was found + @retval -1 Row was not found + @retval 1 Got an error (other than row not found) during read */ static int @@ -18638,15 +18654,18 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, */ if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || - quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || + quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) - goto use_filesort; - ref_key= select->quick->index; - ref_key_parts= select->quick->used_key_parts; + ref_key= MAX_KEY; + else + { + ref_key= select->quick->index; + ref_key_parts= select->quick->used_key_parts; + } } - if (ref_key >= 0) + if (ref_key >= 0 && ref_key != MAX_KEY) { /* We come here when there is a REF key. @@ -19170,49 +19189,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, *(join->pre_sort_join_tab)= *tab; - /*TODO: here, close the index scan, cancel index-only read. */ -#if 0 - /* MariaDB doesn't need the following: */ - if (select) - { - /* - We need to preserve tablesort's output resultset here, because - QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by - SQL_SELECT::cleanup()) may free it assuming it's the result of the quick - select operation that we no longer need. Note that all the other parts of - this data structure are cleaned up when - QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next - SQL_SELECT::cleanup() call changes sort.io_cache alone. - */ - IO_CACHE *tablesort_result_cache; - - tablesort_result_cache= table->sort.io_cache; - table->sort.io_cache= NULL; - // select->cleanup(); // filesort did select - /* - If a quick object was created outside of create_sort_index() - that might be reused, then do not call select->cleanup() since - it will delete the quick object. - */ - if (!keep_quick) - { - select->cleanup(); - /* - The select object should now be ready for the next use. If it - is re-used then there exists a backup copy of this join tab - which has the pointer to it. The join tab will be restored in - JOIN::reset(). So here we just delete the pointer to it. - */ - tab->select= NULL; - // If we deleted the quick select object we need to clear quick_keys - table->quick_keys.clear_all(); - table->intersect_keys.clear_all(); - } - // Restore the output resultset - table->sort.io_cache= tablesort_result_cache; - } -#endif tab->select=NULL; tab->set_select_cond(NULL, __LINE__); tab->type=JT_ALL; // Read with normal read_record @@ -19236,34 +19213,13 @@ void JOIN::clean_pre_sort_join_tab() the table already deleted by st_select_lex_unit::cleanup(). We rely on that fake_select_lex didn't have quick select. */ -#if 0 - if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick) - { - /* - We need to preserve tablesort's output resultset here, because - QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by - SQL_SELECT::cleanup()) may free it assuming it's the result of the quick - select operation that we no longer need. Note that all the other parts of - this data structure are cleaned up when - QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next - SQL_SELECT::cleanup() call changes sort.io_cache alone. - */ - IO_CACHE *tablesort_result_cache; - - tablesort_result_cache= table->sort.io_cache; - table->sort.io_cache= NULL; - pre_sort_join_tab->select->cleanup(); - table->quick_keys.clear_all(); // as far as we cleanup select->quick - table->intersect_keys.clear_all(); - table->sort.io_cache= tablesort_result_cache; - } -#endif - //table->disable_keyread(); // Restore if we used indexes if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick) { pre_sort_join_tab->select->cleanup(); } } + + /***************************************************************************** Remove duplicates from tmp table This should be recoded to add a unique index to the table and remove @@ -20744,40 +20700,66 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array, res_selected_fields.empty(); res_all_fields.empty(); - uint i, border= all_fields.elements - elements; - for (i= 0; (item= it++); i++) + uint border= all_fields.elements - elements; + for (uint i= 0; (item= it++); i++) { Field *field; - - if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) || - (item->type() == Item::FUNC_ITEM && - ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC)) + if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) item_field= item; - else + else if (item->type() == Item::FIELD_ITEM) + item_field= item->get_tmp_table_item(thd); + else if (item->type() == Item::FUNC_ITEM && + ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC) { - if (item->type() == Item::FIELD_ITEM) + field= item->get_tmp_table_field(); + if (field != NULL) { - item_field= item->get_tmp_table_item(thd); + /* + Replace "@:=<expression>" with "@:=<tmp table column>". Otherwise, + we would re-evaluate <expression>, and if expression were + a subquery, this would access already-unlocked tables. + */ + Item_func_set_user_var* suv= + new Item_func_set_user_var((Item_func_set_user_var*) item); + Item_field *new_field= new Item_field(field); + if (!suv || !new_field || suv->fix_fields(thd, (Item**)&suv)) + DBUG_RETURN(true); // Fatal error + ((Item *)suv)->name= item->name; + /* + We are replacing the argument of Item_func_set_user_var after its + value has been read. The argument's null_value should be set by + now, so we must set it explicitly for the replacement argument + since the null_value may be read without any preceeding call to + val_*(). + */ + new_field->update_null_value(); + List<Item> list; + list.push_back(new_field); + suv->set_arguments(list); + item_field= suv; } - else if ((field= item->get_tmp_table_field())) + else + item_field= item; + } + else if ((field= item->get_tmp_table_field())) + { + if (item->type() == Item::SUM_FUNC_ITEM && field->table->group) + item_field= ((Item_sum*) item)->result_item(field); + else + item_field= (Item*) new Item_field(field); + if (!item_field) + DBUG_RETURN(true); // Fatal error + + if (item->real_item()->type() != Item::FIELD_ITEM) + field->orig_table= 0; + item_field->name= item->name; + if (item->type() == Item::REF_ITEM) { - if (item->type() == Item::SUM_FUNC_ITEM && field->table->group) - item_field= ((Item_sum*) item)->result_item(field); - else - item_field= (Item*) new Item_field(field); - if (!item_field) - DBUG_RETURN(TRUE); // Fatal error - - if (item->real_item()->type() != Item::FIELD_ITEM) - field->orig_table= 0; - item_field->name= item->name; - if (item->type() == Item::REF_ITEM) - { - Item_field *ifield= (Item_field *) item_field; - Item_ref *iref= (Item_ref *) item; - ifield->table_name= iref->table_name; - ifield->db_name= iref->db_name; - } + Item_field *ifield= (Item_field *) item_field; + Item_ref *iref= (Item_ref *) item; + ifield->table_name= iref->table_name; + ifield->db_name= iref->db_name; + } #ifndef DBUG_OFF if (!item_field->name) { @@ -20789,20 +20771,20 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array, item_field->name= sql_strmake(str.ptr(),str.length()); } #endif - } - else - item_field= item; } + else + item_field= item; + res_all_fields.push_back(item_field); ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]= item_field; } List_iterator_fast<Item> itr(res_all_fields); - for (i= 0; i < border; i++) + for (uint i= 0; i < border; i++) itr++; itr.sublist(res_selected_fields, elements); - DBUG_RETURN(FALSE); + DBUG_RETURN(false); } @@ -22613,6 +22595,17 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) str->append(STRING_WITH_LEN("select ")); + if (join && join->cleaned) + { + /* + JOIN already cleaned up so it is dangerous to print items + because temporary tables they pointed on could be freed. + */ + str->append('#'); + str->append(select_number); + return; + } + /* First add options */ if (options & SELECT_STRAIGHT_JOIN) str->append(STRING_WITH_LEN("straight_join ")); @@ -23080,6 +23073,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, ha_rows table_records= table->file->stats.records; bool group= join && join->group && order == join->group_list; ha_rows ref_key_quick_rows= HA_POS_ERROR; + const bool has_limit= (select_limit_arg != HA_POS_ERROR); /* If not used with LIMIT, only use keys if the whole query can be @@ -23104,7 +23098,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, else keys= usable_keys; - if (ref_key >= 0 && table->covering_keys.is_set(ref_key)) + if (ref_key >= 0 && ref_key != MAX_KEY && + table->covering_keys.is_set(ref_key)) ref_key_quick_rows= table->quick_rows[ref_key]; if (join) @@ -23211,7 +23206,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, be included into the result set. */ if (select_limit > table_records/rec_per_key) - select_limit= table_records; + select_limit= table_records; else select_limit= (ha_rows) (select_limit*rec_per_key); } /* group */ @@ -23293,7 +23288,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, *new_key= best_key; *new_key_direction= best_key_direction; - *new_select_limit= best_select_limit; + *new_select_limit= has_limit ? best_select_limit : table_records; if (new_used_key_parts != NULL) *new_used_key_parts= best_key_parts; diff --git a/sql/sql_select.h b/sql/sql_select.h index 8420a223fb6..54aca3c4829 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -896,6 +896,14 @@ protected: public: JOIN_TAB *join_tab, **best_ref; + /* + Saved join_tab for pre_sorting. create_sort_index() will save here.. + */ + JOIN_TAB *pre_sort_join_tab; + uint pre_sort_index; + Item *pre_sort_idx_pushed_cond; + void clean_pre_sort_join_tab(); + /* For "Using temporary+Using filesort" queries, JOIN::join_tab can point to either: @@ -908,15 +916,6 @@ public: JOIN_TAB *table_access_tabs; uint top_table_access_tabs_count; - /* - Saved join_tab for pre_sorting. create_sort_index() will save here.. - */ - JOIN_TAB *pre_sort_join_tab; - uint pre_sort_index; - Item *pre_sort_idx_pushed_cond; - void clean_pre_sort_join_tab(); - - JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution @@ -1140,6 +1139,8 @@ public: bool skip_sort_order; bool need_tmp, hidden_group_fields; + /* TRUE if there was full cleunap of the JOIN */ + bool cleaned; DYNAMIC_ARRAY keyuse; Item::cond_result cond_value, having_value; List<Item> all_fields; ///< to store all fields that used in query @@ -1274,6 +1275,7 @@ public: optimized= 0; have_query_plan= QEP_NOT_PRESENT_YET; initialized= 0; + cleaned= 0; cond_equal= 0; having_equal= 0; exec_const_cond= 0; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 73d441a2d9d..4d251aa1c81 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1386,8 +1386,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; @@ -1398,8 +1425,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) && @@ -1411,7 +1437,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]; @@ -1643,16 +1673,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)) @@ -2116,10 +2148,6 @@ public: double progress; }; -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class I_List<thread_info>; -#endif - static const char *thread_state_info(THD *tmp) { #ifndef EMBEDDED_LIBRARY @@ -2320,7 +2348,7 @@ void Show_explain_request::call_in_target_thread() int select_result_explain_buffer::send_data(List<Item> &items) { - fill_record(thd, dst_table->field, items, TRUE, FALSE); + fill_record(thd, dst_table, dst_table->field, items, TRUE, FALSE); if ((dst_table->file->ha_write_tmp_row(dst_table->record[0]))) return 1; return 0; @@ -4056,8 +4084,9 @@ end: /* Restore original LEX value, statement's arena and THD arena values. */ lex_end(thd->lex); - if (i_s_arena.free_list) - i_s_arena.free_items(); + // Free items, before restoring backup_arena below. + DBUG_ASSERT(i_s_arena.free_list == NULL); + thd->free_items(); /* For safety reset list of open temporary tables before closing @@ -5212,7 +5241,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"); @@ -5236,7 +5265,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); @@ -5284,7 +5312,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(); @@ -5301,10 +5329,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) @@ -8905,11 +8931,6 @@ ST_SCHEMA_TABLE schema_tables[]= }; -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List_iterator_fast<char>; -template class List<char>; -#endif - int initialize_schema_table(st_plugin_int *plugin) { ST_SCHEMA_TABLE *schema_table; 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 8b09d449c2b..2966fc2a920 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -346,6 +346,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 66e3b0b2544..6e99ebb0d37 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2610,7 +2610,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 @@ -2624,7 +2623,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; @@ -2746,21 +2744,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 | @@ -2832,6 +2815,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 @@ -2872,7 +2889,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); @@ -3156,7 +3172,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) @@ -3187,12 +3202,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)); @@ -4559,6 +4568,7 @@ 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); @@ -6386,6 +6396,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) @@ -6753,8 +6764,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)); @@ -7331,6 +7340,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 */ @@ -7358,6 +7368,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++; @@ -7377,8 +7388,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 new fields + added to the table. 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) { @@ -7468,7 +7494,12 @@ 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); + update_virtual_fields(thd, to, VCOL_UPDATE_FOR_WRITE); + 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 7fab29a7d64..bda9b919663 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 42e2eb6cbfd..4ed67c86eb1 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(); @@ -563,7 +552,9 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) - update_virtual_fields(thd, table); + update_virtual_fields(thd, table, + table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); if (!select || (error= select->skip_record(thd)) > 0) { @@ -676,7 +667,9 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) - update_virtual_fields(thd, table); + update_virtual_fields(thd, table, + table->triggers ? VCOL_UPDATE_ALL : + VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) { @@ -684,8 +677,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 */ @@ -693,6 +685,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) { @@ -1239,11 +1236,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) @@ -1491,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); } } @@ -1871,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); /* @@ -1975,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); @@ -2165,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_has_explicit_value(); + } if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, @@ -2175,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 b14757f7581..e9df67a7a05 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1595,6 +1595,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type opt_time_precision kill_type kill_option int_num + opt_default_time_precision /* Bit field of MYSQL_START_TRANS_OPT_* flags. @@ -6025,9 +6026,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_default_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; @@ -6119,9 +6120,9 @@ type_with_opt_collate: now_or_signed_literal: - NOW_SYM optional_braces + NOW_SYM opt_default_time_precision { - $$= new (YYTHD->mem_root) Item_func_now_local(6); + $$= new (YYTHD->mem_root) Item_func_now_local($2); if ($$ == NULL) MYSQL_YYABORT; } @@ -7945,6 +7946,12 @@ select_alias: | TEXT_STRING_sys { $$=$1; } ; +opt_default_time_precision: + /* empty */ { $$= NOT_FIXED_DEC; } + | '(' ')' { $$= NOT_FIXED_DEC; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + opt_time_precision: /* empty */ { $$= 0; } | '(' ')' { $$= 0; } diff --git a/sql/sys_vars.h b/sql/sys_vars.h index 98a2b4c17fb..98c71e1df13 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -2047,18 +2047,3 @@ public: } }; - -/**************************************************************************** - Used templates -****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<set_var_base>; -template class List_iterator_fast<set_var_base>; -template class Sys_var_integer<int, GET_INT, SHOW_SINT>; -template class Sys_var_integer<uint, GET_UINT, SHOW_INT>; -template class Sys_var_integer<ulong, GET_ULONG, SHOW_LONG>; -template class Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS>; -template class Sys_var_integer<ulonglong, GET_ULL, SHOW_LONGLONG>; -#endif - diff --git a/sql/table.cc b/sql/table.cc index 18aaed6d81f..9954d91cab4 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -981,7 +981,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keyinfo->ext_key_part_map= 0; if (share->use_ext_keys && i) { - keyinfo->ext_key_flags= keyinfo->flags | HA_NOSAME; keyinfo->ext_key_part_map= 0; for (j= 0; j < first_key_parts && keyinfo->ext_key_parts < MAX_REF_PARTS; @@ -1002,7 +1001,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keyinfo->ext_key_parts++; keyinfo->ext_key_part_map|= 1 << j; } - } + } + if (j == first_key_parts) + keyinfo->ext_key_flags= keyinfo->flags | HA_NOSAME | HA_EXT_NOSAME; } share->ext_key_parts+= keyinfo->ext_key_parts; } @@ -1250,6 +1251,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 +1583,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 +1604,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 +2318,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 +2422,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,11 +2472,9 @@ 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; - else + if (share->vfields) { if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root, (uint) ((share->vfields+1)* @@ -2484,10 +2482,24 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, goto err; outparam->vfield= vfield_ptr; + } + + if (share->default_fields) + { + 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 +2512,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 @@ -3927,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(); @@ -5856,6 +5872,51 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl) /** + Check if a table has a default function either for INSERT or UPDATE-like + operation + @retval true there is a default function + @retval false there is no default function +*/ + +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; +} + + +/** + Add all fields that have a default function to the table write set. +*/ + +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 (((sql_command_flags[cmd] & CF_INSERTS_DATA) && + dfield->has_insert_default_function()) || + ((sql_command_flags[cmd] & CF_UPDATES_DATA) && + dfield->has_update_default_function())) + bitmap_set_bit(write_set, dfield->field_index); + } +} + + +/** @brief Allocate space for keys @@ -6416,22 +6477,25 @@ bool is_simple_order(ORDER *order) @param thd Thread handle @param table The TABLE object - @param for_write Requests to compute only fields needed for write + @param vcol_update_mode Specifies what virtual column are computed @details The function computes the values of the virtual columns of the table and stores them in the table record buffer. - Only fields from vcol_set are computed, and, when the flag for_write is not - set to TRUE, a virtual field is computed only if it's not stored. - The flag for_write is set to TRUE for row insert/update operations. - + If vcol_update_mode is set to VCOL_UPDATE_ALL then all virtual column are + computed. Otherwise, only fields from vcol_set are computed: all of them, + if vcol_update_mode is set to VCOL_UPDATE_FOR_WRITE, and, only those with + the stored_in_db flag set to false, if vcol_update_mode is equal to + VCOL_UPDATE_FOR_READ. + @retval 0 Success @retval >0 Error occurred when storing a virtual field value */ -int update_virtual_fields(THD *thd, TABLE *table, bool for_write) +int update_virtual_fields(THD *thd, TABLE *table, + enum enum_vcol_update_mode vcol_update_mode) { DBUG_ENTER("update_virtual_fields"); Field **vfield_ptr, *vfield; @@ -6444,9 +6508,9 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) { vfield= (*vfield_ptr); DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item); - /* Only update those fields that are marked in the vcol_set bitmap */ - if (bitmap_is_set(table->vcol_set, vfield->field_index) && - (for_write || !vfield->stored_in_db)) + if ((bitmap_is_set(table->vcol_set, vfield->field_index) && + (vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db)) || + vcol_update_mode == VCOL_UPDATE_ALL) { /* Compute the actual value of the virtual fields */ error= vfield->vcol_info->expr_item->save_in_field(vfield, 0); @@ -6461,6 +6525,56 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) DBUG_RETURN(0); } + +/** + Update all DEFAULT and/or ON INSERT fields. + + @details + Compute and set the default value of all fields with a default function. + There are two kinds of default functions - one is used for INSERT-like + operations, the other for UPDATE-like operations. Depending on the field + definition and the current operation one or the other kind of update + function is evaluated. + + @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_VALUE)) + { + 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_VALUE; + } + DBUG_RETURN(res); +} + + /* @brief Reset const_table flag @@ -6767,12 +6881,3 @@ uint TABLE_SHARE::actual_n_key_parts(THD *thd) ext_key_parts : key_parts; } - -/***************************************************************************** -** Instansiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<String>; -template class List_iterator<String>; -#endif diff --git a/sql/table.h b/sql/table.h index 1f546693fb3..a43f729ba5c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -300,6 +300,12 @@ enum tmp_table_type }; enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP }; +enum enum_vcol_update_mode +{ + VCOL_UPDATE_FOR_READ= 0, + VCOL_UPDATE_FOR_WRITE, + VCOL_UPDATE_ALL +}; class Filesort_info { @@ -350,25 +356,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 +595,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 +666,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 +680,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 */ bool use_ext_keys; /* Extended keys can be used */ bool null_field_first; bool system; /* Set if system table (one record) */ @@ -1007,8 +993,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 +1051,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 */ @@ -1212,6 +1186,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) { @@ -1298,6 +1274,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(); }; |