diff options
author | Igor Babaev <igor@askmonty.org> | 2018-11-10 14:52:57 -0800 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2018-11-10 14:52:57 -0800 |
commit | 5f46670bd09babbee75a24ac82eb4ade0706da66 (patch) | |
tree | 85e2759b75650b8165c3e01638e9458eb4d1274c /sql | |
parent | 8d5a11122c32f4d9eb87536886c6e893377bdd07 (diff) | |
parent | 3ea7de9a78a1410a9b79362774247e9e44b201b3 (diff) | |
download | mariadb-git-5f46670bd09babbee75a24ac82eb4ade0706da66.tar.gz |
Merge branch '10.4' into 10.4-mdev16188
Diffstat (limited to 'sql')
147 files changed, 6870 insertions, 4504 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 13a116a71d4..b37962d0fc9 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -153,6 +153,7 @@ IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS) IF(WIN32) SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc) + SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc) ENDIF() SET(SQL_SOURCE ${SQL_SOURCE} threadpool_generic.cc) @@ -204,9 +205,7 @@ IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) SET(MYSQLD_LIB ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib.lib) SET(MYSQLD_CORELIBS sql mysys dbug strings) FOREACH (CORELIB ${MYSQLD_CORELIBS}) - GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION) - FILE(TO_NATIVE_PATH ${LOC} LOC) - SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC}) + SET (LIB_LOCATIONS ${LIB_LOCATIONS} $<TARGET_FILE:${CORELIB}>) ENDFOREACH (CORELIB) SET(_PLATFORM x86) @@ -282,7 +281,7 @@ IF(APPLE) # Add CoreServices framework since some dloadable plugins may need it FIND_LIBRARY(CORESERVICES NAMES CoreServices) IF(CORESERVICES) - TARGET_LINK_LIBRARIES(mysqld ${CORESERVICES}) + TARGET_LINK_LIBRARIES(mysqld LINK_PRIVATE ${CORESERVICES}) ENDIF() ENDIF() @@ -303,13 +302,12 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS) ENDIF() ENDIF(NOT WITHOUT_DYNAMIC_PLUGINS) -TARGET_LINK_LIBRARIES(mysqld sql) +TARGET_LINK_LIBRARIES(mysqld LINK_PRIVATE sql) # Provide plugins with minimal set of libraries SET(INTERFACE_LIBS ${LIBRT}) IF(INTERFACE_LIBS) - SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_INTERFACE_LIBRARIES - "${INTERFACE_LIBS}") + TARGET_LINK_LIBRARIES(mysqld LINK_PUBLIC ${INTERFACE_LIBS}) ENDIF() # On Solaris, some extra effort is required in order to get dtrace probes @@ -321,7 +319,7 @@ DTRACE_INSTRUMENT_STATIC_LIBS(mysqld SET(WITH_MYSQLD_LDFLAGS "" CACHE STRING "Additional linker flags for mysqld") MARK_AS_ADVANCED(WITH_MYSQLD_LDFLAGS) IF(WITH_MYSQLD_LDFLAGS) - GET_TARGET_PROPERTY(mysqld LINK_FLAGS MYSQLD_LINK_FLAGS) + GET_TARGET_PROPERTY(MYSQLD_LINK_FLAGS mysqld LINK_FLAGS) IF(NOT MYSQLD_LINK_FLAGS) SET(MYSQLD_LINK_FLAGS) ENDIF() @@ -422,23 +420,20 @@ SET(DBOPT_FILE ${CMAKE_SOURCE_DIR}/support-files/db.opt ) INSTALL(FILES ${DBOPT_FILE} DESTINATION data/test COMPONENT DataFiles) # Install initial database on windows -IF(NOT CMAKE_CROSSCOMPILING) - GET_TARGET_PROPERTY(MYSQLD_EXECUTABLE mysqld LOCATION) -ENDIF() -IF(WIN32 AND MYSQLD_EXECUTABLE) - CONFIGURE_FILE( - ${CMAKE_SOURCE_DIR}/cmake/create_initial_db.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake - @ONLY - ) - +IF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING) + IF(MSVC_IDE OR CMAKE_GENERATOR MATCHES "Xcode") SET (CONFIG_PARAM -DCONFIG=${CMAKE_CFG_INTDIR}) ENDIF() MAKE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR}/data) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep - COMMAND ${CMAKE_COMMAND} ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake + COMMAND ${CMAKE_COMMAND} ${CONFIG_PARAM} + -DTOP_SRCDIR="${CMAKE_SOURCE_DIR}" + -DBINDIR="${CMAKE_CURRENT_BINARY_DIR}" + -DMYSQLD_EXECUTABLE="$<TARGET_FILE:mysqld>" + -DCMAKE_CFG_INTDIR="${CMAKE_CFG_INTDIR}" + -P ${CMAKE_SOURCE_DIR}/cmake/create_initial_db.cmake COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data DEPENDS mysqld @@ -456,7 +451,7 @@ IF(WIN32 AND MYSQLD_EXECUTABLE) ELSE() # Not windows or cross compiling, just install an empty directory INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql COMPONENT DataFiles) -ENDIF(WIN32 AND MYSQLD_EXECUTABLE) +ENDIF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING) ENDIF(INSTALL_LAYOUT STREQUAL "STANDALONE") IF(WIN32) @@ -492,7 +487,7 @@ IF(WIN32) COMPONENT Server ) SET_TARGET_PROPERTIES(mysql_install_db PROPERTIES COMPILE_FLAGS -DINSTALL_PLUGINDIR=${INSTALL_PLUGINDIR}) - TARGET_LINK_LIBRARIES(mysql_install_db mysys) + TARGET_LINK_LIBRARIES(mysql_install_db mysys shlwapi) ADD_LIBRARY(winservice STATIC winservice.c) TARGET_LINK_LIBRARIES(winservice shell32) diff --git a/sql/compat56.cc b/sql/compat56.cc index 16c25924d6e..d1cb8b0042c 100644 --- a/sql/compat56.cc +++ b/sql/compat56.cc @@ -254,6 +254,9 @@ void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp) { longlong ymd, hms; longlong ymdhms, ym; + + DBUG_ASSERT(tmp != LONGLONG_MIN); + if ((ltime->neg= (tmp < 0))) tmp= -tmp; diff --git a/sql/contributors.h b/sql/contributors.h index a0d05af3fa6..69f8fa6bd4c 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -45,12 +45,14 @@ struct show_table_contributors_st show_table_contributors[]= { {"Visma", "https://visma.com", "Gold Sponsor of the MariaDB Foundation"}, {"DBS", "https://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, {"IBM", "https://www.ibm.com", "Gold Sponsor of the MariaDB Foundation"}, + {"Tencent Games", "http://game.qq.com/", "Gold Sponsor of the MariaDB Foundation"}, {"Nexedi", "https://www.nexedi.com", "Silver Sponsor of the MariaDB Foundation"}, - {"Acronis", "http://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, + {"Acronis", "https://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, {"Verkkokauppa.com", "https://www.verkkokauppa.com", "Bronze Sponsor of the MariaDB Foundation"}, {"Virtuozzo", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, {"Tencent Game DBA", "http://tencentdba.com/about", "Bronze Sponsor of the MariaDB Foundation"}, {"Tencent TDSQL", "http://tdsql.org", "Bronze Sponsor of the MariaDB Foundation"}, + {"Percona", "https://www.percona.com/", "Bronze Sponsor of the MariaDB Foundation"}, /* Sponsors of important features */ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"}, diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 19cb4865ee6..a6803982171 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -654,7 +654,7 @@ my_time_t add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone, interval_type scale, INTERVAL interval) { - if (date_add_interval(ltime, scale, interval)) + if (date_add_interval(current_thd, ltime, scale, interval)) return 0; uint not_used; @@ -1284,7 +1284,11 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql) */ sp_sql->append(STRING_WITH_LEN("() SQL SECURITY INVOKER ")); + if (thd->variables.sql_mode & MODE_ORACLE) + sp_sql->append(STRING_WITH_LEN(" AS BEGIN ")); sp_sql->append(&body); + if (thd->variables.sql_mode & MODE_ORACLE) + sp_sql->append(STRING_WITH_LEN("; END")); DBUG_RETURN(thd->is_fatal_error); } @@ -1387,9 +1391,6 @@ Event_job_data::execute(THD *thd, bool drop) goto end; } - if (construct_sp_sql(thd, &sp_sql)) - goto end; - /* Set up global thread attributes to reflect the properties of this Event. We can simply reset these instead of usual @@ -1401,6 +1402,9 @@ Event_job_data::execute(THD *thd, bool drop) thd->variables.sql_mode= sql_mode; thd->variables.time_zone= time_zone; + if (construct_sp_sql(thd, &sp_sql)) + goto end; + thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length()); { diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index d20d322d864..bfda8438885 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -216,7 +216,7 @@ Event_parse_data::init_execute_at(THD *thd) (starts_null && ends_null))); DBUG_ASSERT(starts_null && ends_null); - if (item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE)) + if (item_execute_at->get_date(thd, <ime, TIME_NO_ZERO_DATE)) goto wrong_value; ltime_utc= TIME_to_timestamp(thd,<ime,¬_used); @@ -275,7 +275,7 @@ Event_parse_data::init_interval(THD *thd) if (item_expression->fix_fields(thd, &item_expression)) goto wrong_value; - if (get_interval_value(item_expression, interval, &interval_tmp)) + if (get_interval_value(thd, item_expression, interval, &interval_tmp)) goto wrong_value; expression= 0; @@ -378,7 +378,7 @@ Event_parse_data::init_starts(THD *thd) if (item_starts->fix_fields(thd, &item_starts)) goto wrong_value; - if (item_starts->get_date(<ime, TIME_NO_ZERO_DATE)) + if (item_starts->get_date(thd, <ime, TIME_NO_ZERO_DATE)) goto wrong_value; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); @@ -433,7 +433,7 @@ Event_parse_data::init_ends(THD *thd) goto error_bad_params; DBUG_PRINT("info", ("convert to TIME")); - if (item_ends->get_date(<ime, TIME_NO_ZERO_DATE)) + if (item_ends->get_date(thd, <ime, TIME_NO_ZERO_DATE)) goto error_bad_params; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); diff --git a/sql/events.cc b/sql/events.cc index af020d5240e..196c8df591d 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -336,7 +336,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0)) DBUG_RETURN(TRUE); - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (lock_object_name(thd, MDL_key::EVENT, parse_data->dbname.str, parse_data->name.str)) @@ -420,9 +420,9 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) DBUG_RETURN(ret); #ifdef WITH_WSREP - error: - DBUG_RETURN(TRUE); -#endif /* WITH_WSREP */ +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -463,6 +463,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0)) DBUG_RETURN(TRUE); + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (lock_object_name(thd, MDL_key::EVENT, @@ -550,9 +551,9 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, thd->restore_stmt_binlog_format(save_binlog_format); DBUG_RETURN(ret); #ifdef WITH_WSREP -error: - DBUG_RETURN(TRUE); -#endif /* WITH_WSREP */ +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -593,6 +594,7 @@ Events::drop_event(THD *thd, const LEX_CSTRING *dbname, if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0)) DBUG_RETURN(TRUE); + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* @@ -617,9 +619,9 @@ Events::drop_event(THD *thd, const LEX_CSTRING *dbname, thd->restore_stmt_binlog_format(save_binlog_format); DBUG_RETURN(ret); #ifdef WITH_WSREP -error: - DBUG_RETURN(TRUE); -#endif /* WITH_WSREP */ +wsrep_error_label: + DBUG_RETURN(true); +#endif } diff --git a/sql/field.cc b/sql/field.cc index e014b62788a..7e27ed1bfc3 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1832,7 +1832,7 @@ int Field::store_timestamp(my_time_t ts, ulong sec_part) { MYSQL_TIME ltime; THD *thd= get_thd(); - thd->timestamp_to_TIME(<ime, ts, sec_part, 0); + thd->timestamp_to_TIME(<ime, ts, sec_part, date_mode_t(0)); return store_time_dec(<ime, decimals()); } @@ -2064,17 +2064,16 @@ my_decimal* Field_int::val_decimal(my_decimal *decimal_value) } -bool Field_int::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_int::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { ASSERT_COLUMN_MARKED_FOR_READ; - longlong nr= val_int(); - bool neg= !(flags & UNSIGNED_FLAG) && nr < 0; - return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate, - field_name.str); + Longlong_hybrid nr(val_int(), (flags & UNSIGNED_FLAG)); + return int_to_datetime_with_warn(get_thd(), nr, ltime, + fuzzydate, field_name.str); } -bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) +bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id) { ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(ltime); @@ -2254,15 +2253,13 @@ uint Field::fill_cache_field(CACHE_FIELD *copy) } -bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field::get_date(MYSQL_TIME *to, date_mode_t mode) { - char buff[40]; - String tmp(buff,sizeof(buff),&my_charset_bin),*res; - if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate)) - return 1; - return 0; + StringBuffer<40> tmp; + Temporal::Warn_push warn(get_thd(), NullS, to, mode); + Temporal_hybrid *t= new(to) Temporal_hybrid(get_thd(), &warn, + val_str(&tmp), mode); + return !t->is_valid_temporal(); } /** @@ -3106,6 +3103,12 @@ Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table, ** Field_new_decimal ****************************************************************************/ +static uint get_decimal_precision(uint len, uint8 dec, bool unsigned_val) +{ + uint precision= my_decimal_length_to_precision(len, dec, unsigned_val); + return MY_MIN(precision, DECIMAL_MAX_PRECISION); +} + Field_new_decimal::Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -3116,8 +3119,7 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) { - precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); + precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); @@ -4507,7 +4509,7 @@ longlong Field_float::val_int(void) { float j; float4get(j,ptr); - return (longlong) rint(j); + return Converter_double_to_longlong(j, false).result(); } @@ -4825,11 +4827,12 @@ my_decimal *Field_real::val_decimal(my_decimal *decimal_value) } -bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { ASSERT_COLUMN_MARKED_FOR_READ; double nr= val_real(); - return double_to_datetime_with_warn(nr, ltime, fuzzydate, field_name.str); + return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate, + field_name.str); } @@ -5058,8 +5061,9 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt, return 1; // date was fine but pointed to a DST gap } - // Adjust and store the value - store_TIMEVAL(Timeval(timestamp, l_time->second_part).trunc(decimals())); + // Store the value + DBUG_ASSERT(!dt->fraction_remainder(decimals())); + store_TIMEVAL(Timeval(timestamp, l_time->second_part)); // Calculate return value and send warnings if needed if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap @@ -5072,10 +5076,10 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt, } -sql_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const +date_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const { // We don't want to store invalid or fuzzy datetime values in TIMESTAMP - return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE; + return date_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE); } @@ -5084,7 +5088,7 @@ int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) int warn; ErrConvTime str(ltime); THD *thd= get_thd(); - Datetime dt(thd, &warn, ltime, sql_mode_for_timestamp(thd)); + Datetime dt(thd, &warn, ltime, sql_mode_for_timestamp(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, warn); } @@ -5093,9 +5097,9 @@ int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) { ErrConvString str(from, len, cs); THD *thd= get_thd(); - int error; - Datetime dt(&error, from, len, cs, sql_mode_for_timestamp(thd)); - return store_TIME_with_warning(thd, &dt, &str, error); + MYSQL_TIME_STATUS st; + Datetime dt(&st, from, len, cs, sql_mode_for_timestamp(thd), decimals()); + return store_TIME_with_warning(thd, &dt, &str, st.warnings); } @@ -5104,7 +5108,7 @@ int Field_timestamp::store(double nr) int error; ErrConvDouble str(nr); THD *thd= get_thd(); - Datetime dt(&error, nr, sql_mode_for_timestamp(thd)); + Datetime dt(&error, Sec6(nr), sql_mode_for_timestamp(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5112,9 +5116,9 @@ int Field_timestamp::store(double nr) int Field_timestamp::store(longlong nr, bool unsigned_val) { int error; - ErrConvInteger str(nr, unsigned_val); + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); THD *thd= get_thd(); - Datetime dt(&error, nr, unsigned_val, sql_mode_for_timestamp(thd)); + Datetime dt(&error, Sec6(nr, unsigned_val), sql_mode_for_timestamp(thd)); return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5123,7 +5127,7 @@ int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part) { store_TIME(ts, sec_part); if (ts == 0 && sec_part == 0 && - get_thd()->variables.sql_mode & TIME_NO_ZERO_DATE) + get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE) { ErrConvString s( STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7), @@ -5232,11 +5236,11 @@ Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const DBUG_ASSERT(!is_null_in_record(record)); ulong sec_part; return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part && - (sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != 0; + bool(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false; } -bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { ulong sec_part; my_time_t ts= get_timestamp(&sec_part); @@ -5247,7 +5251,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) bool Field_timestamp::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - Field_timestamp::get_date(<ime, 0); + Field_timestamp::get_date(<ime, date_mode_t(0)); return protocol->store(<ime, 0); } @@ -5405,7 +5409,7 @@ double Field_timestamp_with_dec::val_real(void) my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return TIME_to_my_decimal(<ime, d); } @@ -5414,7 +5418,7 @@ int Field_timestamp::store_decimal(const my_decimal *d) int error; THD *thd= get_thd(); ErrConvDecimal str(d); - Datetime dt(&error, d, sql_mode_for_timestamp(thd)); + Datetime dt(&error, Sec6(d), sql_mode_for_timestamp(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5430,7 +5434,7 @@ int Field_timestamp_with_dec::set_time() bool Field_timestamp_with_dec::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - Field_timestamp::get_date(<ime, 0); + Field_timestamp::get_date(<ime, date_mode_t(0)); return protocol->store(<ime, dec); } @@ -5535,81 +5539,66 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level, 3 Datetime value that was cut (warning level NOTE) This is used by opt_range.cc:get_mm_leaf(). */ -int Field_temporal_with_date::store_TIME_with_warning(const Datetime *dt, - const ErrConv *str, - int was_cut) +int Field_datetime::store_TIME_with_warning(const Datetime *dt, + const ErrConv *str, + int was_cut) { - Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN; - timestamp_type ts_type= type_handler()->mysql_timestamp_type(); - ASSERT_COLUMN_MARKED_FOR_WRITE; // Handle totally bad values if (!dt->is_valid_datetime()) - { - static const Datetime zero; - store_TIME(zero.get_mysql_time()); - if (was_cut == 0) // special case: zero date - { - set_warnings(trunc_level, str, MYSQL_TIME_WARN_OUT_OF_RANGE, ts_type); - return 2; - } - set_warnings(trunc_level, str, MYSQL_TIME_WARN_TRUNCATED, ts_type); - return 1; - } - // Adjust and store the value - if (ts_type == MYSQL_TIMESTAMP_DATE) - { - if (!dt->hhmmssff_is_zero()) - was_cut|= MYSQL_TIME_NOTE_TRUNCATED; - store_TIME(dt->get_mysql_time()); - } - else if (dt->fraction_remainder(decimals())) - { - Datetime truncated(dt->trunc(decimals())); - store_TIME(truncated.get_mysql_time()); - } - else - store_TIME(dt->get_mysql_time()); + return store_invalid_with_warning(str, was_cut, MYSQL_TIMESTAMP_DATETIME); + // Store the value + DBUG_ASSERT(!dt->fraction_remainder(decimals())); + store_TIME(dt->get_mysql_time()); // Caclulate return value and send warnings if needed - return store_TIME_return_code_with_warnings(was_cut, str, ts_type); + return store_TIME_return_code_with_warnings(was_cut, str, + MYSQL_TIMESTAMP_DATETIME); } -int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs) +int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs) { - int error; + MYSQL_TIME_STATUS st; ErrConvString str(from, len, cs); - Datetime dt(&error, from, len, cs, sql_mode_for_dates(get_thd())); - return store_TIME_with_warning(&dt, &str, error); + Datetime dt(&st, from, len, cs, sql_mode_for_dates(get_thd()), decimals()); + return store_TIME_with_warning(&dt, &str, st.warnings); } -int Field_temporal_with_date::store(double nr) +int Field_datetime::store(double nr) { int error; ErrConvDouble str(nr); - Datetime dt(&error, nr, sql_mode_for_dates(get_thd())); + Datetime dt(&error, Sec6(nr), sql_mode_for_dates(get_thd()), decimals()); return store_TIME_with_warning(&dt, &str, error); } -int Field_temporal_with_date::store(longlong nr, bool unsigned_val) +int Field_datetime::store(longlong nr, bool unsigned_val) { int error; - ErrConvInteger str(nr, unsigned_val); - Datetime dt(&error, nr, unsigned_val, sql_mode_for_dates(get_thd())); + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); + Datetime dt(&error, Sec6(nr, unsigned_val), sql_mode_for_dates(get_thd())); return store_TIME_with_warning(&dt, &str, error); } -int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec) +int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec) { int error; ErrConvTime str(ltime); THD *thd= get_thd(); - Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd)); + Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd), decimals()); return store_TIME_with_warning(&dt, &str, error); } +int Field_datetime::store_decimal(const my_decimal *d) +{ + int error; + ErrConvDecimal str(d); + Datetime tm(&error, Sec6(d), sql_mode_for_dates(get_thd()), decimals()); + return store_TIME_with_warning(&tm, &str, error); +} + bool Field_temporal_with_date::validate_value_in_record(THD *thd, const uchar *record) const @@ -5623,7 +5612,7 @@ Field_temporal_with_date::validate_value_in_record(THD *thd, my_decimal *Field_temporal::val_decimal(my_decimal *d) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_date(<ime, date_mode_t(0))) { bzero(<ime, sizeof(ltime)); ltime.time_type= type_handler()->mysql_timestamp_type(); @@ -5656,7 +5645,7 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, const_item->field_type() != MYSQL_TYPE_TIMESTAMP) || const_item->decimals != decimals()) { - Datetime dt(thd, const_item, 0); + Datetime dt(thd, const_item, date_mode_t(0)); if (!dt.is_valid_datetime()) return NULL; /* @@ -5698,20 +5687,10 @@ int Field_time::store_TIME_with_warning(const Time *t, ASSERT_COLUMN_MARKED_FOR_WRITE; // Handle totally bad values if (!t->is_valid_time()) - { - static const Datetime zero; - store_TIME(zero.get_mysql_time()); - set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED); - return 1; - } - // Adjust and store the value - if (t->fraction_remainder(decimals())) - { - Time truncated(t->trunc(decimals())); - store_TIME(truncated.get_mysql_time()); - } - else - store_TIME(t->get_mysql_time()); + return store_invalid_with_warning(str, warn, MYSQL_TIMESTAMP_TIME); + // Store the value + DBUG_ASSERT(!t->fraction_remainder(decimals())); + store_TIME(t->get_mysql_time()); // Calculate return value and send warnings if needed return store_TIME_return_code_with_warnings(warn, str, MYSQL_TIMESTAMP_TIME); } @@ -5731,9 +5710,10 @@ void Field_time::store_TIME(const MYSQL_TIME *ltime) int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) { ErrConvString str(from, len, cs); - int error; - Time tm(&error, from, len, cs, sql_mode_for_dates(get_thd())); - return store_TIME_with_warning(&tm, &str, error); + MYSQL_TIME_STATUS st; + THD *thd= get_thd(); + Time tm(thd, &st, from, len, cs, sql_mode_for_dates(thd), decimals()); + return store_TIME_with_warning(&tm, &str, st.warnings); } @@ -5741,7 +5721,7 @@ int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec) { ErrConvTime str(ltime); int warn; - Time tm(&warn, ltime, curdays); + Time tm(&warn, ltime, curdays, decimals()); return store_TIME_with_warning(&tm, &str, warn); } @@ -5750,16 +5730,17 @@ int Field_time::store(double nr) { ErrConvDouble str(nr); int was_cut; - Time tm(&was_cut, nr); + Time tm(get_thd(), &was_cut, Sec6(nr), decimals()); return store_TIME_with_warning(&tm, &str, was_cut); } int Field_time::store(longlong nr, bool unsigned_val) { - ErrConvInteger str(nr, unsigned_val); + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); int was_cut; - Time tm(&was_cut, nr, unsigned_val); + // Need fractional digit truncation if nr overflows to '838:59:59.999999' + Time tm(get_thd(), &was_cut, Sec6(nr, unsigned_val), decimals()); return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5819,7 +5800,7 @@ String *Field_time::val_str(String *str, } -bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate) +bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate) { if (!(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & TIME_NO_ZERO_IN_DATE)) { @@ -5841,7 +5822,7 @@ bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate) DATE_FORMAT(time, "%l.%i %p") */ -bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_time::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_zero_in_date_with_warn(fuzzydate)) return true; @@ -5917,7 +5898,7 @@ int Field_time::store_decimal(const my_decimal *d) { ErrConvDecimal str(d); int was_cut; - Time tm(&was_cut, d); + Time tm(get_thd(), &was_cut, Sec6(d), decimals()); return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5977,7 +5958,7 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, if (const_item->field_type() != MYSQL_TYPE_TIME) { // Get the value of const_item with conversion from DATETIME to TIME - Time tm(const_item, + Time tm(get_thd(), const_item, Time::Options(Time::comparison_flags_for_get_date(), mode)); if (!tm.is_valid_time()) return NULL; @@ -6002,7 +5983,8 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, if (const_item->field_type() != MYSQL_TYPE_TIME || const_item->decimals != decimals()) { - Time tm(const_item, Time::Options(TIME_TIME_ONLY, mode)); + Time tm(get_thd(), const_item, + Time::Options(TIME_TIME_ONLY, mode)); if (!tm.is_valid_time()) return NULL; /* @@ -6045,7 +6027,7 @@ double Field_time_with_dec::val_real(void) return TIME_to_double(<ime); } -bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_zero_in_date_with_warn(fuzzydate)) return true; @@ -6097,7 +6079,7 @@ void Field_timef::store_TIME(const MYSQL_TIME *ltime) my_time_packed_to_binary(tmp, ptr, dec); } -bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_zero_in_date_with_warn(fuzzydate)) return true; @@ -6231,12 +6213,13 @@ String *Field_year::val_str(String *val_buffer, } -bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_year::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate) { int tmp= (int) ptr[0]; if (tmp || field_length != 4) tmp+= 1900; - return int_to_datetime_with_warn(false, tmp * 10000, + return int_to_datetime_with_warn(get_thd(), + Longlong_hybrid(tmp * 10000, true), ltime, fuzzydate, field_name.str); } @@ -6249,6 +6232,67 @@ void Field_year::sql_type(String &res) const } +/*****************************************************************************/ + +int Field_date_common::store_TIME_with_warning(const Datetime *dt, + const ErrConv *str, + int was_cut) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE; + // Handle totally bad values + if (!dt->is_valid_datetime()) + return store_invalid_with_warning(str, was_cut, MYSQL_TIMESTAMP_DATE); + // Store the value + if (!dt->hhmmssff_is_zero()) + was_cut|= MYSQL_TIME_NOTE_TRUNCATED; + store_TIME(dt->get_mysql_time()); + // Caclulate return value and send warnings if needed + return store_TIME_return_code_with_warnings(was_cut, str, + MYSQL_TIMESTAMP_DATE); +} + +int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs) +{ + MYSQL_TIME_STATUS st; + ErrConvString str(from, len, cs); + Datetime dt(&st, from, len, cs, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, st.warnings); +} + +int Field_date_common::store(double nr) +{ + int error; + ErrConvDouble str(nr); + Datetime dt(&error, Sec6(nr), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store(longlong nr, bool unsigned_val) +{ + int error; + ErrConvInteger str(Longlong_hybrid(nr, unsigned_val)); + Datetime dt(&error, Sec6(nr, unsigned_val), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec) +{ + int error; + ErrConvTime str(ltime); + THD *thd= get_thd(); + Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd)); + return store_TIME_with_warning(&dt, &str, error); +} + +int Field_date_common::store_decimal(const my_decimal *d) +{ + int error; + ErrConvDecimal str(d); + Datetime tm(&error, Sec6(d), sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&tm, &str, error); +} + + /**************************************************************************** ** date type ** In string context: YYYY-MM-DD @@ -6292,7 +6336,7 @@ longlong Field_date::val_int(void) bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; int32 tmp= sint4korr(pos); @@ -6309,7 +6353,7 @@ String *Field_date::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { MYSQL_TIME ltime; - get_TIME(<ime, ptr, 0); + get_TIME(<ime, ptr, date_mode_t(0)); val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); uint length= (uint) my_date_to_str(<ime, const_cast<char*>(val_buffer->ptr())); @@ -6359,7 +6403,7 @@ void Field_newdate::store_TIME(const MYSQL_TIME *ltime) bool Field_newdate::send_binary(Protocol *protocol) { MYSQL_TIME tm; - Field_newdate::get_date(&tm,0); + Field_newdate::get_date(&tm, date_mode_t(0)); return protocol->store_date(&tm); } @@ -6411,7 +6455,7 @@ String *Field_newdate::val_str(String *val_buffer, bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; uint32 tmp=(uint32) uint3korr(pos); @@ -6482,7 +6526,7 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, case IDENTITY_SUBST: if (const_item->field_type() != MYSQL_TYPE_DATE) { - Date d(thd, const_item, 0); + Date d(thd, const_item, date_mode_t(0)); if (!d.is_valid_date()) return NULL; return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time()); @@ -6509,7 +6553,7 @@ void Field_datetime::store_TIME(const MYSQL_TIME *ltime) bool Field_datetime::send_binary(Protocol *protocol) { MYSQL_TIME tm; - Field_datetime::get_date(&tm, 0); + Field_datetime::get_date(&tm, date_mode_t(0)); return protocol->store(&tm, 0); } @@ -6575,7 +6619,7 @@ String *Field_datetime::val_str(String *val_buffer, } bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; longlong tmp= sint8korr(pos); @@ -6651,18 +6695,10 @@ void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime) store_bigendian(packed, ptr, Field_datetime_hires::pack_length()); } -int Field_temporal_with_date::store_decimal(const my_decimal *d) -{ - int error; - ErrConvDecimal str(d); - Datetime tm(&error, d, sql_mode_for_dates(get_thd())); - return store_TIME_with_warning(&tm, &str, error); -} - bool Field_datetime_with_dec::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return protocol->store(<ime, dec); } @@ -6670,14 +6706,14 @@ bool Field_datetime_with_dec::send_binary(Protocol *protocol) double Field_datetime_with_dec::val_real(void) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return TIME_to_double(<ime); } longlong Field_datetime_with_dec::val_int(void) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); return TIME_to_ulonglong_datetime(<ime); } @@ -6686,7 +6722,7 @@ String *Field_datetime_with_dec::val_str(String *str, String *unused __attribute__((unused))) { MYSQL_TIME ltime; - get_date(<ime, 0); + get_date(<ime, date_mode_t(0)); str->alloc(field_length+1); str->length(field_length); my_datetime_to_str(<ime, (char*) str->ptr(), dec); @@ -6696,7 +6732,7 @@ String *Field_datetime_with_dec::val_str(String *str, bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length()); @@ -6736,7 +6772,7 @@ void Field_datetimef::store_TIME(const MYSQL_TIME *ltime) } bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { ASSERT_COLUMN_MARKED_FOR_READ; longlong tmp= my_datetime_packed_from_binary(pos, dec); @@ -7120,7 +7156,9 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_string::sort_string(uchar *to,uint length) { - IF_DBUG(size_t tmp= ,) +#ifdef DBUG_ASSERT_EXISTS + size_t tmp= +#endif field_charset->coll->strnxfrm(field_charset, to, length, char_length() * @@ -7526,7 +7564,7 @@ void Field_varstring::sort_string(uchar *to,uint length) length-= length_bytes; } -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS size_t rc= #endif field_charset->coll->strnxfrm(field_charset, to, length, @@ -8064,7 +8102,13 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs) return 0; } - if (table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT + /* + For min/max fields of statistical data 'table' is set to NULL. + It could not be otherwise as this data is shared by many instances + of the same base table. + */ + + if (table && table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT { DBUG_ASSERT(!f_is_hex_escape(flags)); DBUG_ASSERT(field_charset == cs); @@ -8388,7 +8432,7 @@ void Field_blob::sort_string(uchar *to,uint length) store_bigendian(buf.length(), to + length, packlength); } -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS size_t rc= #endif field_charset->coll->strnxfrm(field_charset, to, length, length, @@ -10128,12 +10172,9 @@ void Column_definition::create_length_to_internal_length_bit() void Column_definition::create_length_to_internal_length_newdecimal() { - key_length= pack_length= - my_decimal_get_binary_size(my_decimal_length_to_precision((uint) length, - decimals, - flags & - UNSIGNED_FLAG), - decimals); + DBUG_ASSERT(length < UINT_MAX32); + uint prec= get_decimal_precision((uint)length, decimals, flags & UNSIGNED_FLAG); + key_length= pack_length= my_decimal_get_binary_size(prec, decimals); } diff --git a/sql/field.h b/sql/field.h index 8b080a0d06f..eb39b6bcec9 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1342,7 +1342,7 @@ public: } void copy_from_tmp(int offset); uint fill_cache_field(struct st_cache_field *copy); - virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } virtual TYPELIB *get_typelib() const { return NULL; } virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } @@ -1958,7 +1958,7 @@ public: } int store_decimal(const my_decimal *dec) { return store(dec->to_double()); } int store_time_dec(const MYSQL_TIME *ltime, uint dec); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); my_decimal *val_decimal(my_decimal *); bool val_bool() { return val_real() != 0e0; } uint32 max_display_length() const { return field_length; } @@ -2075,10 +2075,11 @@ public: return my_decimal(ptr, precision, dec). to_string(val_buffer, fixed_precision, dec, '0'); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return my_decimal(ptr, precision, dec). - to_datetime_with_warn(ltime, fuzzydate, field_name.str); + my_decimal nr(ptr, precision, dec); + return decimal_to_datetime_with_warn(get_thd(), &nr, ltime, + fuzzydate, field_name.str); } bool val_bool() { @@ -2127,7 +2128,7 @@ public: return nr < 0 && !unsigned_flag ? 0 : (ulonglong) nr; } int store_time_dec(const MYSQL_TIME *ltime, uint dec); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); virtual const Type_limits_int *type_limits_int() const= 0; uint32 max_display_length() const { @@ -2390,7 +2391,6 @@ public: { return unpack_int64(to, from, from_end); } - void set_max(); bool is_max(); }; @@ -2411,8 +2411,8 @@ public: {} const Type_handler *type_handler() const { return &type_handler_vers_trx_id; } uint size_of() const { return sizeof(*this); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return get_date(ltime, fuzzydate, (ulonglong) val_int()); } @@ -2595,6 +2595,8 @@ class Field_temporal: public Field { protected: Item *get_equal_const_item_datetime(THD *thd, const Context &ctx, Item *const_item); + void set_warnings(Sql_condition::enum_warning_level trunc_level, + const ErrConv *str, int was_cut, timestamp_type ts_type); int store_TIME_return_code_with_warnings(int warn, const ErrConv *str, timestamp_type ts_type) { @@ -2608,7 +2610,21 @@ protected: set_warnings(Sql_condition::WARN_LEVEL_WARN, str, warn, ts_type); return warn ? 2 : 0; } - + int store_invalid_with_warning(const ErrConv *str, int was_cut, + timestamp_type ts_type) + { + DBUG_ASSERT(was_cut); + reset(); + Sql_condition::enum_warning_level level= Sql_condition::WARN_LEVEL_WARN; + if (was_cut & MYSQL_TIME_WARN_ZERO_DATE) + { + DBUG_ASSERT(ts_type != MYSQL_TIMESTAMP_TIME); + set_warnings(level, str, MYSQL_TIME_WARN_OUT_OF_RANGE, ts_type); + return 2; + } + set_warnings(level, str, MYSQL_TIME_WARN_TRUNCATED, ts_type); + return 1; + } public: Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -2624,7 +2640,7 @@ public: int save_in_field(Field *to) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_date(<ime, date_mode_t(0))) return to->reset(); return to->store_time_dec(<ime, decimals()); } @@ -2643,8 +2659,6 @@ public: return (Field::eq_def(field) && decimals() == field->decimals()); } my_decimal *val_decimal(my_decimal*); - void set_warnings(Sql_condition::enum_warning_level trunc_level, - const ErrConv *str, int was_cut, timestamp_type ts_type); double pos_in_interval(Field *min, Field *max) { return pos_in_interval_val_real(min, max); @@ -2674,19 +2688,16 @@ public: */ class Field_temporal_with_date: public Field_temporal { protected: - int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str, - int was_cut); - void store_TIME_with_trunc(const Time *); virtual void store_TIME(const MYSQL_TIME *ltime) = 0; virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const = 0; + date_mode_t fuzzydate) const = 0; bool validate_MMDD(bool not_zero_date, uint month, uint day, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { if (!not_zero_date) - return fuzzydate & TIME_NO_ZERO_DATE; + return bool(fuzzydate & TIME_NO_ZERO_DATE); if (!month || !day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; + return bool(fuzzydate & TIME_NO_ZERO_IN_DATE); return false; } public: @@ -2697,18 +2708,13 @@ public: :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) {} - int store(const char *to, size_t length, CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time_dec(const MYSQL_TIME *ltime, uint dec); - int store_decimal(const my_decimal *); bool validate_value_in_record(THD *thd, const uchar *record) const; }; class Field_timestamp :public Field_temporal { protected: - sql_mode_t sql_mode_for_timestamp(THD *thd) const; + date_mode_t sql_mode_for_timestamp(THD *thd) const; int store_TIME_with_warning(THD *, const Datetime *, const ErrConv *, int warn); virtual void store_TIMEVAL(const timeval &tv) @@ -2758,7 +2764,7 @@ public: { store_TIMEVAL(Timeval(timestamp, sec_part).trunc(decimals())); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); uchar *pack(uchar *to, const uchar *from, uint max_length __attribute__((unused))) { @@ -2938,7 +2944,7 @@ public: double val_real(void); longlong val_int(void); String *val_str(String*,String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); bool send_binary(Protocol *protocol); Information_schema_numeric_attributes information_schema_numeric_attributes() const @@ -2952,6 +2958,9 @@ public: class Field_date_common: public Field_temporal_with_date { +protected: + int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str, + int was_cut); public: Field_date_common(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, @@ -2963,13 +2972,18 @@ public: SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part, const Item_bool_func *cond, scalar_comparison_op op, Item *value); + int store(const char *to, size_t length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(const MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); }; class Field_date :public Field_date_common { void store_TIME(const MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; public: Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) @@ -2978,7 +2992,7 @@ public: const Type_handler *type_handler() const { return &type_handler_date; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_date::get_TIME(ltime, ptr, fuzzydate); } double val_real(void); longlong val_int(void); @@ -3005,7 +3019,7 @@ public: class Field_newdate :public Field_date_common { void store_TIME(const MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; public: Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) @@ -3023,7 +3037,7 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_newdate::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); @@ -3040,12 +3054,7 @@ class Field_time :public Field_temporal { protected: virtual void store_TIME(const MYSQL_TIME *ltime); int store_TIME_with_warning(const Time *ltime, const ErrConv *str, int warn); - void set_warnings(Sql_condition::enum_warning_level level, - const ErrConv *str, int was_cut) - { - Field_temporal::set_warnings(level, str, was_cut, MYSQL_TIMESTAMP_TIME); - } - bool check_zero_in_date_with_warn(ulonglong fuzzydate); + bool check_zero_in_date_with_warn(date_mode_t fuzzydate); static void do_field_time(Copy_field *copy); public: Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, @@ -3079,7 +3088,7 @@ public: double val_real(void); longlong val_int(void); String *val_str(String*,String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); bool send_binary(Protocol *protocol); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); @@ -3140,7 +3149,7 @@ public: ((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec); } int reset(void); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); uint32 pack_length() const { return Type_handler_time::hires_bytes(dec); } @@ -3191,14 +3200,17 @@ public: return memcmp(a_ptr, b_ptr, pack_length()); } int reset(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); uint size_of() const { return sizeof(*this); } }; class Field_datetime :public Field_temporal_with_date { void store_TIME(const MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; +protected: + int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str, + int was_cut); public: Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, @@ -3212,6 +3224,11 @@ public: } const Type_handler *type_handler() const { return &type_handler_datetime; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } + int store(const char *to, size_t length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(const MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3220,7 +3237,7 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_datetime::get_TIME(ltime, ptr, fuzzydate); } int set_time(); int evaluate_update_default_function() @@ -3291,7 +3308,7 @@ public: */ class Field_datetime_hires :public Field_datetime_with_dec { void store_TIME(const MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; public: Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, @@ -3303,7 +3320,7 @@ public: } int cmp(const uchar *,const uchar *); uint32 pack_length() const { return Type_handler_datetime::hires_bytes(dec); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } }; @@ -3314,7 +3331,7 @@ public: */ class Field_datetimef :public Field_datetime_with_dec { void store_TIME(const MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const; int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); @@ -3345,7 +3362,7 @@ public: return memcmp(a_ptr, b_ptr, pack_length()); } int reset(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index f413dec82be..8b3d9c04656 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2010, 2016, MariaDB + Copyright (c) 2010, 2018, MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -429,7 +429,7 @@ void Field::do_field_temporal(Copy_field *copy) { MYSQL_TIME ltime; // TODO: we now need to check result - if (copy->from_field->get_date(<ime, 0)) + if (copy->from_field->get_date(<ime, date_mode_t(0))) copy->to_field->reset(); else copy->to_field->store_time_dec(<ime, copy->from_field->decimals()); diff --git a/sql/filesort.cc b/sql/filesort.cc index 1855430b944..6f2a6096aa2 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1004,7 +1004,9 @@ Type_handler_string_result::make_sort_key(uchar *to, Item *item, if (use_strnxfrm(cs)) { - IF_DBUG(size_t tmp_length= ,) +#ifdef DBUG_ASSERT_EXISTS + size_t tmp_length= +#endif cs->coll->strnxfrm(cs, to, sort_field->length, item->max_char_length() * cs->strxfrm_multiply, @@ -1055,7 +1057,7 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item, Sort_param *param) const { MYSQL_TIME buf; - if (item->get_date_result(&buf, TIME_INVALID_DATES)) + if (item->get_date_result(current_thd, &buf, TIME_INVALID_DATES)) { DBUG_ASSERT(item->maybe_null); DBUG_ASSERT(item->null_value); diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc index ce1f4394ebd..9127bb95aeb 100644 --- a/sql/gcalc_slicescan.cc +++ b/sql/gcalc_slicescan.cc @@ -177,6 +177,17 @@ Gcalc_dyn_list::Gcalc_dyn_list(size_t blk_size, size_t sizeof_item): {} +Gcalc_dyn_list::Gcalc_dyn_list(const Gcalc_dyn_list &dl) +{ + m_blk_size= dl.m_blk_size; + m_sizeof_item= dl.m_sizeof_item; + m_points_per_blk= dl.m_points_per_blk; + m_blk_hook= &m_first_blk; + m_free= NULL; + m_keep= NULL; +} + + void Gcalc_dyn_list::format_blk(void* block) { Item *pi_end, *cur_pi, *first_pi; diff --git a/sql/gcalc_slicescan.h b/sql/gcalc_slicescan.h index b9516fc8d8c..ebf173c1a57 100644 --- a/sql/gcalc_slicescan.h +++ b/sql/gcalc_slicescan.h @@ -63,6 +63,7 @@ public: }; Gcalc_dyn_list(size_t blk_size, size_t sizeof_item); + Gcalc_dyn_list(const Gcalc_dyn_list &dl); ~Gcalc_dyn_list(); Item *new_item() { @@ -229,6 +230,12 @@ public: Gcalc_dyn_list(blk_size, sizeof(Info)), m_hook(&m_first), m_n_points(0) {} + + Gcalc_heap(const Gcalc_heap &gh) : + Gcalc_dyn_list(gh), + m_hook(&m_first), m_n_points(0) + {} + void set_extent(double xmin, double xmax, double ymin, double ymax); Info *new_point_info(double x, double y, gcalc_shape_info shape); void free_point_info(Info *i, Gcalc_dyn_list::Item **i_hook); diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc index 2aeaafc1a76..832a52db522 100644 --- a/sql/gcalc_tools.cc +++ b/sql/gcalc_tools.cc @@ -663,6 +663,17 @@ Gcalc_operation_reducer::Gcalc_operation_reducer(size_t blk_size) : {} +Gcalc_operation_reducer::Gcalc_operation_reducer( + const Gcalc_operation_reducer &gor) : + Gcalc_dyn_list(gor), +#ifndef GCALC_DBUG_OFF + n_res_points(0), +#endif /*GCALC_DBUG_OFF*/ + m_res_hook((Gcalc_dyn_list::Item **)&m_result), + m_first_active_thread(NULL) +{} + + void Gcalc_operation_reducer::init(Gcalc_function *fn, modes mode) { m_fn= fn; diff --git a/sql/gcalc_tools.h b/sql/gcalc_tools.h index 8bda3c144a6..4d5aec0d443 100644 --- a/sql/gcalc_tools.h +++ b/sql/gcalc_tools.h @@ -224,6 +224,7 @@ public: }; Gcalc_operation_reducer(size_t blk_size=8192); + Gcalc_operation_reducer(const Gcalc_operation_reducer &gor); void init(Gcalc_function *fn, modes mode= default_mode); Gcalc_operation_reducer(Gcalc_function *fn, modes mode= default_mode, size_t blk_size=8192); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 262e791ec7a..37a3decdbca 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -419,7 +419,7 @@ const char *ha_partition::table_type() const ha_partition::~ha_partition() { - DBUG_ENTER("ha_partition::~ha_partition()"); + DBUG_ENTER("ha_partition::~ha_partition"); if (m_new_partitions_share_refs.elements) m_new_partitions_share_refs.delete_elements(); if (m_file != NULL) @@ -634,7 +634,7 @@ int ha_partition::create_partitioning_metadata(const char *path, const char *old_path, int action_flag) { - DBUG_ENTER("ha_partition::create_partitioning_metadata()"); + DBUG_ENTER("ha_partition::create_partitioning_metadata"); /* We need to update total number of parts since we might write the handler @@ -4034,9 +4034,14 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, } else { - for (i= bitmap_get_first_set(&(m_part_info->lock_partitions)); + MY_BITMAP *used_partitions= lock_type == TL_UNLOCK || + lock_type == TL_IGNORE ? + &m_locked_partitions : + &m_part_info->lock_partitions; + + for (i= bitmap_get_first_set(used_partitions); i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) + i= bitmap_get_next_set(used_partitions, i)) { DBUG_PRINT("info", ("store lock %u iteration", i)); to= m_file[i]->store_lock(thd, to, lock_type); @@ -8471,6 +8476,24 @@ err_handler: } +static int extra_cb(handler *h, void *operation) +{ + return h->extra(*(enum ha_extra_function*)operation); +} + + +static int start_keyread_cb(handler* h, void *p) +{ + return h->ha_start_keyread(*(uint*)p); +} + + +static int end_keyread_cb(handler* h, void *unused) +{ + return h->ha_end_keyread(); +} + + /** General function to prepare handler for certain behavior. @@ -8799,11 +8822,12 @@ int ha_partition::extra(enum ha_extra_function operation) switch (operation) { /* Category 1), used by most handlers */ - case HA_EXTRA_KEYREAD: case HA_EXTRA_NO_KEYREAD: + DBUG_RETURN(loop_partitions(end_keyread_cb, NULL)); + case HA_EXTRA_KEYREAD: case HA_EXTRA_FLUSH: case HA_EXTRA_PREPARE_FOR_FORCED_CLOSE: - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); case HA_EXTRA_PREPARE_FOR_RENAME: case HA_EXTRA_FORCE_REOPEN: DBUG_RETURN(loop_extra_alter(operation)); @@ -8815,7 +8839,7 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_KEYREAD_PRESERVE_FIELDS: { if (!m_myisam) - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); } break; @@ -8842,7 +8866,7 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_REMEMBER_POS: case HA_EXTRA_RESTORE_POS: { - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); } case HA_EXTRA_NO_READCHECK: { @@ -8874,7 +8898,7 @@ int ha_partition::extra(enum ha_extra_function operation) m_extra_cache_size= 0; m_extra_prepare_for_update= FALSE; m_extra_cache_part_id= NO_CURRENT_PART_ID; - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); } case HA_EXTRA_IGNORE_NO_KEY: case HA_EXTRA_NO_IGNORE_NO_KEY: @@ -8895,11 +8919,11 @@ int ha_partition::extra(enum ha_extra_function operation) At this time, this is safe by limitation of ha_partition */ - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); } /* Category 7), used by federated handlers */ case HA_EXTRA_INSERT_WITH_UPDATE: - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); /* Category 8) Operations only used by NDB */ case HA_EXTRA_DELETE_CANNOT_BATCH: case HA_EXTRA_UPDATE_CANNOT_BATCH: @@ -8909,13 +8933,13 @@ int ha_partition::extra(enum ha_extra_function operation) } /* Category 9) Operations only used by MERGE */ case HA_EXTRA_ADD_CHILDREN_LIST: - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); case HA_EXTRA_ATTACH_CHILDREN: { int result; uint num_locks; handler **file; - if ((result= loop_extra(operation))) + if ((result= loop_partitions(extra_cb, &operation))) DBUG_RETURN(result); /* Recalculate lock count as each child may have different set of locks */ @@ -8930,9 +8954,9 @@ int ha_partition::extra(enum ha_extra_function operation) break; } case HA_EXTRA_IS_ATTACHED_CHILDREN: - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); case HA_EXTRA_DETACH_CHILDREN: - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); case HA_EXTRA_MARK_AS_LOG_TABLE: /* http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html @@ -8944,7 +8968,7 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_BEGIN_ALTER_COPY: case HA_EXTRA_END_ALTER_COPY: case HA_EXTRA_FAKE_START_STMT: - DBUG_RETURN(loop_extra(operation)); + DBUG_RETURN(loop_partitions(extra_cb, &operation)); default: { /* Temporary crash to discover what is wrong */ @@ -8952,7 +8976,7 @@ int ha_partition::extra(enum ha_extra_function operation) break; } } - DBUG_RETURN(0); + DBUG_RETURN(1); } @@ -8989,26 +9013,42 @@ int ha_partition::reset(void) DBUG_RETURN(result); } -/* - Special extra method for HA_EXTRA_CACHE with cachesize as extra parameter +/** + Special extra method with additional parameter + See @ref ha_partition::extra - SYNOPSIS - extra_opt() - operation Must be HA_EXTRA_CACHE - cachesize Size of cache in full table scan + @param[in] operation operation to execute + @param[in] arg extra argument - RETURN VALUE - >0 Error code - 0 Success + @return status + @retval 0 success + @retval >0 error code + + @detail + Operations supported by extra_opt: + HA_EXTRA_KEYREAD: + arg is interpreted as key index + HA_EXTRA_CACHE: + arg is interpreted as size of cache in full table scan + + For detailed description refer to @ref ha_partition::extra */ -int ha_partition::extra_opt(enum ha_extra_function operation, ulong cachesize) +int ha_partition::extra_opt(enum ha_extra_function operation, ulong arg) { - DBUG_ENTER("ha_partition::extra_opt()"); + DBUG_ENTER("ha_partition::extra_opt"); - DBUG_ASSERT(HA_EXTRA_CACHE == operation); - prepare_extra_cache(cachesize); - DBUG_RETURN(0); + switch (operation) + { + case HA_EXTRA_KEYREAD: + DBUG_RETURN(loop_partitions(start_keyread_cb, &arg)); + case HA_EXTRA_CACHE: + prepare_extra_cache(arg); + DBUG_RETURN(0); + default: + DBUG_ASSERT(0); + } + DBUG_RETURN(1); } @@ -9025,7 +9065,7 @@ int ha_partition::extra_opt(enum ha_extra_function operation, ulong cachesize) void ha_partition::prepare_extra_cache(uint cachesize) { - DBUG_ENTER("ha_partition::prepare_extra_cache()"); + DBUG_ENTER("ha_partition::prepare_extra_cache"); DBUG_PRINT("enter", ("cachesize %u", cachesize)); m_extra_cache= TRUE; @@ -9055,7 +9095,7 @@ int ha_partition::loop_extra_alter(enum ha_extra_function operation) { int result= 0, tmp; handler **file; - DBUG_ENTER("ha_partition::loop_extra_alter()"); + DBUG_ENTER("ha_partition::loop_extra_alter"); DBUG_ASSERT(operation == HA_EXTRA_PREPARE_FOR_RENAME || operation == HA_EXTRA_FORCE_REOPEN); @@ -9071,28 +9111,28 @@ int ha_partition::loop_extra_alter(enum ha_extra_function operation) if ((tmp= (*file)->extra(operation))) result= tmp; } - if ((tmp= loop_extra(operation))) + if ((tmp= loop_partitions(extra_cb, &operation))) result= tmp; DBUG_RETURN(result); } -/* - Call extra on all partitions - SYNOPSIS - loop_extra() - operation extra operation type +/** + Call callback(part, param) on all partitions - RETURN VALUE - >0 Error code - 0 Success + @param callback a callback to call for each partition + @param param a void*-parameter passed to callback + + @return Operation status + @retval >0 Error code + @retval 0 Success */ -int ha_partition::loop_extra(enum ha_extra_function operation) +int ha_partition::loop_partitions(handler_callback callback, void *param) { int result= 0, tmp; uint i; - DBUG_ENTER("ha_partition::loop_extra()"); + DBUG_ENTER("ha_partition::loop_partitions"); for (i= bitmap_get_first_set(&m_part_info->lock_partitions); i < m_tot_parts; @@ -9103,7 +9143,7 @@ int ha_partition::loop_extra(enum ha_extra_function operation) In this case calling 'extra' can crash. */ if (bitmap_is_set(&m_opened_partitions, i) && - (tmp= m_file[i]->extra(operation))) + (tmp= callback(m_file[i], param))) result= tmp; } /* Add all used partitions to be called in reset(). */ @@ -10856,11 +10896,9 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt) } m_part_info->key_algorithm= partition_info::KEY_ALGORITHM_51; if (skip_generation || - !(part_buf= generate_partition_syntax(thd, m_part_info, - &part_buf_len, - true, - NULL, - NULL)) || + !(part_buf= generate_partition_syntax_for_frm(thd, m_part_info, + &part_buf_len, + NULL, NULL)) || print_admin_msg(thd, SQL_ADMIN_MSG_TEXT_SIZE + 1, "error", table_share->db.str, table->alias, @@ -10908,6 +10946,18 @@ TABLE_LIST *ha_partition::get_next_global_for_child() } +/** + Push an engine condition to the condition stack of the storage engine + for each partition. + + @param cond Pointer to the engine condition to be pushed. + + @return NULL Underlying engine will not return rows that + do not match the passed condition. + <> NULL 'Remainder' condition that the caller must use + to filter out records. +*/ + const COND *ha_partition::cond_push(const COND *cond) { handler **file= m_file; @@ -10944,10 +10994,15 @@ const COND *ha_partition::cond_push(const COND *cond) } +/** + Pop the top condition from the condition stack of the storage engine + for each partition. +*/ + void ha_partition::cond_pop() { handler **file= m_file; - DBUG_ENTER("ha_partition::cond_push"); + DBUG_ENTER("ha_partition::cond_pop"); do { diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 8a251016703..202f27840dc 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -349,7 +349,6 @@ private: /* Variables for lock structures. */ - THR_LOCK_DATA lock; /* MySQL lock */ bool auto_increment_lock; /**< lock reading/updating auto_inc */ /** @@ -844,7 +843,7 @@ public: int change_partitions_to_open(List<String> *partition_names); int open_read_partitions(char *name_buff, size_t name_buff_size); virtual int extra(enum ha_extra_function operation); - virtual int extra_opt(enum ha_extra_function operation, ulong cachesize); + virtual int extra_opt(enum ha_extra_function operation, ulong arg); virtual int reset(void); virtual uint count_query_cache_dependant_tables(uint8 *tables_type); virtual my_bool @@ -854,6 +853,8 @@ public: uint *n); private: + typedef int handler_callback(handler *, void *); + my_bool reg_query_cache_dependant_table(THD *thd, char *engine_key, uint engine_key_len, @@ -864,7 +865,7 @@ private: **block_table, handler *file, uint *n); static const uint NO_CURRENT_PART_ID= NOT_A_PARTITION_ID; - int loop_extra(enum ha_extra_function operation); + int loop_partitions(handler_callback callback, void *param); int loop_extra_alter(enum ha_extra_function operations); void late_extra_cache(uint partition_id); void late_extra_no_cache(uint partition_id); @@ -1039,6 +1040,10 @@ public: with hidden primary key) (No handler has this limitation currently) + HA_WANTS_PRIMARY_KEY: + Can't define a table without primary key except sequences + (Only InnoDB has this when using innodb_force_primary_key == ON) + HA_STATS_RECORDS_IS_EXACT: Does the counter of records after the info call specify an exact value or not. If it does this flag is set. diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc new file mode 100644 index 00000000000..b37b4dedad1 --- /dev/null +++ b/sql/handle_connections_win.cc @@ -0,0 +1,555 @@ +/* Copyright (c) 2018 MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/* Accepting connections on Windows */ + +#include <my_global.h> +#include <sql_class.h> +#include <sql_connect.h> +#include <mysqld.h> +#include <mswsock.h> +#include <mysql/psi/mysql_socket.h> +#include <sddl.h> + +#include <handle_connections_win.h> + +/* From mysqld.cc */ +extern HANDLE hEventShutdown; +extern MYSQL_SOCKET base_ip_sock, extra_ip_sock; +extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ(); +extern void tp_win_callback_prolog(); +static SECURITY_ATTRIBUTES pipe_security; + +/** + Abstract base class for accepting new connection, + asynchronously (i.e the accept() operation can be posted, + and result is retrieved later) , and creating a new connection. +*/ + +struct Listener +{ + /** Windows handle of the Listener. + Subclasses would use SOCKET or named pipe handle + */ + HANDLE m_handle; + /** Required for all async IO*/ + OVERLAPPED m_overlapped; + + /** Create new listener + @param handle - @see m_handle + @param wait_handle - usually, event handle or INVALID_HANDLE_VALUE + @see wait_handle + */ + Listener(HANDLE handle, HANDLE wait_handle): + m_handle(handle), m_overlapped() + { + m_overlapped.hEvent= wait_handle; + } + + /** + if not NULL, this handle can be be used in WaitForSingle/MultipleObject(s). + This handle will be closed when object is destroyed. + + If NULL, the completion notification happens in threadpool. + */ + HANDLE wait_handle() + { + return m_overlapped.hEvent; + } + + /* Start waiting for new client connection. */ + virtual void begin_accept()= 0; + + /** + Completion callback,called whenever IO posted by begin_accept is finisjed + Listener needs to create a new THD then (or, call scheduler so it creates one) + + @param success - whether IO completed successfull + */ + virtual void completion_callback(bool success)= 0; + + /** + Completion callback for Listener, that uses events for waiting + to IO. Not suitable for threadpool etc. Retrieves the status of + completed IO from the OVERLAPPED structure + */ + void completion_callback() + { + DBUG_ASSERT(wait_handle() && (wait_handle() != INVALID_HANDLE_VALUE)); + DWORD bytes; + return completion_callback( + GetOverlappedResult(wait_handle(), &m_overlapped, &bytes, FALSE)); + } + + /** Cancel an in-progress IO. Useful for threadpool-bound IO */ + void cancel() + { + CancelIoEx(m_handle, &m_overlapped); + } + + /* Destructor. Closes wait handle, if it was passed in constructor */ + virtual ~Listener() + { + if (m_overlapped.hEvent) + CloseHandle(m_overlapped.hEvent); + }; +}; + +/* Winsock extension finctions. */ +static LPFN_ACCEPTEX my_AcceptEx; +static LPFN_GETACCEPTEXSOCKADDRS my_GetAcceptExSockaddrs; + +/** + Listener that handles socket connections. + Can be threadpool-bound (i.e the completion is executed in threadpool thread), + or use events for waits. + + Threadpool-bound listener should be used with theradpool scheduler, for better + performance. +*/ +struct Socket_Listener: public Listener +{ + /** Client socket passed to AcceptEx() call.*/ + SOCKET m_client_socket; + + /** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */ + char m_buffer[2 * sizeof(sockaddr_storage) + 32]; + + /* Threadpool IO struct.*/ + PTP_IO m_tp_io; + + /** + Callback for Windows threadpool's StartThreadpoolIo() function. + */ + static void CALLBACK tp_accept_completion_callback( + PTP_CALLBACK_INSTANCE, PVOID context, PVOID , ULONG io_result, + ULONG_PTR, PTP_IO io) + { + tp_win_callback_prolog(); + Listener *listener= (Listener *)context; + + if (io_result == ERROR_OPERATION_ABORTED) + { + /* ERROR_OPERATION_ABORTED caused by CancelIoEx()*/ + CloseThreadpoolIo(io); + delete listener; + return; + } + listener->completion_callback(io_result == 0); + } + + /** + Constructor + @param listen_socket - listening socket + @PTP_CALLBACK_ENVIRON callback_environ - threadpool environment, or NULL + if threadpool is not used for completion callbacks. + */ + Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) : + Listener((HANDLE)listen_socket.fd,0), + m_client_socket(INVALID_SOCKET) + { + if (callback_environ) + { + /* Accept executed in threadpool. */ + m_tp_io= CreateThreadpoolIo(m_handle, + tp_accept_completion_callback, this, callback_environ); + } + else + { + /* Completion signaled via event. */ + m_tp_io= 0; + m_overlapped.hEvent= CreateEvent(0, FALSE , FALSE, 0); + } + } + + /* + Use AcceptEx to asynchronously wait for new connection; + */ + void begin_accept() + { +retry : + m_client_socket= socket(server_socket_ai_family, SOCK_STREAM, IPPROTO_TCP); + if (m_client_socket == INVALID_SOCKET) + { + sql_perror("socket() call failed."); + unireg_abort(1); + } + + DWORD bytes_received; + if (m_tp_io) + StartThreadpoolIo(m_tp_io); + + BOOL ret= my_AcceptEx( + (SOCKET)m_handle, + m_client_socket, + m_buffer, + 0, + sizeof(sockaddr_storage) + 16, + sizeof(sockaddr_storage) + 16, + &bytes_received, + &m_overlapped); + + DWORD last_error= ret? 0: WSAGetLastError(); + if (last_error == WSAECONNRESET) + { + if (m_tp_io) + CancelThreadpoolIo(m_tp_io); + goto retry; + } + + if (ret || last_error == ERROR_IO_PENDING || abort_loop) + return; + + sql_print_error("my_AcceptEx failed, last error %u", last_error); + abort(); + } + + /* Create new socket connection.*/ + void completion_callback(bool success) + { + if (!success) + { + /* my_AcceptEx() returned error */ + closesocket(m_client_socket); + begin_accept(); + return; + } + + MYSQL_SOCKET s_client{m_client_socket}; + MYSQL_SOCKET s_listen{(SOCKET)m_handle}; + +#ifdef HAVE_PSI_SOCKET_INTERFACE + /* Parse socket addresses buffer filled by AcceptEx(), + only needed for PSI instrumentation. */ + sockaddr *local_addr, *remote_addr; + int local_addr_len, remote_addr_len; + + my_GetAcceptExSockaddrs(m_buffer, + 0, sizeof(sockaddr_storage) + 16, sizeof(sockaddr_storage) + 16, + &local_addr, &local_addr_len, &remote_addr, &remote_addr_len); + + s_client.m_psi= PSI_SOCKET_CALL(init_socket) + (key_socket_client_connection, (const my_socket*)&s_listen.fd, remote_addr, remote_addr_len); +#endif + + /* Start accepting new connection. After this point, do not use + any member data, they could be used by a different (threadpool) thread. */ + begin_accept(); + + /* Some chores post-AcceptEx() that we need to create a normal socket.*/ + if (setsockopt(s_client.fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char *)&s_listen.fd, sizeof(s_listen.fd))) + { + if (!abort_loop) + { + sql_perror("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed."); + abort(); + } + } + + /* Create a new connection.*/ + handle_accepted_socket(s_client, s_listen); + } + + ~Socket_Listener() + { + if (m_client_socket != INVALID_SOCKET) + closesocket(m_client_socket); + } + + /* + Retrieve the pointer to the Winsock extension functions + AcceptEx and GetAcceptExSockaddrs. + */ + static void init_winsock_extensions() + { + SOCKET s= mysql_socket_getfd(base_ip_sock); + if (s == INVALID_SOCKET) + s= mysql_socket_getfd(extra_ip_sock); + if (s == INVALID_SOCKET) + { + /* --skip-networking was used*/ + return; + } + GUID guid_AcceptEx= WSAID_ACCEPTEX; + GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS; + + GUID *guids[]= { &guid_AcceptEx, &guid_GetAcceptExSockaddrs }; + void *funcs[]= { &my_AcceptEx, &my_GetAcceptExSockaddrs }; + DWORD bytes; + for (int i= 0; i < array_elements(guids); i++) + { + if (WSAIoctl(s, + SIO_GET_EXTENSION_FUNCTION_POINTER, + guids[i], sizeof(GUID), + funcs[i], sizeof(void *), + &bytes, 0, 0) == -1) + { + sql_print_error("WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER) failed"); + unireg_abort(1); + } + } + } +}; + + +/** + Pipe Listener. + Only event notification mode is implemented, no threadpool +*/ +struct Pipe_Listener : public Listener +{ + PTP_CALLBACK_ENVIRON m_tp_env; + Pipe_Listener(): + Listener(INVALID_HANDLE_VALUE, CreateEvent(0, FALSE, FALSE, 0)), + m_tp_env(get_threadpool_win_callback_environ()) + { + } + + /* + Creates local named pipe instance \\.\pipe\$socket for named pipe connection. + */ + static HANDLE create_named_pipe() + { + static bool first_instance= true; + static char pipe_name[512]; + DWORD open_mode= PIPE_ACCESS_DUPLEX | + FILE_FLAG_OVERLAPPED; + + if (first_instance) + { + snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\%s", mysqld_unix_port); + open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( + "S:(ML;; NW;;; LW) D:(A;; FRFW;;; WD)", + 1, &pipe_security.lpSecurityDescriptor, NULL)) + { + sql_perror("Can't start server : Initialize security descriptor"); + unireg_abort(1); + } + pipe_security.nLength= sizeof(SECURITY_ATTRIBUTES); + pipe_security.bInheritHandle= FALSE; + } + HANDLE pipe_handle= CreateNamedPipe(pipe_name, + open_mode, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + (int)global_system_variables.net_buffer_length, + (int)global_system_variables.net_buffer_length, + NMPWAIT_USE_DEFAULT_WAIT, + &pipe_security); + if (pipe_handle == INVALID_HANDLE_VALUE) + { + sql_perror("Create named pipe failed"); + sql_print_error("Aborting\n"); + exit(1); + } + first_instance= false; + return pipe_handle; + } + + static void create_pipe_connection(HANDLE pipe) + { + CONNECT *connect; + if (!(connect= new CONNECT) || !(connect->vio= vio_new_win32pipe(pipe))) + { + CloseHandle(pipe); + delete connect; + statistic_increment(aborted_connects, &LOCK_status); + statistic_increment(connection_errors_internal, &LOCK_status); + return; + } + connect->host= my_localhost; + create_new_thread(connect); + } + + /* Threadpool callback.*/ + static void CALLBACK tp_create_pipe_connection( + PTP_CALLBACK_INSTANCE,void *Context) + { + tp_win_callback_prolog(); + create_pipe_connection(Context); + } + + void begin_accept() + { + m_handle= create_named_pipe(); + BOOL connected= ConnectNamedPipe(m_handle, &m_overlapped); + if (connected) + { + /* Overlapped ConnectNamedPipe should return zero. */ + sql_perror("Overlapped ConnectNamedPipe() already connected."); + abort(); + } + DWORD last_error= GetLastError(); + switch (last_error) + { + case ERROR_PIPE_CONNECTED: + /* Client is already connected, so signal an event.*/ + { + /* + Cleanup overlapped (so that subsequent GetOverlappedResult() + does not show results of previous IO + */ + HANDLE e= m_overlapped.hEvent; + memset(&m_overlapped, 0, sizeof(m_overlapped)); + m_overlapped.hEvent = e; + } + if (!SetEvent(m_overlapped.hEvent)) + { + sql_perror("SetEvent() failed for connected pipe."); + abort(); + } + break; + case ERROR_IO_PENDING: + break; + default: + sql_perror("ConnectNamedPipe() failed."); + abort(); + break; + } + } + + void completion_callback(bool success) + { + if (!success) + { +#ifdef DBUG_OFF + sql_print_warning("ConnectNamedPipe completed with %u", GetLastError()); +#endif + CloseHandle(m_handle); + m_handle= INVALID_HANDLE_VALUE; + begin_accept(); + return; + } + HANDLE pipe= m_handle; + begin_accept(); + // If threadpool is on, create connection in threadpool thread + if (!m_tp_env || !TrySubmitThreadpoolCallback(tp_create_pipe_connection, pipe, m_tp_env)) + create_pipe_connection(pipe); + } + + ~Pipe_Listener() + { + if (m_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(m_handle); + } + } + + static void cleanup() + { + LocalFree(pipe_security.lpSecurityDescriptor); + } +}; + +/** + Accept new client connections on Windows. + + Since we deal with pipe and sockets, they cannot be put into a select/loop. + But we can use asynchronous IO, and WaitForMultipleObject() loop. + + In addition, for slightly better performance, if we're using threadpool, + socket connections are accepted directly in the threadpool. + + The mode of operation is therefore + + 1. There is WaitForMultipleObject() loop that waits for shutdown notification + (hEventShutdown),and possibly pipes and sockets(e.g if threadpool is not used) + This loop ends when shutdown notification is detected. + + 2. If threadpool is used, new socket connections are accepted there. +*/ + + +#define MAX_WAIT_HANDLES 32 +#define NUM_PIPE_LISTENERS 24 +#define SHUTDOWN_IDX 0 +#define LISTENER_START_IDX 1 + +void handle_connections_win() +{ + Listener* all_listeners[MAX_WAIT_HANDLES]= {}; + HANDLE wait_events[MAX_WAIT_HANDLES]= {}; + int n_listeners= 0; + int n_waits= 0; + + Socket_Listener::init_winsock_extensions(); + + /* Listen for TCP connections on "extra-port" (no threadpool).*/ + if (extra_ip_sock.fd != INVALID_SOCKET) + all_listeners[n_listeners++]= new Socket_Listener(extra_ip_sock, 0); + + /* Listen for named pipe connections */ + if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) + { + /* + Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side. + */ + for (int i= 0; i < NUM_PIPE_LISTENERS; i++) + all_listeners[n_listeners++]= new Pipe_Listener(); + } + + if (base_ip_sock.fd != INVALID_SOCKET) + { + /* Wait for TCP connections.*/ + SetFileCompletionNotificationModes((HANDLE)base_ip_sock.fd, FILE_SKIP_SET_EVENT_ON_HANDLE); + all_listeners[n_listeners++]= new Socket_Listener(base_ip_sock, get_threadpool_win_callback_environ()); + } + + if (!n_listeners && !opt_bootstrap) + { + sql_print_error("Either TCP connections or named pipe connections must be enabled."); + unireg_abort(1); + } + + wait_events[SHUTDOWN_IDX]= hEventShutdown; + n_waits = 1; + + for (int i= 0; i < n_listeners; i++) + { + HANDLE wait_handle= all_listeners[i]->wait_handle(); + if(wait_handle) + { + DBUG_ASSERT((i == 0) || (all_listeners[i-1]->wait_handle() != 0)); + wait_events[n_waits++]= wait_handle; + } + all_listeners[i]->begin_accept(); + } + + for (;;) + { + DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE); + DBUG_ASSERT((int)idx >= 0 && (int)idx < n_waits); + + if (idx == SHUTDOWN_IDX) + break; + + all_listeners[idx - LISTENER_START_IDX]->completion_callback(); + } + + /* Cleanup */ + for (int i= 0; i < n_listeners; i++) + { + Listener *listener= all_listeners[i]; + if (listener->wait_handle()) + delete listener; + else + // Threadpool-bound listener will be deleted in threadpool + // Do not call destructor, because callback maybe running. + listener->cancel(); + } + Pipe_Listener::cleanup(); +}
\ No newline at end of file diff --git a/sql/handle_connections_win.h b/sql/handle_connections_win.h new file mode 100644 index 00000000000..a81f4346fb2 --- /dev/null +++ b/sql/handle_connections_win.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2018 MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/** + Handles incoming socket and pipe connections, on Windows. + Creates new (THD) connections.. +*/ +extern void handle_connections_win(); diff --git a/sql/handler.cc b/sql/handler.cc index d48a4660d52..e5081accb30 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -21,6 +21,7 @@ */ #include "mariadb.h" +#include <inttypes.h> #include "sql_priv.h" #include "unireg.h" #include "rpl_rli.h" @@ -74,7 +75,7 @@ KEY_CREATE_INFO default_key_create_info= ulong total_ha= 0; /* number of storage engines (from handlertons[]) that support 2pc */ ulong total_ha_2pc= 0; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS /* Number of non-mandatory 2pc handlertons whose initialization failed to estimate total_ha_2pc value under supposition of the failures @@ -661,7 +662,7 @@ err_deinit: (void) plugin->plugin->deinit(NULL); err: -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (hton->prepare && hton->state == SHOW_OPTION_YES) failed_ha_2pc++; #endif @@ -3086,6 +3087,45 @@ void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr) } +/** @brief + Computes the largest number X: + - smaller than or equal to "nr" + - of the form: auto_increment_offset + N * auto_increment_increment + where N>=0. + + SYNOPSIS + prev_insert_id + nr Number to "round down" + variables variables struct containing auto_increment_increment and + auto_increment_offset + + RETURN + The number X if it exists, "nr" otherwise. +*/ +inline ulonglong +prev_insert_id(ulonglong nr, struct system_variables *variables) +{ + if (unlikely(nr < variables->auto_increment_offset)) + { + /* + There's nothing good we can do here. That is a pathological case, where + the offset is larger than the column's max possible value, i.e. not even + the first sequence value may be inserted. User will receive warning. + */ + DBUG_PRINT("info",("auto_increment: nr: %lu cannot honour " + "auto_increment_offset: %lu", + (ulong) nr, variables->auto_increment_offset)); + return nr; + } + if (variables->auto_increment_increment == 1) + return nr; // optimization of the formula below + nr= (((nr - variables->auto_increment_offset)) / + (ulonglong) variables->auto_increment_increment); + return (nr * (ulonglong) variables->auto_increment_increment + + variables->auto_increment_offset); +} + + /** Update the auto_increment field if necessary. @@ -4593,6 +4633,7 @@ handler::ha_create_partitioning_metadata(const char *name, (!old_name && strcmp(name, table_share->path.str))); + mark_trx_read_write(); return create_partitioning_metadata(name, old_name, action_flag); } @@ -6521,7 +6562,7 @@ void ha_fake_trx_id(THD *thd) if (thd->wsrep_ws_handle.trx_id != WSREP_UNDEFINED_TRX_ID) { - WSREP_DEBUG("fake trx id skipped: %lu", thd->wsrep_ws_handle.trx_id); + WSREP_DEBUG("fake trx id skipped: %" PRIu64, thd->wsrep_ws_handle.trx_id); DBUG_VOID_RETURN; } @@ -6786,7 +6827,7 @@ int del_global_index_stats_for_table(THD *thd, uchar* cache_key, size_t cache_ke /* Remove a table from global table statistics */ -int del_global_table_stat(THD *thd, LEX_CSTRING *db, LEX_CSTRING *table) +int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table) { TABLE_STATS *table_stats; int res = 0; @@ -6927,7 +6968,7 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info) return false; } -bool Table_scope_and_contents_source_st::vers_native(THD *thd) const +bool Table_scope_and_contents_source_pod_st::vers_native(THD *thd) const { if (ha_check_storage_engine_flag(db_type, HTON_NATIVE_SYS_VERSIONING)) return true; diff --git a/sql/handler.h b/sql/handler.h index bfc8bd774b3..e5a6deb6659 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2,7 +2,7 @@ #define HANDLER_INCLUDED /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2017, MariaDB Corporation. + Copyright (c) 2009, 2018, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -151,7 +151,6 @@ enum enum_alter_inplace_result { #define HA_HAS_OLD_CHECKSUM (1ULL << 24) /* Table data are stored in separate files (for lower_case_table_names) */ #define HA_FILE_BASED (1ULL << 26) -#define HA_NO_VARCHAR (1ULL << 27) /* unused */ #define HA_CAN_BIT_FIELD (1ULL << 28) /* supports bit fields */ #define HA_NEED_READ_RANGE_BUFFER (1ULL << 29) /* for read_multi_range */ #define HA_ANY_INDEX_MAY_BE_UNIQUE (1ULL << 30) @@ -302,6 +301,9 @@ enum enum_alter_inplace_result { /* calling cmp_ref() on the engine is expensive */ #define HA_CMP_REF_IS_EXPENSIVE (1ULL << 54) +/* Engine wants primary keys for everything except sequences */ +#define HA_WANTS_PRIMARY_KEY (1ULL << 55) + /* bits in index_flags(index_number) for what you can do with index */ #define HA_READ_NEXT 1 /* TODO really use this flag */ #define HA_READ_PREV 2 /* supports ::index_prev */ @@ -818,7 +820,7 @@ struct xid_t { bqual_length= b; memcpy(data, d, g+b); } - bool is_null() { return formatID == -1; } + bool is_null() const { return formatID == -1; } void null() { formatID= -1; } my_xid quick_get_my_xid() { @@ -950,6 +952,7 @@ enum enum_schema_tables SCH_ALL_PLUGINS, SCH_APPLICABLE_ROLES, SCH_CHARSETS, + SCH_CHECK_CONSTRAINTS, SCH_COLLATIONS, SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_COLUMNS, @@ -1905,6 +1908,8 @@ enum vers_sys_type_t VERS_TRX_ID }; +extern const LEX_CSTRING null_clex_str; + struct Vers_parse_info { Vers_parse_info() : @@ -1913,6 +1918,15 @@ struct Vers_parse_info unversioned_fields(false) {} + void init() // Deep initialization + { + system_time= start_end_t(null_clex_str, null_clex_str); + as_row= start_end_t(null_clex_str, null_clex_str); + check_unit= VERS_UNDEFINED; + versioned_fields= false; + unversioned_fields= false; + } + struct start_end_t { start_end_t() @@ -1992,7 +2006,7 @@ public: - [AS] SELECT ... // Copy structure from a subquery */ -struct Table_scope_and_contents_source_st +struct Table_scope_and_contents_source_pod_st // For trivial members { CHARSET_INFO *table_charset; LEX_CUSTRING tabledef_version; @@ -2018,7 +2032,6 @@ struct Table_scope_and_contents_source_st uint options; /* OR of HA_CREATE_ options */ uint merge_insert_method; uint extra_size; /* length of extra data segment */ - SQL_I_List<TABLE_LIST> merge_list; handlerton *db_type; /** Row type of the table definition. @@ -2052,15 +2065,6 @@ struct Table_scope_and_contents_source_st bool table_was_deleted; sequence_definition *seq_create_info; - Vers_parse_info vers_info; - - bool vers_fix_system_fields(THD *thd, Alter_info *alter_info, - const TABLE_LIST &create_table, - bool create_select= false); - - bool vers_check_system_fields(THD *thd, Alter_info *alter_info, - const TABLE_LIST &create_table); - bool vers_native(THD *thd) const; void init() @@ -2081,6 +2085,30 @@ struct Table_scope_and_contents_source_st }; +struct Table_scope_and_contents_source_st: + public Table_scope_and_contents_source_pod_st +{ + SQL_I_List<TABLE_LIST> merge_list; + + Vers_parse_info vers_info; + + void init() + { + Table_scope_and_contents_source_pod_st::init(); + merge_list.empty(); + vers_info.init(); + } + + bool vers_fix_system_fields(THD *thd, Alter_info *alter_info, + const TABLE_LIST &create_table, + bool create_select= false); + + bool vers_check_system_fields(THD *thd, Alter_info *alter_info, + const TABLE_LIST &create_table); + +}; + + /** This struct is passed to handler table routines, e.g. ha_create(). It does not include the "OR REPLACE" and "IF NOT EXISTS" parts, as these @@ -3096,7 +3124,7 @@ public: bool keyread_enabled() { return keyread < MAX_KEY; } int ha_start_keyread(uint idx) { - int res= keyread_enabled() ? 0 : extra(HA_EXTRA_KEYREAD); + int res= keyread_enabled() ? 0 : extra_opt(HA_EXTRA_KEYREAD, idx); keyread= idx; return res; } @@ -3227,7 +3255,7 @@ public: /* True if changes to the table is persistent (no rollback) - This is manly used to decide how to log changes to the table in + This is mainly used to decide how to log changes to the table in the binary log. */ bool has_transactions() @@ -3610,7 +3638,7 @@ public: { return 0; } virtual int extra(enum ha_extra_function operation) { return 0; } - virtual int extra_opt(enum ha_extra_function operation, ulong cache_size) + virtual int extra_opt(enum ha_extra_function operation, ulong arg) { return extra(operation); } /** @@ -4831,5 +4859,5 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag); void print_keydup_error(TABLE *table, KEY *key, myf errflag); int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); -int del_global_table_stat(THD *thd, LEX_CSTRING *db, LEX_CSTRING *table); +int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table); #endif /* HANDLER_INCLUDED */ diff --git a/sql/init.h b/sql/init.h index e8dec0c1e2e..e96bb478cee 100644 --- a/sql/init.h +++ b/sql/init.h @@ -17,6 +17,6 @@ #define INIT_INCLUDED void unireg_init(ulong options); -ATTRIBUTE_NORETURN void unireg_end(void); +void unireg_end(void); #endif /* INIT_INCLUDED */ diff --git a/sql/item.cc b/sql/item.cc index 8782efc88e3..d4cfa790986 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. + Copyright (c) 2000, 2018, Oracle and/or its affiliates. Copyright (c) 2010, 2018, MariaDB Corporation This program is free software; you can redistribute it and/or modify @@ -115,14 +115,15 @@ void Item::push_note_converted_to_positive_complement(THD *thd) } -longlong Item::val_datetime_packed_result() +longlong Item::val_datetime_packed_result(THD *thd) { MYSQL_TIME ltime, tmp; - if (get_date_result(<ime, Datetime::comparison_flags_for_get_date())) + if (get_date_result(thd, <ime, Datetime::comparison_flags_for_get_date())) return 0; if (ltime.time_type != MYSQL_TIMESTAMP_TIME) return pack_time(<ime); - if ((null_value= time_to_datetime_with_warn(current_thd, <ime, &tmp, 0))) + if ((null_value= time_to_datetime_with_warn(thd, <ime, + &tmp, date_mode_t(0)))) return 0; return pack_time(&tmp); } @@ -293,7 +294,7 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) int Item::save_time_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_time(<ime)) + if (get_time(field->table->in_use, <ime)) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); @@ -303,7 +304,8 @@ int Item::save_time_in_field(Field *field, bool no_conversions) int Item::save_date_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_date(<ime, sql_mode_for_dates(field->table->in_use))) + THD *thd= field->table->in_use; + if (get_date(thd, <ime, sql_mode_for_dates(thd))) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); @@ -1272,64 +1274,30 @@ Item *Item_param::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) As a extra convenience the time structure is reset on error or NULL values! */ -bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::get_date_from_int(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - longlong value= val_int(); - bool neg= !unsigned_flag && value < 0; - if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, - ltime, fuzzydate, - field_name_or_null())) - return null_value|= make_zero_date(ltime, fuzzydate); - return null_value= false; + Longlong_hybrid value(val_int(), unsigned_flag); + return null_value || int_to_datetime_with_warn(thd, value, + ltime, fuzzydate, + field_name_or_null()); } -bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { double value= val_real(); - if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, - field_name_or_null())) - return null_value|= make_zero_date(ltime, fuzzydate); - return null_value= false; -} - - -bool Item::get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate) -{ - char buff[40]; - String tmp(buff,sizeof(buff), &my_charset_bin),*res; - if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate)) - return null_value|= make_zero_date(ltime, fuzzydate); - return null_value= false; + return null_value || double_to_datetime_with_warn(thd, value, + ltime, fuzzydate, + field_name_or_null()); } -bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item::get_date_from_string(THD *thd, MYSQL_TIME *to, date_mode_t mode) { - /* - if the item was not null and convertion failed, we return a zero date - if allowed, otherwise - null. - */ - bzero((char*) ltime,sizeof(*ltime)); - if (fuzzydate & TIME_TIME_ONLY) - { - /* - In the following scenario: - - The caller expected to get a TIME value - - Item returned a not NULL string or numeric value - - But then conversion from string or number to TIME failed - we need to change the default time_type from MYSQL_TIMESTAMP_DATE - (which was set in bzero) to MYSQL_TIMESTAMP_TIME and therefore - return TIME'00:00:00' rather than DATE'0000-00-00'. - If we don't do this, methods like Item::get_time_with_conversion() - will erroneously subtract CURRENT_DATE from '0000-00-00 00:00:00' - and return TIME'-838:59:59' instead of TIME'00:00:00' as a result. - */ - ltime->time_type= MYSQL_TIMESTAMP_TIME; - } - return !(fuzzydate & TIME_FUZZY_DATES); + StringBuffer<40> tmp; + Temporal::Warn_push warn(thd, field_name_or_null(), to, mode); + Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, val_str(&tmp), mode); + return !t->is_valid_temporal(); } @@ -1429,7 +1397,14 @@ Query_fragment::Query_fragment(THD *thd, sp_head *sphead, const char *start, const char *end) { DBUG_ASSERT(start <= end); - if (sphead) + if (thd->lex->clone_spec_offset) + { + Lex_input_stream *lip= (& thd->m_parser_state->m_lip); + DBUG_ASSERT(lip->get_buf() <= start); + DBUG_ASSERT(end <= lip->get_end_of_query()); + set(start - lip->get_buf(), end - start); + } + else if (sphead) { if (sphead->m_tmp_query) { @@ -1561,11 +1536,11 @@ my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value) } -bool Item_sp_variable::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_sp_variable::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); Item *it= this_item(); - bool val= it->get_date(ltime, fuzzydate); + bool val= it->get_date(thd, ltime, fuzzydate); null_value= it->null_value; return val; } @@ -1942,10 +1917,10 @@ my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value) return val; } -bool Item_name_const::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_name_const::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); - bool rc= value_item->get_date(ltime, fuzzydate); + bool rc= value_item->get_date(thd, ltime, fuzzydate); null_value= value_item->null_value; return rc; } @@ -1960,25 +1935,6 @@ Item_name_const::Item_name_const(THD *thd, Item *name_arg, Item *val): Item_fixed_hybrid(thd), value_item(val), name_item(name_arg) { Item::maybe_null= TRUE; - if (!name_item->basic_const_item()) - goto err; - - if (value_item->basic_const_item()) - return; // ok - - if (value_item->type() == FUNC_ITEM) - { - Item_func *value_func= (Item_func *) value_item; - if (value_func->functype() != Item_func::COLLATE_FUNC && - value_func->functype() != Item_func::NEG_FUNC) - goto err; - - if (value_func->key_item()->basic_const_item()) - return; // ok - } - -err: - my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); } @@ -3201,7 +3157,7 @@ String *Item_field::str_result(String *str) return result_field->val_str(str,&str_value); } -bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_field::get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate) { if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate)) { @@ -3211,7 +3167,7 @@ bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) return 0; } -bool Item_field::get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_field::get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (result_field->is_null() || result_field->get_date(ltime,fuzzydate)) { @@ -3744,9 +3700,9 @@ my_decimal *Item_null::val_decimal(my_decimal *decimal_value) } -bool Item_null::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_null::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - make_zero_date(ltime, fuzzydate); + set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); return (null_value= true); } @@ -3805,7 +3761,8 @@ Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg, For dynamic SQL, settability depends on the type of Item passed as an actual parameter. See Item_param::set_from_item(). */ - m_is_settable_routine_parameter(true) + m_is_settable_routine_parameter(true), + m_clones(thd->mem_root) { name= *name_arg; /* @@ -3817,6 +3774,59 @@ Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg, } +/* Add reference to Item_param used in a copy of CTE to its master as a clone */ + +bool Item_param::add_as_clone(THD *thd) +{ + LEX *lex= thd->lex; + my_ptrdiff_t master_pos= pos_in_query + lex->clone_spec_offset; + List_iterator_fast<Item_param> it(lex->param_list); + Item_param *master_param; + while ((master_param = it++)) + { + if (master_pos == master_param->pos_in_query) + return master_param->register_clone(this); + } + DBUG_ASSERT(false); + return false; +} + + +/* Update all clones of Item_param to sync their values with the item's value */ + +void Item_param::sync_clones() +{ + Item_param **c_ptr= m_clones.begin(); + Item_param **end= m_clones.end(); + for ( ; c_ptr < end; c_ptr++) + { + Item_param *c= *c_ptr; + /* Scalar-type members: */ + c->maybe_null= maybe_null; + c->null_value= null_value; + c->Type_std_attributes::operator=(*this); + c->Type_handler_hybrid_field_type::operator=(*this); + c->Type_geometry_attributes::operator=(*this); + + c->state= state; + c->m_empty_string_is_null= m_empty_string_is_null; + + c->value.PValue_simple::operator=(value); + c->value.Type_handler_hybrid_field_type::operator=(value); + type_handler()->Item_param_setup_conversion(current_thd, c); + + /* Class-type members: */ + c->value.m_decimal= value.m_decimal; + /* + Note that String's assignment op properly sets m_is_alloced to 'false', + which is correct here: c->str_value doesn't own anything. + */ + c->value.m_string= value.m_string; + c->value.m_string_ptr= value.m_string_ptr; + } +} + + void Item_param::set_null() { DBUG_ENTER("Item_param::set_null"); @@ -4101,7 +4111,7 @@ bool Item_param::set_from_item(THD *thd, Item *item) } } struct st_value tmp; - if (!item->save_in_value(&tmp)) + if (!item->save_in_value(thd, &tmp)) { const Type_handler *h= item->type_handler(); set_handler(h); @@ -4204,7 +4214,7 @@ void Item_param::invalid_default_param() const } -bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) +bool Item_param::get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { /* LIMIT clause parameter should not call get_date() @@ -4218,7 +4228,7 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) *res= value.time; return 0; } - return type_handler()->Item_get_date(this, res, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, res, fuzzydate); } @@ -4251,7 +4261,7 @@ longlong Item_param::PValue::val_int(const Type_std_attributes *attr) const { switch (type_handler()->cmp_type()) { case REAL_RESULT: - return (longlong) rint(real); + return Converter_double_to_longlong(real, attr->unsigned_flag).result(); case INT_RESULT: return integer; case DECIMAL_RESULT: @@ -4634,7 +4644,7 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) correctly fetches the value from the client-server protocol, using set_param_func(). */ - if (arg->save_in_value(&tmp) || + if (arg->save_in_value(thd, &tmp) || set_value(thd, arg, &tmp, arg->type_handler())) { set_null(); @@ -4719,32 +4729,6 @@ bool Item_param::append_for_log(THD *thd, String *str) return str->append(*val); } -/**************************************************************************** - Item_copy -****************************************************************************/ - -Item_copy *Item_copy::create(THD *thd, Item *item) -{ - MEM_ROOT *mem_root= thd->mem_root; - switch (item->result_type()) - { - case STRING_RESULT: - return new (mem_root) Item_copy_string(thd, item); - case REAL_RESULT: - return new (mem_root) Item_copy_float(thd, item); - case INT_RESULT: - return item->unsigned_flag ? - new (mem_root) Item_copy_uint(thd, item) : - new (mem_root) Item_copy_int(thd, item); - case DECIMAL_RESULT: - return new (mem_root) Item_copy_decimal(thd, item); - case TIME_RESULT: - case ROW_RESULT: - DBUG_ASSERT (0); - } - /* should not happen */ - return NULL; -} /**************************************************************************** Item_copy_string @@ -4802,138 +4786,6 @@ my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) } -/**************************************************************************** - Item_copy_int -****************************************************************************/ - -void Item_copy_int::copy() -{ - cached_value= item->val_int(); - null_value=item->null_value; -} - -static int save_int_value_in_field (Field *, longlong, bool, bool); - -int Item_copy_int::save_in_field(Field *field, bool no_conversions) -{ - return save_int_value_in_field(field, cached_value, - null_value, unsigned_flag); -} - - -String *Item_copy_int::val_str(String *str) -{ - if (null_value) - return (String *) 0; - - str->set(cached_value, &my_charset_bin); - return str; -} - - -my_decimal *Item_copy_int::val_decimal(my_decimal *decimal_value) -{ - if (null_value) - return (my_decimal *) 0; - - int2my_decimal(E_DEC_FATAL_ERROR, cached_value, unsigned_flag, decimal_value); - return decimal_value; -} - - -/**************************************************************************** - Item_copy_uint -****************************************************************************/ - -String *Item_copy_uint::val_str(String *str) -{ - if (null_value) - return (String *) 0; - - str->set((ulonglong) cached_value, &my_charset_bin); - return str; -} - - -/**************************************************************************** - Item_copy_float -****************************************************************************/ - -String *Item_copy_float::val_str(String *str) -{ - if (null_value) - return (String *) 0; - else - { - double nr= val_real(); - str->set_real(nr,decimals, &my_charset_bin); - return str; - } -} - - -my_decimal *Item_copy_float::val_decimal(my_decimal *decimal_value) -{ - if (null_value) - return (my_decimal *) 0; - else - { - double nr= val_real(); - double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); - return decimal_value; - } -} - - -int Item_copy_float::save_in_field(Field *field, bool no_conversions) -{ - if (null_value) - return set_field_to_null(field); - field->set_notnull(); - return field->store(cached_value); -} - - -/**************************************************************************** - Item_copy_decimal -****************************************************************************/ - -int Item_copy_decimal::save_in_field(Field *field, bool no_conversions) -{ - if (null_value) - return set_field_to_null(field); - field->set_notnull(); - return field->store_decimal(&cached_value); -} - - -String *Item_copy_decimal::val_str(String *result) -{ - return null_value ? NULL : cached_value.to_string(result); -} - - -double Item_copy_decimal::val_real() -{ - return null_value ? 0.0 : cached_value.to_double(); -} - - -longlong Item_copy_decimal::val_int() -{ - return null_value ? 0 : cached_value.to_longlong(unsigned_flag); -} - - -void Item_copy_decimal::copy() -{ - my_decimal *nr= item->val_decimal(&cached_value); - if (nr && nr != &cached_value) - my_decimal2decimal (nr, &cached_value); - null_value= item->null_value; -} - - /* Functions to convert item to field (for send_result_set_metadata) */ @@ -4991,9 +4843,9 @@ String* Item_ref_null_helper::val_str(String* s) } -bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_ref_null_helper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return (owner->was_null|= null_value= (*ref)->get_date_result(ltime, fuzzydate)); + return (owner->was_null|= null_value= (*ref)->get_date_result(thd, ltime, fuzzydate)); } @@ -7032,11 +6884,11 @@ Item *Item_date_literal::clone_item(THD *thd) } -bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_date_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - fuzzy_date |= sql_mode_for_dates(current_thd); + fuzzydate |= sql_mode_for_dates(thd); *ltime= cached_time; - return (null_value= check_date_with_warn(ltime, fuzzy_date, + return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7057,11 +6909,11 @@ Item *Item_datetime_literal::clone_item(THD *thd) } -bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_datetime_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - fuzzy_date |= sql_mode_for_dates(current_thd); + fuzzydate |= sql_mode_for_dates(thd); *ltime= cached_time; - return (null_value= check_date_with_warn(ltime, fuzzy_date, + return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7082,12 +6934,12 @@ Item *Item_time_literal::clone_item(THD *thd) } -bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_time_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { *ltime= cached_time; - if (fuzzy_date & TIME_TIME_ONLY) + if (fuzzydate & TIME_TIME_ONLY) return (null_value= false); - return (null_value= check_date_with_warn(ltime, fuzzy_date, + return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7594,7 +7446,7 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd, DBUG_ASSERT (producing_item != NULL); return producing_item->build_clone(thd); } - return this; + return (*ref); } static @@ -7656,7 +7508,7 @@ Item_direct_view_ref::grouping_field_transformer_for_where(THD *thd, void Item_field::print(String *str, enum_query_type query_type) { if (field && field->table->const_table && - !(query_type & QT_NO_DATA_EXPANSION)) + !(query_type & (QT_NO_DATA_EXPANSION | QT_VIEW_INTERNAL))) { print_value(str); return; @@ -8313,9 +8165,9 @@ bool Item_ref::is_null() } -bool Item_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); + return (null_value=(*ref)->get_date_result(thd, ltime, fuzzydate)); } @@ -8450,9 +8302,9 @@ bool Item_direct_ref::is_null() } -bool Item_direct_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_direct_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return (null_value=(*ref)->get_date(ltime,fuzzydate)); + return (null_value=(*ref)->get_date(thd, ltime, fuzzydate)); } @@ -8829,18 +8681,18 @@ bool Item_cache_wrapper::is_null() Get the date value of the possibly cached item */ -bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_cache_wrapper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { Item *cached_value; DBUG_ENTER("Item_cache_wrapper::get_date"); if (!expr_cache) - DBUG_RETURN((null_value= orig_item->get_date(ltime, fuzzydate))); + DBUG_RETURN((null_value= orig_item->get_date(thd, ltime, fuzzydate))); if ((cached_value= check_cache())) - DBUG_RETURN((null_value= cached_value->get_date(ltime, fuzzydate))); + DBUG_RETURN((null_value= cached_value->get_date(thd, ltime, fuzzydate))); cache(); - DBUG_RETURN((null_value= expr_value->get_date(ltime, fuzzydate))); + DBUG_RETURN((null_value= expr_value->get_date(thd, ltime, fuzzydate))); } @@ -9257,10 +9109,10 @@ my_decimal *Item_default_value::val_decimal(my_decimal *decimal_value) return Item_field::val_decimal(decimal_value); } -bool Item_default_value::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_default_value::get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate) { calculate(); - return Item_field::get_date(ltime, fuzzydate); + return Item_field::get_date(thd, ltime, fuzzydate); } bool Item_default_value::send(Protocol *protocol, st_value *buffer) @@ -9363,7 +9215,7 @@ my_decimal *Item_ignore_value::val_decimal(my_decimal *decimal_value) return 0; } -bool Item_ignore_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_ignore_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); // never should be called null_value= 1; @@ -9605,13 +9457,11 @@ void Item_trigger_field::cleanup() Item_result item_cmp_type(Item_result a,Item_result b) { - if (a == STRING_RESULT && b == STRING_RESULT) - return STRING_RESULT; - if (a == INT_RESULT && b == INT_RESULT) - return INT_RESULT; - else if (a == ROW_RESULT || b == ROW_RESULT) + if (a == b) + return a; + if (a == ROW_RESULT || b == ROW_RESULT) return ROW_RESULT; - else if (a == TIME_RESULT || b == TIME_RESULT) + if (a == TIME_RESULT || b == TIME_RESULT) return TIME_RESULT; if ((a == INT_RESULT || a == DECIMAL_RESULT) && (b == INT_RESULT || b == DECIMAL_RESULT)) @@ -9798,7 +9648,7 @@ bool Item_cache_temporal::cache_value() if (!example) return false; value_cached= true; - value= example->val_datetime_packed_result(); + value= example->val_datetime_packed_result(current_thd); null_value= example->null_value; return true; } @@ -9809,16 +9659,14 @@ bool Item_cache_time::cache_value() if (!example) return false; value_cached= true; - value= example->val_time_packed_result(); + value= example->val_time_packed_result(current_thd); null_value= example->null_value; return true; } -bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_cache_temporal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - ErrConvInteger str(value); - if (!has_value()) { bzero((char*) ltime,sizeof(*ltime)); @@ -9833,7 +9681,7 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) int Item_cache_temporal::save_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_date(field->get_thd(), <ime, date_mode_t(0))) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); int error= field->store_time_dec(<ime, decimals); @@ -9876,21 +9724,21 @@ Item *Item_cache_temporal::convert_to_basic_const_item(THD *thd) Item *Item_cache_datetime::make_literal(THD *thd) { MYSQL_TIME ltime; - unpack_time(val_datetime_packed(), <ime, MYSQL_TIMESTAMP_DATETIME); + unpack_time(val_datetime_packed(thd), <ime, MYSQL_TIMESTAMP_DATETIME); return new (thd->mem_root) Item_datetime_literal(thd, <ime, decimals); } Item *Item_cache_date::make_literal(THD *thd) { MYSQL_TIME ltime; - unpack_time(val_datetime_packed(), <ime, MYSQL_TIMESTAMP_DATE); + unpack_time(val_datetime_packed(thd), <ime, MYSQL_TIMESTAMP_DATE); return new (thd->mem_root) Item_date_literal(thd, <ime); } Item *Item_cache_time::make_literal(THD *thd) { MYSQL_TIME ltime; - unpack_time(val_time_packed(), <ime, MYSQL_TIMESTAMP_TIME); + unpack_time(val_time_packed(thd), <ime, MYSQL_TIMESTAMP_TIME); return new (thd->mem_root) Item_time_literal(thd, <ime, decimals); } @@ -9916,7 +9764,7 @@ longlong Item_cache_real::val_int() { if (!has_value()) return 0; - return (longlong) rint(value); + return Converter_double_to_longlong(value, unsigned_flag).result(); } @@ -10238,7 +10086,7 @@ String *Item_type_holder::val_str(String*) return 0; } -bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_type_holder::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); // should never be called return true; diff --git a/sql/item.h b/sql/item.h index 727d1f33f5f..c2666c35c67 100644 --- a/sql/item.h +++ b/sql/item.h @@ -28,6 +28,7 @@ #include "field.h" /* Derivation */ #include "sql_type.h" #include "sql_time.h" +#include "mem_root_array.h" C_MODE_START #include <ma_dyncol.h> @@ -861,19 +862,14 @@ protected: value= NULL; return value; } - bool get_date_from_item(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date_from_item(THD *thd, Item *item, + MYSQL_TIME *ltime, date_mode_t fuzzydate) { - bool rc= item->get_date(ltime, fuzzydate); + bool rc= item->get_date(thd, ltime, fuzzydate); null_value= MY_TEST(rc || item->null_value); return rc; } public: - /* - This method is used if the item was not null but convertion to - TIME/DATE/DATETIME failed. We return a zero date if allowed, - otherwise - null. - */ - bool make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate); /* Cache val_str() into the own buffer, e.g. to evaluate constant @@ -994,9 +990,9 @@ public: DBUG_ASSERT(0); } - bool save_in_value(struct st_value *value) + bool save_in_value(THD *thd, struct st_value *value) { - return type_handler()->Item_save_in_value(this, value); + return type_handler()->Item_save_in_value(thd, this, value); } /* Function returns 1 on overflow and -1 on fatal errors */ @@ -1170,6 +1166,12 @@ public: If value is not null null_value flag will be reset to FALSE. */ virtual double val_real()=0; + Double_null to_double_null() + { + // val_real() must be caleed on a separate line. See to_longlong_null() + double nr= val_real(); + return Double_null(nr, null_value); + } /* Return integer representation of item. @@ -1181,6 +1183,20 @@ public: If value is not null null_value flag will be reset to FALSE. */ virtual longlong val_int()=0; + Longlong_null to_longlong_null() + { + longlong nr= val_int(); + /* + C++ does not guarantee the order of parameter evaluation, + so to make sure "null_value" is passed to the constructor + after the val_int() call, val_int() is caled on a separate line. + */ + return Longlong_null(nr, null_value); + } + Longlong_hybrid_null to_longlong_hybrid_null() + { + return Longlong_hybrid_null(to_longlong_null(), unsigned_flag); + } /** Get a value for CAST(x AS SIGNED). Too large positive unsigned integer values are converted @@ -1505,14 +1521,14 @@ public: /** TIME or DATETIME precision of the item: 0..6 */ - uint time_precision() + uint time_precision(THD *thd) { - return const_item() ? type_handler()->Item_time_precision(this) : + return const_item() ? type_handler()->Item_time_precision(thd, this) : MY_MIN(decimals, TIME_SECOND_PART_DIGITS); } - uint datetime_precision() + uint datetime_precision(THD *thd) { - return const_item() ? type_handler()->Item_datetime_precision(this) : + return const_item() ? type_handler()->Item_datetime_precision(thd, this) : MY_MIN(decimals, TIME_SECOND_PART_DIGITS); } virtual longlong val_int_min() const @@ -1610,33 +1626,33 @@ public: void split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, Item **ref, uint flags); - virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0; - bool get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_time(MYSQL_TIME *ltime) - { return get_date(ltime, Time::flags_for_get_date()); } + virtual bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)= 0; + bool get_date_from_int(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_from_string(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_time(THD *thd, MYSQL_TIME *ltime) + { return get_date(thd, ltime, Time::flags_for_get_date()); } // Get a DATE or DATETIME value in numeric packed format for comparison - virtual longlong val_datetime_packed() + virtual longlong val_datetime_packed(THD *thd) { - ulonglong fuzzydate= Datetime::comparison_flags_for_get_date(); + date_mode_t fuzzydate= Datetime::comparison_flags_for_get_date(); return Datetime(current_thd, this, fuzzydate).to_packed(); } // Get a TIME value in numeric packed format for comparison - virtual longlong val_time_packed() + virtual longlong val_time_packed(THD *thd) { - return Time(this, Time::comparison_flags_for_get_date()).to_packed(); + return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); } - longlong val_datetime_packed_result(); - longlong val_time_packed_result() + longlong val_datetime_packed_result(THD *thd); + longlong val_time_packed_result(THD *thd) { MYSQL_TIME ltime; - ulonglong fuzzydate= Time::comparison_flags_for_get_date(); - return get_date_result(<ime, fuzzydate) ? 0 : pack_time(<ime); + date_mode_t fuzzydate= Time::comparison_flags_for_get_date(); + return get_date_result(thd, <ime, fuzzydate) ? 0 : pack_time(<ime); } - virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date(ltime,fuzzydate); } + virtual bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date(thd, ltime,fuzzydate); } /* The method allows to determine nullness of a complex expression without fully evaluating it, instead of calling val/result*() then @@ -1765,6 +1781,11 @@ public: virtual bool limit_index_condition_pushdown_processor(void *arg) { return 0; } virtual bool exists2in_processor(void *arg) { return 0; } virtual bool find_selective_predicates_list_processor(void *arg) { return 0; } + bool cleanup_is_expensive_cache_processor(void *arg) + { + is_expensive_cache= (int8)(-1); + return 0; + } /* TRUE if the expression depends only on the table indicated by tab_map @@ -2388,6 +2409,15 @@ protected: } return true; } + bool eq(const Item_args *other, bool binary_cmp) const + { + for (uint i= 0; i < arg_count ; i++) + { + if (!args[i]->eq(other->args[i], binary_cmp)) + return false; + } + return true; + } bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) { for (uint i= 0; i < arg_count; i++) @@ -2653,7 +2683,7 @@ public: longlong val_int(); String *val_str(String *sp); my_decimal *val_decimal(my_decimal *decimal_value); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool is_null(); public: @@ -2943,7 +2973,7 @@ public: longlong val_int(); String *val_str(String *sp); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool is_null(); virtual void print(String *str, enum_query_type query_type); @@ -3003,9 +3033,9 @@ class Item_num: public Item_literal public: Item_num(THD *thd): Item_literal(thd) { collation.set_numeric(); } Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } }; @@ -3136,7 +3166,7 @@ public: longlong val_int() { return field->val_int(); } String *val_str(String *str) { return field->val_str(str); } my_decimal *val_decimal(my_decimal *dec) { return field->val_decimal(dec); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { return field->get_date(ltime, fuzzydate); } @@ -3254,8 +3284,8 @@ public: return MONOTONIC_STRICT_INCREASING; } longlong val_int_endpoint(bool left_endp, bool *incl_endp); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool get_date_result(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool is_null() { return field->is_null(); } void update_null_value(); void update_table_bitmaps() @@ -3460,7 +3490,7 @@ public: longlong val_int(); String *val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); int save_in_field(Field *field, bool no_conversions); int save_safe_in_field(Field *field); bool send(Protocol *protocol, st_value *buffer); @@ -3779,7 +3809,7 @@ public: { return can_return_value() ? value.val_str(str, this) : NULL; } - bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *tm, date_mode_t fuzzydate); int save_in_field(Field *field, bool no_conversions); void set_default(); @@ -3904,6 +3934,10 @@ public: bool check_vcol_func_processor(void *int_arg) {return FALSE;} Item *get_copy(THD *thd) { return 0; } + bool add_as_clone(THD *thd); + void sync_clones(); + bool register_clone(Item_param *i) { return m_clones.push_back(i); } + private: void invalid_default_param() const; @@ -3921,6 +3955,12 @@ public: private: Send_field *m_out_param_info; bool m_is_settable_routine_parameter; + /* + Array of all references of this parameter marker used in a CTE to its clones + created for copies of this marker used the CTE's copies. It's used to + synchronize the actual value of the parameter with the values of the clones. + */ + Mem_root_array<Item_param *, true> m_clones; }; @@ -4027,7 +4067,7 @@ public: longlong val_int(); double val_real() { return (double)val_int(); } void set(longlong packed, enum_mysql_timestamp_type ts_type); - bool get_date(MYSQL_TIME *to, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) { *to= ltime; return false; @@ -4226,9 +4266,9 @@ public: return (String*) &str_value; } my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return get_date_from_string(ltime, fuzzydate); + return get_date_from_string(thd, ltime, fuzzydate); } int save_in_field(Field *field, bool no_conversions); const Type_handler *type_handler() const { return &type_handler_varchar; } @@ -4478,9 +4518,9 @@ public: } const String *const_ptr_string() const { return &str_value; } String *val_str(String*) { return &str_value; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } }; @@ -4625,7 +4665,7 @@ public: double val_real() { return Date(this).to_double(); } String *val_str(String *to) { return Date(this).to_string(to); } my_decimal *val_decimal(my_decimal *to) { return Date(this).to_decimal(to); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_date_literal>(thd, this); } }; @@ -4649,7 +4689,7 @@ public: double val_real() { return Time(this).to_double(); } String *val_str(String *to) { return Time(this).to_string(to, decimals); } my_decimal *val_decimal(my_decimal *to) { return Time(this).to_decimal(to); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_time_literal>(thd, this); } }; @@ -4681,7 +4721,7 @@ public: { return Datetime(this).to_decimal(to); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_datetime_literal>(thd, this); } }; @@ -4718,7 +4758,7 @@ class Item_date_literal_for_invalid_dates: public Item_date_literal public: Item_date_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime) :Item_date_literal(thd, ltime) { } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { *ltime= cached_time; return (null_value= false); @@ -4736,7 +4776,7 @@ public: Item_datetime_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime, uint dec_arg) :Item_datetime_literal(thd, ltime, dec_arg) { } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { *ltime= cached_time; return (null_value= false); @@ -5018,7 +5058,7 @@ public: bool val_bool(); String *val_str(String* tmp); bool is_null(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); double val_result(); longlong val_int_result(); String *str_result(String* tmp); @@ -5229,7 +5269,7 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); bool is_null(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); virtual Ref_Type ref_type() { return DIRECT_REF; } Item *get_copy(THD *thd) { return get_item_copy<Item_direct_ref>(thd, this); } @@ -5325,7 +5365,7 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); bool is_null(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, fast_field_copier data __attribute__ ((__unused__))) @@ -5539,14 +5579,14 @@ public: else return Item_direct_ref::is_null(); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (check_null_ref()) { bzero((char*) ltime,sizeof(*ltime)); return 1; } - return Item_direct_ref::get_date(ltime, fuzzydate); + return Item_direct_ref::get_date(thd, ltime, fuzzydate); } bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, @@ -5662,7 +5702,7 @@ public: String* val_str(String* s); my_decimal *val_decimal(my_decimal *); bool val_bool(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); virtual void print(String *str, enum_query_type query_type); table_map used_tables() const; Item *get_copy(THD *thd) @@ -5716,7 +5756,7 @@ public: Base class to implement typed value caching Item classes Item_copy_ classes are very similar to the corresponding Item_ - classes (e.g. Item_copy_int is similar to Item_int) but they add + classes (e.g. Item_copy_string is similar to Item_string) but they add the following additional functionality to Item_ : 1. Nullability 2. Possibility to store the value not only on instantiation time, @@ -5763,13 +5803,6 @@ protected: } public: - /** - Factory method to create the appropriate subclass dependent on the type of - the original item. - - @param item the original item. - */ - static Item_copy *create(THD *thd, Item *item); /** Update the cache with the value of the original item @@ -5834,8 +5867,8 @@ public: my_decimal *val_decimal(my_decimal *); double val_real(); longlong val_int(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_string(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_string(thd, ltime, fuzzydate); } void copy(); int save_in_field(Field *field, bool no_conversions); Item *get_copy(THD *thd) @@ -5843,107 +5876,6 @@ public: }; -class Item_copy_int : public Item_copy -{ -protected: - longlong cached_value; -public: - Item_copy_int(THD *thd, Item *i): Item_copy(thd, i) {} - int save_in_field(Field *field, bool no_conversions); - - virtual String *val_str(String*); - virtual my_decimal *val_decimal(my_decimal *); - virtual double val_real() - { - return null_value ? 0.0 : (double) cached_value; - } - virtual longlong val_int() - { - return null_value ? 0 : cached_value; - } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } - virtual void copy(); - Item *get_copy(THD *thd) - { return get_item_copy<Item_copy_int>(thd, this); } -}; - - -class Item_copy_uint : public Item_copy_int -{ -public: - Item_copy_uint(THD *thd, Item *item_arg): Item_copy_int(thd, item_arg) - { - unsigned_flag= 1; - } - - String *val_str(String*); - double val_real() - { - return null_value ? 0.0 : (double) (ulonglong) cached_value; - } - Item *get_copy(THD *thd) - { return get_item_copy<Item_copy_uint>(thd, this); } -}; - - -class Item_copy_float : public Item_copy -{ -protected: - double cached_value; -public: - Item_copy_float(THD *thd, Item *i): Item_copy(thd, i) {} - int save_in_field(Field *field, bool no_conversions); - - String *val_str(String*); - my_decimal *val_decimal(my_decimal *); - double val_real() - { - return null_value ? 0.0 : cached_value; - } - longlong val_int() - { - return (longlong) rint(val_real()); - } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { - return get_date_from_real(ltime, fuzzydate); - } - void copy() - { - cached_value= item->val_real(); - null_value= item->null_value; - } - Item *get_copy(THD *thd) - { return get_item_copy<Item_copy_float>(thd, this); } -}; - - -class Item_copy_decimal : public Item_copy -{ -protected: - my_decimal cached_value; -public: - Item_copy_decimal(THD *thd, Item *i): Item_copy(thd, i) {} - int save_in_field(Field *field, bool no_conversions); - - String *val_str(String*); - my_decimal *val_decimal(my_decimal *) - { - return null_value ? NULL: &cached_value; - } - double val_real(); - longlong val_int(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { - return VDec(this).to_datetime_with_warn(ltime, fuzzydate, this); - } - void copy(); - Item *get_copy(THD *thd) - { return get_item_copy<Item_copy_decimal>(thd, this); } -}; - - /* Cached_item_XXX objects are not exactly caches. They do the following: @@ -6077,7 +6009,7 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *decimal_value); - bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool send(Protocol *protocol, st_value *buffer); int save_in_field(Field *field_arg, bool no_conversions); bool save_in_param(THD *thd, Item_param *param) @@ -6129,7 +6061,7 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *decimal_value); - bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool send(Protocol *protocol, st_value *buffer); }; @@ -6446,8 +6378,8 @@ public: longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_int(thd, ltime, fuzzydate); } bool cache_value(); int save_in_field(Field *field, bool no_conversions); Item *convert_to_basic_const_item(THD *thd); @@ -6461,10 +6393,9 @@ class Item_cache_year: public Item_cache_int public: Item_cache_year(THD *thd, const Type_handler *handler) :Item_cache_int(thd, handler) { } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) { - return null_value= - VYear(this).to_mysql_time_with_warn(ltime, fuzzydate, NULL); + return type_handler_year.Item_get_date_with_warn(thd, this, to, mode); } }; @@ -6475,7 +6406,7 @@ protected: Item_cache_temporal(THD *thd, const Type_handler *handler); public: bool cache_value(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); int save_in_field(Field *field, bool no_conversions); void store_packed(longlong val_arg, Item *example); /* @@ -6498,12 +6429,12 @@ public: Item *get_copy(THD *thd) { return get_item_copy<Item_cache_time>(thd, this); } Item *make_literal(THD *); - longlong val_datetime_packed() + longlong val_datetime_packed(THD *thd) { - ulonglong fuzzy= Datetime::comparison_flags_for_get_date(); - return has_value() ? Datetime(current_thd, this, fuzzy).to_longlong() : 0; + date_mode_t fuzzy= Datetime::comparison_flags_for_get_date(); + return has_value() ? Datetime(thd, this, fuzzy).to_packed() : 0; } - longlong val_time_packed() + longlong val_time_packed(THD *thd) { return has_value() ? value : 0; } @@ -6534,13 +6465,13 @@ public: Item *get_copy(THD *thd) { return get_item_copy<Item_cache_datetime>(thd, this); } Item *make_literal(THD *); - longlong val_datetime_packed() + longlong val_datetime_packed(THD *thd) { return has_value() ? value : 0; } - longlong val_time_packed() + longlong val_time_packed(THD *thd) { - return Time(this, Time::comparison_flags_for_get_date()).to_packed(); + return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); } longlong val_int() { @@ -6569,13 +6500,13 @@ public: Item *get_copy(THD *thd) { return get_item_copy<Item_cache_date>(thd, this); } Item *make_literal(THD *); - longlong val_datetime_packed() + longlong val_datetime_packed(THD *thd) { return has_value() ? value : 0; } - longlong val_time_packed() + longlong val_time_packed(THD *thd) { - return Time(this, Time::comparison_flags_for_get_date()).to_packed(); + return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); } longlong val_int() { return has_value() ? Date(this).to_longlong() : 0; } double val_real() { return has_value() ? Date(this).to_double() : 0; } @@ -6601,8 +6532,8 @@ public: longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_real(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_real(thd, ltime, fuzzydate); } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); Item *get_copy(THD *thd) @@ -6621,8 +6552,10 @@ public: longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return VDec(this).to_datetime_with_warn(ltime, fuzzydate, this); } + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) + { + return decimal_to_datetime_with_warn(thd, VDec(this).ptr(), to, mode, NULL); + } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); Item *get_copy(THD *thd) @@ -6649,8 +6582,8 @@ public: longlong val_int(); String* val_str(String *); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_string(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_string(thd, ltime, fuzzydate); } CHARSET_INFO *charset() const { return value->charset(); }; int save_in_field(Field *field, bool no_conversions); bool cache_value(); @@ -6731,7 +6664,7 @@ public: illegal_method_call((const char*)"val_decimal"); return 0; }; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { illegal_method_call((const char*)"val_decimal"); return true; @@ -6814,7 +6747,7 @@ public: longlong val_int(); my_decimal *val_decimal(my_decimal *); String *val_str(String*); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, const Tmp_field_param *param) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index b62e71d84fa..f5c49214076 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -684,10 +684,11 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, int Arg_comparator::compare_time() { - longlong val1= (*a)->val_time_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_time_packed(thd); if (!(*a)->null_value) { - longlong val2= (*b)->val_time_packed(); + longlong val2= (*b)->val_time_packed(thd); if (!(*b)->null_value) return compare_not_null_values(val1, val2); } @@ -699,8 +700,9 @@ int Arg_comparator::compare_time() int Arg_comparator::compare_e_time() { - longlong val1= (*a)->val_time_packed(); - longlong val2= (*b)->val_time_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_time_packed(thd); + longlong val2= (*b)->val_time_packed(thd); if ((*a)->null_value || (*b)->null_value) return MY_TEST((*a)->null_value && (*b)->null_value); return MY_TEST(val1 == val2); @@ -710,10 +712,11 @@ int Arg_comparator::compare_e_time() int Arg_comparator::compare_datetime() { - longlong val1= (*a)->val_datetime_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_datetime_packed(thd); if (!(*a)->null_value) { - longlong val2= (*b)->val_datetime_packed(); + longlong val2= (*b)->val_datetime_packed(thd); if (!(*b)->null_value) return compare_not_null_values(val1, val2); } @@ -725,8 +728,9 @@ int Arg_comparator::compare_datetime() int Arg_comparator::compare_e_datetime() { - longlong val1= (*a)->val_datetime_packed(); - longlong val2= (*b)->val_datetime_packed(); + THD *thd= current_thd; + longlong val1= (*a)->val_datetime_packed(thd); + longlong val2= (*b)->val_datetime_packed(thd); if ((*a)->null_value || (*b)->null_value) return MY_TEST((*a)->null_value && (*b)->null_value); return MY_TEST(val1 == val2); @@ -823,7 +827,7 @@ int Arg_comparator::compare_e_real() int Arg_comparator::compare_e_decimal() { VDec val1(*a), val2(*b); - if (val1.is_null() || val1.is_null()) + if (val1.is_null() || val2.is_null()) return MY_TEST(val1.is_null() && val2.is_null()); return MY_TEST(val1.cmp(val2) == 0); } @@ -1786,10 +1790,7 @@ bool Item_func_opt_neg::eq(const Item *item, bool binary_cmp) const return 0; if (negated != ((Item_func_opt_neg *) item_func)->negated) return 0; - for (uint i=0; i < arg_count ; i++) - if (!args[i]->eq(item_func->arguments()[i], binary_cmp)) - return 0; - return 1; + return Item_args::eq(item_func, binary_cmp); } @@ -2099,22 +2100,24 @@ bool Item_func_between::fix_length_and_dec_temporal(THD *thd) longlong Item_func_between::val_int_cmp_datetime() { - longlong value= args[0]->val_datetime_packed(), a, b; + THD *thd= current_thd; + longlong value= args[0]->val_datetime_packed(thd), a, b; if ((null_value= args[0]->null_value)) return 0; - a= args[1]->val_datetime_packed(); - b= args[2]->val_datetime_packed(); + a= args[1]->val_datetime_packed(thd); + b= args[2]->val_datetime_packed(thd); return val_int_cmp_int_finalize(value, a, b); } longlong Item_func_between::val_int_cmp_time() { - longlong value= args[0]->val_time_packed(), a, b; + THD *thd= current_thd; + longlong value= args[0]->val_time_packed(thd), a, b; if ((null_value= args[0]->null_value)) return 0; - a= args[1]->val_time_packed(); - b= args[2]->val_time_packed(); + a= args[1]->val_time_packed(thd); + b= args[2]->val_time_packed(thd); return val_int_cmp_int_finalize(value, a, b); } @@ -2296,12 +2299,12 @@ Item_func_ifnull::str_op(String *str) } -bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_ifnull::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < 2; i++) { - Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + Datetime dt(thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); if (!(dt.copy_to_mysql_time(ltime, mysql_timestamp_type()))) return (null_value= false); } @@ -2309,12 +2312,12 @@ bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) } -bool Item_func_ifnull::time_op(MYSQL_TIME *ltime) +bool Item_func_ifnull::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < 2; i++) { - if (!Time(args[i]).copy_to_mysql_time(ltime)) + if (!Time(thd, args[i]).copy_to_mysql_time(ltime)) return (null_value= false); } return (null_value= true); @@ -2796,23 +2799,23 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value) bool -Item_func_nullif::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +Item_func_nullif::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (!compare()) return (null_value= true); - Datetime dt(current_thd, args[2], fuzzydate); + Datetime dt(thd, args[2], fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } bool -Item_func_nullif::time_op(MYSQL_TIME *ltime) +Item_func_nullif::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); if (!compare()) return (null_value= true); - return (null_value= Time(args[2]).copy_to_mysql_time(ltime)); + return (null_value= Time(thd, args[2]).copy_to_mysql_time(ltime)); } @@ -2894,7 +2897,7 @@ Item *Item_func_case_simple::find_item() Item *Item_func_decode_oracle::find_item() { uint idx; - if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx)) + if (!Predicant_to_list_comparator::cmp_nulls_equal(current_thd, this, &idx)) return args[idx + when_count()]; Item **pos= Item_func_decode_oracle::else_expr_addr(); return pos ? pos[0] : 0; @@ -2970,24 +2973,24 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) } -bool Item_func_case::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_case::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); Item *item= find_item(); if (!item) return (null_value= true); - Datetime dt(current_thd, item, fuzzydate); + Datetime dt(thd, item, fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } -bool Item_func_case::time_op(MYSQL_TIME *ltime) +bool Item_func_case::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); Item *item= find_item(); if (!item) return (null_value= true); - return (null_value= Time(item).copy_to_mysql_time(ltime)); + return (null_value= Time(thd, item).copy_to_mysql_time(ltime)); } @@ -3323,12 +3326,12 @@ double Item_func_coalesce::real_op() } -bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_coalesce::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < arg_count; i++) { - Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + Datetime dt(thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); if (!dt.copy_to_mysql_time(ltime, mysql_timestamp_type())) return (null_value= false); } @@ -3336,12 +3339,12 @@ bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) } -bool Item_func_coalesce::time_op(MYSQL_TIME *ltime) +bool Item_func_coalesce::time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < arg_count; i++) { - if (!Time(args[i]).copy_to_mysql_time(ltime)) + if (!Time(thd, args[i]).copy_to_mysql_time(ltime)) return (null_value= false); } return (null_value= true); @@ -3629,7 +3632,7 @@ void in_datetime::set(uint pos,Item *item) { struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= item->val_datetime_packed(); + buff->val= item->val_datetime_packed(current_thd); buff->unsigned_flag= 1L; } @@ -3637,13 +3640,13 @@ void in_time::set(uint pos,Item *item) { struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= item->val_time_packed(); + buff->val= item->val_time_packed(current_thd); buff->unsigned_flag= 1L; } uchar *in_datetime::get_value(Item *item) { - tmp.val= item->val_datetime_packed(); + tmp.val= item->val_datetime_packed(current_thd); if (item->null_value) return 0; tmp.unsigned_flag= 1L; @@ -3652,7 +3655,7 @@ uchar *in_datetime::get_value(Item *item) uchar *in_time::get_value(Item *item) { - tmp.val= item->val_time_packed(); + tmp.val= item->val_time_packed(current_thd); if (item->null_value) return 0; tmp.unsigned_flag= 1L; @@ -3993,7 +3996,7 @@ int cmp_item_datetime::cmp_not_null(const Value *val) int cmp_item_datetime::cmp(Item *arg) { - const bool rc= value != arg->val_datetime_packed(); + const bool rc= value != arg->val_datetime_packed(current_thd); return (m_null_value || arg->null_value) ? UNKNOWN : rc; } @@ -4008,7 +4011,7 @@ int cmp_item_time::cmp_not_null(const Value *val) int cmp_item_time::cmp(Item *arg) { - const bool rc= value != arg->val_time_packed(); + const bool rc= value != arg->val_time_packed(current_thd); return (m_null_value || arg->null_value) ? UNKNOWN : rc; } @@ -4228,12 +4231,21 @@ bool Item_func_in::value_list_convert_const_to_int(THD *thd) if (field_item->field_type() == MYSQL_TYPE_LONGLONG || field_item->field_type() == MYSQL_TYPE_YEAR) { - bool all_converted= TRUE; + bool all_converted= true; Item **arg, **arg_end; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) { - if (!convert_const_to_int(thd, field_item, &arg[0])) - all_converted= FALSE; + /* + Explicit NULLs should not affect data cmp_type resolution: + - we ignore NULLs when calling collect_cmp_type() + - we ignore NULLs here + So this expression: + year_column IN (DATE'2001-01-01', NULL) + switches from TIME_RESULT to INT_RESULT. + */ + if (arg[0]->type() != Item::NULL_ITEM && + !convert_const_to_int(thd, field_item, &arg[0])) + all_converted= false; } if (all_converted) m_comparator.set_handler(&type_handler_longlong); @@ -4984,6 +4996,19 @@ Item *Item_cond::build_clone(THD *thd) } +bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel) +{ + List_iterator_fast<Item> li(list); + Item *item; + while ((item= li++)) + { + if (!item->excl_dep_on_grouping_fields(sel)) + return false; + } + return true; +} + + void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding) { List_iterator<Item> li(list); @@ -5120,7 +5145,11 @@ longlong Item_func_isnull::val_int() void Item_func_isnull::print(String *str, enum_query_type query_type) { - args[0]->print_parenthesised(str, query_type, precedence()); + if (const_item() && !args[0]->maybe_null && + !(query_type & (QT_NO_DATA_EXPANSION | QT_VIEW_INTERNAL))) + str->append("/*always not null*/ 1"); + else + args[0]->print_parenthesised(str, query_type, precedence()); str->append(STRING_WITH_LEN(" is null")); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 856aae54a6b..cf210a71433 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1011,8 +1011,8 @@ public: longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); bool fix_length_and_dec() { if (aggregate_for_result(func_name(), args, arg_count, true)) @@ -1090,8 +1090,8 @@ public: longlong int_op(); String *str_op(String *str); my_decimal *decimal_op(my_decimal *); - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); bool fix_length_and_dec() { if (Item_func_case_abbreviation2::fix_length_and_dec2(args)) @@ -1125,12 +1125,12 @@ public: :Item_func_case_abbreviation2(thd, a, b, c) { } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Datetime dt(current_thd, find_item(), fuzzydate); + Datetime dt(thd, find_item(), fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } - bool time_op(MYSQL_TIME *ltime) + bool time_op(THD *thd, MYSQL_TIME *ltime) { return (null_value= Time(find_item()).copy_to_mysql_time(ltime)); } @@ -1243,8 +1243,8 @@ public: Item_func_hybrid_field_type::cleanup(); arg_count= 2; // See the comment to the constructor } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); double real_op(); longlong int_op(); String *str_op(String *str); @@ -1641,7 +1641,7 @@ public: { } void store_value(Item *item) { - value= item->val_datetime_packed(); + value= item->val_datetime_packed(current_thd); m_null_value= item->null_value; } int cmp_not_null(const Value *val); @@ -1658,7 +1658,7 @@ public: { } void store_value(Item *item) { - value= item->val_time_packed(); + value= item->val_time_packed(current_thd); m_null_value= item->null_value; } int cmp_not_null(const Value *val); @@ -1897,7 +1897,7 @@ class Predicant_to_list_comparator return UNKNOWN; return in_item->cmp(args->arguments()[m_comparators[i].m_arg_index]); } - int cmp_args_nulls_equal(Item_args *args, uint i) + int cmp_args_nulls_equal(THD *thd, Item_args *args, uint i) { Predicant_to_value_comparator *cmp= &m_comparators[m_comparators[i].m_handler_index]; @@ -1908,7 +1908,7 @@ class Predicant_to_list_comparator ValueBuffer<MAX_FIELD_WIDTH> val; if (m_comparators[i].m_handler_index == i) in_item->store_value(predicant); - m_comparators[i].m_handler->Item_save_in_value(arg, &val); + m_comparators[i].m_handler->Item_save_in_value(thd, arg, &val); if (predicant->null_value && val.is_null()) return FALSE; // Two nulls are equal if (predicant->null_value || val.is_null()) @@ -2089,12 +2089,12 @@ public: /* Same as above, but treats two NULLs as equal, e.g. as in DECODE_ORACLE(). */ - bool cmp_nulls_equal(Item_args *args, uint *idx) + bool cmp_nulls_equal(THD *thd, Item_args *args, uint *idx) { for (uint i= 0 ; i < m_comparator_count ; i++) { DBUG_ASSERT(m_comparators[i].m_handler != NULL); - if (cmp_args_nulls_equal(args, i) == FALSE) + if (cmp_args_nulls_equal(thd, args, i) == FALSE) { *idx= m_comparators[i].m_arg_index; return false; // Found a matching value @@ -2130,8 +2130,8 @@ public: longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool time_op(MYSQL_TIME *ltime); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); bool fix_fields(THD *thd, Item **ref); table_map not_null_tables() const { return 0; } const char *func_name() const { return "case"; } @@ -2160,6 +2160,7 @@ public: DBUG_ASSERT(arg_count >= 2); reorder_args(0); } + enum Functype functype() const { return CASE_SEARCHED_FUNC; } void print(String *str, enum_query_type query_type); bool fix_length_and_dec(); Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) @@ -2212,6 +2213,7 @@ public: Predicant_to_list_comparator::cleanup(); DBUG_VOID_RETURN; } + enum Functype functype() const { return CASE_SIMPLE_FUNC; } void print(String *str, enum_query_type query_type); bool fix_length_and_dec(); Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); @@ -2846,16 +2848,7 @@ public: bool fix_length_and_dec(); const char *func_name() const { return "regexp"; } enum precedence precedence() const { return CMP_PRECEDENCE; } - Item *get_copy(THD *thd) - { return get_item_copy<Item_func_regex>(thd, this); } - Item *build_clone(THD *thd) - { - Item_func_regex *clone= (Item_func_regex*) Item_bool_func::build_clone(thd); - if (clone) - clone->re.reset(); - return clone; - } - + Item *get_copy(THD *) { return 0; } void print(String *str, enum_query_type query_type) { print_op(str, query_type); @@ -2895,8 +2888,7 @@ public: bool fix_fields(THD *thd, Item **ref); bool fix_length_and_dec(); const char *func_name() const { return "regexp_instr"; } - Item *get_copy(THD *thd) - { return get_item_copy<Item_func_regexp_instr>(thd, this); } + Item *get_copy(THD *thd) { return 0; } }; @@ -2974,6 +2966,7 @@ public: Item_transformer transformer, uchar *arg_t); bool eval_not_null_tables(void *opt_arg); Item *build_clone(THD *thd); + bool excl_dep_on_grouping_fields(st_select_lex *sel); }; template <template<class> class LI, class T> class Item_equal_iterator; diff --git a/sql/item_create.cc b/sql/item_create.cc index d9b007d4728..87bf69f3c96 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -6103,7 +6103,26 @@ Create_func_name_const Create_func_name_const::s_singleton; Item* Create_func_name_const::create_2_arg(THD *thd, Item *arg1, Item *arg2) { - return new (thd->mem_root) Item_name_const(thd, arg1, arg2); + if (!arg1->basic_const_item()) + goto err; + + if (arg2->basic_const_item()) + return new (thd->mem_root) Item_name_const(thd, arg1, arg2); + + if (arg2->type() == Item::FUNC_ITEM) + { + Item_func *value_func= (Item_func *) arg2; + if (value_func->functype() != Item_func::COLLATE_FUNC && + value_func->functype() != Item_func::NEG_FUNC) + goto err; + + if (!value_func->key_item()->basic_const_item()) + goto err; + return new (thd->mem_root) Item_name_const(thd, arg1, arg2); + } +err: + my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); + return NULL; } diff --git a/sql/item_func.cc b/sql/item_func.cc index fa14bc58f2e..c176a7e43a7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -647,10 +647,7 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const (func_type == Item_func::FUNC_SP && my_strcasecmp(system_charset_info, func_name(), item_func->func_name()))) return 0; - for (uint i=0; i < arg_count ; i++) - if (!args[i]->eq(item_func->args[i], binary_cmp)) - return 0; - return 1; + return Item_args::eq(item_func, binary_cmp); } @@ -834,17 +831,6 @@ Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec) return dec; } -bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) -{ - longlong value= int_op(); - bool neg= !unsigned_flag && value < 0; - if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, - ltime, fuzzydate, NULL)) - return make_zero_mysql_time(ltime, fuzzydate); - return (null_value= 0); -} - String *Item_func_hybrid_field_type::val_str_from_real_op(String *str) { @@ -857,7 +843,7 @@ String *Item_func_hybrid_field_type::val_str_from_real_op(String *str) longlong Item_func_hybrid_field_type::val_int_from_real_op() { - return (longlong) rint(real_op()); + return Converter_double_to_longlong(real_op(), unsigned_flag).result(); } my_decimal * @@ -870,20 +856,11 @@ Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec) return dec; } -bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) -{ - double value= real_op(); - if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, NULL)) - return make_zero_mysql_time(ltime, fuzzydate); - return (null_value= 0); -} - String *Item_func_hybrid_field_type::val_str_from_date_op(String *str) { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime) || + if (date_op_with_null_check(current_thd, <ime) || (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) return (String *) 0; str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); @@ -895,7 +872,7 @@ String *Item_func_hybrid_field_type::val_str_from_date_op(String *str) double Item_func_hybrid_field_type::val_real_from_date_op() { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) + if (date_op_with_null_check(current_thd, <ime)) return 0; return TIME_to_double(<ime); } @@ -903,7 +880,7 @@ double Item_func_hybrid_field_type::val_real_from_date_op() longlong Item_func_hybrid_field_type::val_int_from_date_op() { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) + if (date_op_with_null_check(current_thd, <ime)) return 0; return TIME_to_ulonglong(<ime); } @@ -912,7 +889,7 @@ my_decimal * Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec) { MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) + if (date_op_with_null_check(current_thd, <ime)) { my_decimal_set_zero(dec); return 0; @@ -924,7 +901,7 @@ Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec) String *Item_func_hybrid_field_type::val_str_from_time_op(String *str) { MYSQL_TIME ltime; - if (time_op_with_null_check(<ime) || + if (time_op_with_null_check(current_thd, <ime) || (null_value= my_TIME_to_str(<ime, str, decimals))) return NULL; return str; @@ -933,20 +910,22 @@ String *Item_func_hybrid_field_type::val_str_from_time_op(String *str) double Item_func_hybrid_field_type::val_real_from_time_op() { MYSQL_TIME ltime; - return time_op_with_null_check(<ime) ? 0 : TIME_to_double(<ime); + return time_op_with_null_check(current_thd, <ime) ? 0 : + TIME_to_double(<ime); } longlong Item_func_hybrid_field_type::val_int_from_time_op() { MYSQL_TIME ltime; - return time_op_with_null_check(<ime) ? 0 : TIME_to_ulonglong(<ime); + return time_op_with_null_check(current_thd, <ime) ? 0 : + TIME_to_ulonglong(<ime); } my_decimal * Item_func_hybrid_field_type::val_decimal_from_time_op(my_decimal *dec) { MYSQL_TIME ltime; - if (time_op_with_null_check(<ime)) + if (time_op_with_null_check(current_thd, <ime)) { my_decimal_set_zero(dec); return 0; @@ -974,18 +953,6 @@ Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value) return res ? decimal_from_string_with_check(decimal_value, res) : 0; } -bool Item_func_hybrid_field_type::get_date_from_str_op(MYSQL_TIME *ltime, - ulonglong fuzzydate) -{ - StringBuffer<40> tmp; - String *res; - if (!(res= str_op_with_null_check(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate)) - return make_zero_mysql_time(ltime, fuzzydate); - return (null_value= 0); -} - void Item_func_signed::print(String *str, enum_query_type query_type) { @@ -2611,14 +2578,15 @@ bool Item_func_min_max::fix_attributes(Item **items, uint nitems) 0 Otherwise */ -bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_min_max::get_date_native(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) { longlong UNINIT_VAR(min_max); DBUG_ASSERT(fixed == 1); for (uint i=0; i < arg_count ; i++) { - longlong res= args[i]->val_datetime_packed(); + longlong res= args[i]->val_datetime_packed(thd); /* Check if we need to stop (because of error or KILL) and stop the loop */ if (unlikely(args[i]->null_value)) @@ -2629,8 +2597,8 @@ bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) } unpack_time(min_max, ltime, mysql_timestamp_type()); - if (!(fuzzy_date & TIME_TIME_ONLY) && - unlikely((null_value= check_date_with_warn(ltime, fuzzy_date, + if (!(fuzzydate & TIME_TIME_ONLY) && + unlikely((null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)))) return true; @@ -2638,17 +2606,17 @@ bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) } -bool Item_func_min_max::get_time_native(MYSQL_TIME *ltime) +bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); - Time value(args[0]); + Time value(thd, args[0], Time::Options(), decimals); if (!value.is_valid_time()) return (null_value= true); for (uint i= 1; i < arg_count ; i++) { - Time tmp(args[i]); + Time tmp(thd, args[i], Time::Options(), decimals); if (!tmp.is_valid_time()) return (null_value= true); @@ -5416,7 +5384,8 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer) } -bool Item_user_var_as_out_param::get_date(MYSQL_TIME *ltime, ulonglong fuzzy) +bool Item_user_var_as_out_param::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) { DBUG_ASSERT(0); return true; @@ -6512,10 +6481,10 @@ my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value) } -bool Item_func_last_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_func_last_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { evaluate_sideeffects(); - bool tmp= last_value->get_date(ltime, fuzzydate); + bool tmp= last_value->get_date(thd, ltime, fuzzydate); null_value= last_value->null_value; return tmp; } diff --git a/sql/item_func.h b/sql/item_func.h index 48b2ad2afeb..660d39f48ea 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -76,7 +76,10 @@ public: SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC, NEG_FUNC, GSYSVAR_FUNC, IN_OPTIMIZER_FUNC, DYNCOL_FUNC, - JSON_EXTRACT_FUNC }; + JSON_EXTRACT_FUNC, + CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider + CASE_SIMPLE_FUNC // Used by ColumnStore/spider + }; static scalar_comparison_op functype_to_scalar_comparison_op(Functype type) { switch (type) { @@ -177,12 +180,6 @@ public: virtual void print(String *str, enum_query_type query_type); void print_op(String *str, enum_query_type query_type); void print_args(String *str, uint from, enum_query_type query_type); - inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) - { - DBUG_ASSERT(!(fuzzy_date & TIME_TIME_ONLY)); - Datetime dt(current_thd, args[0], fuzzy_date); - return (null_value= dt.copy_to_mysql_time(ltime)); - } bool is_null() { update_null_value(); return null_value; @@ -410,9 +407,12 @@ public: String *val_str(String*str); my_decimal *val_decimal(my_decimal *decimal_value); longlong val_int() - { DBUG_ASSERT(fixed == 1); return (longlong) rint(val_real()); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_real(ltime, fuzzydate); } + { + DBUG_ASSERT(fixed == 1); + return Converter_double_to_longlong(val_real(), unsigned_flag).result(); + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_real(thd, ltime, fuzzydate); } const Type_handler *type_handler() const { return &type_handler_double; } bool fix_length_and_dec() { @@ -464,7 +464,7 @@ public: virtual double val_real(Item_handled_func *) const= 0; virtual longlong val_int(Item_handled_func *) const= 0; virtual my_decimal *val_decimal(Item_handled_func *, my_decimal *) const= 0; - virtual bool get_date(Item_handled_func *, MYSQL_TIME *, ulonglong fuzzydate) const= 0; + virtual bool get_date(THD *thd, Item_handled_func *, MYSQL_TIME *, date_mode_t fuzzydate) const= 0; virtual const Type_handler *return_type_handler() const= 0; virtual bool fix_length_and_dec(Item_handled_func *) const= 0; }; @@ -632,9 +632,9 @@ public: { return m_func_handler->val_decimal(this, to); } - bool get_date(MYSQL_TIME *to, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) { - return m_func_handler->get_date(this, to, fuzzydate); + return m_func_handler->get_date(thd, this, to, fuzzydate); } }; @@ -661,15 +661,15 @@ class Item_func_hybrid_field_type: public Item_hybrid_func Helper methods to make sure that the result of decimal_op(), str_op() and date_op() is properly synched with null_value. */ - bool date_op_with_null_check(MYSQL_TIME *ltime) + bool date_op_with_null_check(THD *thd, MYSQL_TIME *ltime) { - bool rc= date_op(ltime, 0); + bool rc= date_op(thd, ltime, date_mode_t(0)); DBUG_ASSERT(!rc ^ null_value); return rc; } - bool time_op_with_null_check(MYSQL_TIME *ltime) + bool time_op_with_null_check(THD *thd, MYSQL_TIME *ltime) { - bool rc= time_op(ltime); + bool rc= time_op(thd, ltime); DBUG_ASSERT(!rc ^ null_value); DBUG_ASSERT(rc || ltime->time_type == MYSQL_TIMESTAMP_TIME); return rc; @@ -680,11 +680,6 @@ class Item_func_hybrid_field_type: public Item_hybrid_func DBUG_ASSERT((res != NULL) ^ null_value); return res; } - bool make_zero_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate) - { - bzero(ltime, sizeof(*ltime)); - return null_value|= !(fuzzydate & TIME_FUZZY_DATES); - } public: // Value methods that involve no conversion @@ -723,10 +718,6 @@ public: double val_real_from_time_op(); double val_real_from_int_op(); - bool get_date_from_str_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_real_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_date_from_int_op(MYSQL_TIME *ltime, ulonglong fuzzydate); - public: Item_func_hybrid_field_type(THD *thd): Item_hybrid_func(thd) @@ -770,11 +761,11 @@ public: DBUG_ASSERT(null_value == (res == NULL)); return res; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) { DBUG_ASSERT(fixed); return Item_func_hybrid_field_type::type_handler()-> - Item_func_hybrid_field_type_get_date(this, res, fuzzy_date); + Item_func_hybrid_field_type_get_date_with_warn(thd, this, to, mode); } /** @@ -784,6 +775,20 @@ public: @return The result of the operation. */ virtual longlong int_op()= 0; + Longlong_null to_longlong_null_op() + { + longlong nr= int_op(); + /* + C++ does not guarantee the order of parameter evaluation, + so to make sure "null_value" is passed to the constructor + after the int_op() call, int_op() is caled on a separate line. + */ + return Longlong_null(nr, null_value); + } + Longlong_hybrid_null to_longlong_hybrid_null_op() + { + return Longlong_hybrid_null(to_longlong_null_op(), unsigned_flag); + } /** @brief Performs the operation that this functions implements when the @@ -792,6 +797,12 @@ public: @return The result of the operation. */ virtual double real_op()= 0; + Double_null to_double_null_op() + { + // val_real() must be caleed on a separate line. See to_longlong_null() + double nr= real_op(); + return Double_null(nr, null_value); + } /** @brief Performs the operation that this functions implements when the @@ -818,14 +829,14 @@ public: field type is DATETIME or DATE. @return The result of the operation. */ - virtual bool date_op(MYSQL_TIME *res, ulonglong fuzzy_date)= 0; + virtual bool date_op(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate)= 0; /** @brief Performs the operation that this functions implements when field type is TIME. @return The result of the operation. */ - virtual bool time_op(MYSQL_TIME *res)= 0; + virtual bool time_op(THD *thd, MYSQL_TIME *res)= 0; }; @@ -884,12 +895,12 @@ public: Item_func_hybrid_field_type(thd, list) { } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); return true; } - bool time_op(MYSQL_TIME *ltime) + bool time_op(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(0); return true; @@ -975,8 +986,8 @@ public: { collation.set_numeric(); } double val_real(); String *val_str(String*str); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_int(thd, ltime, fuzzydate); } const Type_handler *type_handler() const= 0; bool fix_length_and_dec() { return FALSE; } }; @@ -1170,8 +1181,10 @@ public: double val_real() { return VDec(this).to_double(); } longlong val_int() { return VDec(this).to_longlong(unsigned_flag); } my_decimal *val_decimal(my_decimal*); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return VDec(this).to_datetime_with_warn(ltime, fuzzydate, this); } + bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) + { + return decimal_to_datetime_with_warn(thd, VDec(this).ptr(), to, mode, NULL); + } const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec_generic() {} bool fix_length_and_dec() @@ -1725,8 +1738,8 @@ public: double val_real_native(); longlong val_int_native(); my_decimal *val_decimal_native(my_decimal *); - bool get_date_native(MYSQL_TIME *res, ulonglong fuzzydate); - bool get_time_native(MYSQL_TIME *res); + bool get_date_native(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); + bool get_time_native(THD *thd, MYSQL_TIME *res); double val_real() { @@ -1752,11 +1765,11 @@ public: return Item_func_min_max::type_handler()-> Item_func_min_max_val_decimal(this, dec); } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { DBUG_ASSERT(fixed); return Item_func_min_max::type_handler()-> - Item_func_min_max_get_date(this, res, fuzzy_date); + Item_func_min_max_get_date(thd, this, res, fuzzydate); } void aggregate_attributes_real(Item **items, uint nitems) { @@ -1823,8 +1836,8 @@ public: String *val_str(String *str) { return val_str_from_item(args[0], str); } my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_item(args[0], dec); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_item(args[0], ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_item(thd, args[0], ltime, fuzzydate); } const char *func_name() const { return "rollup_const"; } bool const_item() const { return 0; } const Type_handler *type_handler() const { return args[0]->type_handler(); } @@ -2280,9 +2293,9 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } }; @@ -2298,7 +2311,8 @@ class Item_func_udf_float :public Item_udf_func longlong val_int() { DBUG_ASSERT(fixed == 1); - return (longlong) rint(Item_func_udf_float::val_real()); + return Converter_double_to_longlong(Item_func_udf_float::val_real(), + unsigned_flag).result(); } my_decimal *val_decimal(my_decimal *dec_buf) { @@ -2590,8 +2604,10 @@ public: Field *create_field_for_create_select(TABLE *table) { return create_table_field_from_handler(table); } bool check_vcol_func_processor(void *arg); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return type_handler()->Item_get_date(this, ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); + } }; @@ -2772,7 +2788,7 @@ public: double val_real(); longlong val_int(); String *val_str(String *str); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); my_decimal *val_decimal(my_decimal *decimal_buffer); /* fix_fields() binds variable name with its entry structure */ bool fix_fields(THD *thd, Item **ref); @@ -2819,9 +2835,9 @@ public: String* val_str(String*); my_decimal *val_decimal(my_decimal *dec_buf) { return val_decimal_from_real(dec_buf); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } /* TODO: fix to support views */ const char *func_name() const { return "get_system_var"; } @@ -3111,7 +3127,7 @@ public: return sp_result_field->val_decimal(dec_buf); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (execute()) return true; @@ -3165,15 +3181,7 @@ public: { return TRUE; } - Item *get_copy(THD *thd) - { return get_item_copy<Item_func_sp>(thd, this); } - Item *build_clone(THD *thd) - { - Item_func_sp *clone= (Item_func_sp *) Item_func::build_clone(thd); - if (clone) - clone->sp_result_field= NULL; - return clone; - } + Item *get_copy(THD *) { return 0; } bool eval_not_null_tables(void *opt_arg) { not_null_tables_cache= 0; @@ -3273,7 +3281,7 @@ public: longlong val_int(); String *val_str(String *); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec(); const char *func_name() const { return "last_value"; } const Type_handler *type_handler() const { return last_value->type_handler(); } diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 5e7f49e2a26..4ec481cf439 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -479,7 +479,7 @@ String *Item_func_json_value::val_str(String *str) (const uchar *) js->ptr() + js->length()); str->length(0); - str->set_charset(&my_charset_utf8mb4_bin); + str->set_charset(collation.collation); path.cur_step= path.p.steps; continue_search: @@ -1622,13 +1622,15 @@ String *Item_func_json_array_append::val_str(String *str) if (je.value_type == JSON_VALUE_ARRAY) { - if (json_skip_level(&je)) + int n_items; + if (json_skip_level_and_count(&je, &n_items)) goto js_error; ar_end= je.s.c_str - je.sav_c_len; str_rest_len= js->length() - (ar_end - (const uchar *) js->ptr()); str->q_append(js->ptr(), ar_end-(const uchar *) js->ptr()); - str->append(", ", 2); + if (n_items) + str->append(", ", 2); if (append_json_value(str, args[n_arg+1], &tmp_val)) goto return_null; /* Out of memory. */ @@ -2019,23 +2021,14 @@ continue_j2: else { const uchar *end1, *beg1, *end2, *beg2; - int empty_array= 0; + int n_items1=1, n_items2= 1; beg1= je1->value_begin; /* Merge as a single array. */ if (je1->value_type == JSON_VALUE_ARRAY) { - int cur_level= je1->stack_p; - empty_array= 1; - while (json_scan_next(je1) == 0) - { - if (je1->stack_p < cur_level) - break; - empty_array= 0; - } - - if (unlikely(je1->s.error)) + if (json_skip_level_and_count(je1, &n_items1)) return 1; end1= je1->s.c_str - je1->sav_c_len; @@ -2054,8 +2047,7 @@ continue_j2: end1= je1->value_end; } - if (str->append((const char*) beg1, end1 - beg1) || - (!empty_array && str->append(", ", 2))) + if (str->append((const char*) beg1, end1 - beg1)) return 3; if (json_value_scalar(je2)) @@ -2066,15 +2058,22 @@ continue_j2: else { if (je2->value_type == JSON_VALUE_OBJECT) + { beg2= je2->value_begin; + if (json_skip_level(je2)) + return 2; + } else + { beg2= je2->s.c_str; - if (json_skip_level(je2)) - return 2; + if (json_skip_level_and_count(je2, &n_items2)) + return 2; + } end2= je2->s.c_str; } - if (str->append((const char*) beg2, end2 - beg2)) + if ((n_items1 && n_items2 && str->append(", ", 2)) || + str->append((const char*) beg2, end2 - beg2)) return 3; if (je2->value_type != JSON_VALUE_ARRAY && @@ -2387,8 +2386,9 @@ String *Item_func_json_insert::val_str(String *str) if ((null_value= args[0]->null_value)) return 0; - str->set_charset(js->charset()); - json_string_set_cs(&key_name, js->charset()); + str->set_charset(collation.collation); + tmp_js.set_charset(collation.collation); + json_string_set_cs(&key_name, collation.collation); for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) { @@ -2602,7 +2602,6 @@ continue_point: json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); str->length(0); - str->set_charset(js->charset()); if (json_nice(&je, str, Item_func_json_format::LOOSE)) goto js_error; @@ -3057,7 +3056,7 @@ static int append_json_path(String *str, const json_path_t *p) String *Item_func_json_search::val_str(String *str) { String *js= args[0]->val_json(&tmp_js); - String *s_str= args[2]->val_str(&tmp_js); + String *s_str= args[2]->val_str(&tmp_path); json_engine_t je; json_path_t p, sav_path; uint n_arg; @@ -3306,5 +3305,3 @@ int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s) return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0); } - - diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index af0995b9605..a4705f012f2 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -407,7 +407,7 @@ public: class Item_func_json_search: public Item_json_str_multipath { protected: - String tmp_js, esc_value; + String tmp_js, tmp_path, esc_value; bool mode_one; bool ooa_constant, ooa_parsed; int escape; diff --git a/sql/item_row.h b/sql/item_row.h index 278dc88479d..4f60a33ab9f 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -91,7 +91,7 @@ public: illegal_method_call((const char*)"val_decimal"); return 0; }; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { illegal_method_call((const char*)"get_date"); return true; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 12618d68c27..6c82c580858 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -603,7 +603,7 @@ String *Item_func_concat::val_str(String *str) goto null; if (res != str) - str->copy(res->ptr(), res->length(), res->charset()); + str->copy_or_move(res->ptr(), res->length(), res->charset()); for (uint i= 1 ; i < arg_count ; i++) { @@ -1568,32 +1568,18 @@ String *Item_str_conv::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res; - if (!(res=args[0]->val_str(str))) - { - null_value=1; /* purecov: inspected */ - return 0; /* purecov: inspected */ - } - null_value=0; - if (multiply == 1) - { - size_t len; - res= copy_if_not_alloced(&tmp_value, res, res->length()); - len= converter(collation.collation, (char*) res->ptr(), res->length(), - (char*) res->ptr(), res->length()); - DBUG_ASSERT(len <= res->length()); - res->length(len); - } - else - { - size_t len= res->length() * multiply; - tmp_value.alloc(len); - tmp_value.set_charset(collation.collation); - len= converter(collation.collation, (char*) res->ptr(), res->length(), - (char*) tmp_value.ptr(), len); - tmp_value.length(len); - res= &tmp_value; - } - return res; + size_t alloced_length, len; + + if ((null_value= (!(res= args[0]->val_str(&tmp_value)) || + str->alloc((alloced_length= res->length() * multiply))))) + return 0; + + len= converter(collation.collation, (char*) res->ptr(), res->length(), + (char*) str->ptr(), alloced_length); + DBUG_ASSERT(len <= alloced_length); + str->set_charset(collation.collation); + str->length(len); + return str; } @@ -1796,7 +1782,7 @@ String *Item_func_substr_index::val_str(String *str) DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),system_charset_info); - String *res= args[0]->val_str(str); + String *res= args[0]->val_str(&tmp_value); String *delimiter= args[1]->val_str(&tmp); int32 count= (int32) args[2]->val_int(); uint offset; @@ -1845,20 +1831,31 @@ String *Item_func_substr_index::val_str(String *str) if (pass == 0) /* count<0 */ { c+=n+1; - if (c<=0) return res; /* not found, return original string */ + if (c<=0) + { + str->copy(res->ptr(), res->length(), collation.collation); + return str; // not found, return the original string + } ptr=res->ptr(); } else { - if (c) return res; /* Not found, return original string */ + if (c) + { + str->copy(res->ptr(), res->length(), collation.collation); + return str; // not found, return the original string + } if (count>0) /* return left part */ { - tmp_value.set(*res,0,(ulong) (ptr-res->ptr())); + str->copy(res->ptr(), (uint32) (ptr-res->ptr()), collation.collation); + return str; } else /* return right part */ { - ptr+= delimiter_length; - tmp_value.set(*res,(ulong) (ptr-res->ptr()), (ulong) (strend-ptr)); + ptr+= delimiter_length; + str->copy(res->ptr() + (ptr-res->ptr()), (uint32) (strend - ptr), + collation.collation); + return str; } } } @@ -1870,13 +1867,16 @@ String *Item_func_substr_index::val_str(String *str) { // start counting from the beginning for (offset=0; ; offset+= delimiter_length) { - if ((int) (offset= res->strstr(*delimiter, offset)) < 0) - return res; // Didn't find, return org string - if (!--count) - { - tmp_value.set(*res,0,offset); - break; - } + if ((int) (offset= res->strstr(*delimiter, offset)) < 0) + { + str->copy(res->ptr(), res->length(), collation.collation); + return str; // not found, return the original string + } + if (!--count) + { + str->copy(res->ptr(), offset, collation.collation); + return str; + } } } else @@ -1891,30 +1891,32 @@ String *Item_func_substr_index::val_str(String *str) address space less than where the found substring is located in res */ - if ((int) (offset= res->strrstr(*delimiter, offset)) < 0) - return res; // Didn't find, return org string + if ((int) (offset= res->strrstr(*delimiter, offset)) < 0) + { + str->copy(res->ptr(), res->length(), collation.collation); + return str; // not found, return the original string + } /* At this point, we've searched for the substring the number of times as supplied by the index value */ - if (!++count) - { - offset+= delimiter_length; - tmp_value.set(*res,offset,res->length()- offset); - break; - } + if (!++count) + { + offset+= delimiter_length; + str->copy(res->ptr() + offset, res->length() - offset, + collation.collation); + return str; + } } if (count) - return res; // Didn't find, return org string + { + str->copy(res->ptr(), res->length(), collation.collation); + return str; // not found, return the original string + } } } - /* - We always mark tmp_value as const so that if val_str() is called again - on this object, we don't disrupt the contents of tmp_value when it was - derived from another String. - */ - tmp_value.mark_as_const(); - return (&tmp_value); + DBUG_ASSERT(0); + return NULL; } /* @@ -4541,11 +4543,11 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg) break; case DYN_COL_DATETIME: case DYN_COL_DATE: - args[valpos]->get_date(&vals[i].x.time_value, + args[valpos]->get_date(thd, &vals[i].x.time_value, sql_mode_for_dates(thd)); break; case DYN_COL_TIME: - args[valpos]->get_time(&vals[i].x.time_value); + args[valpos]->get_time(thd, &vals[i].x.time_value); break; default: DBUG_ASSERT(0); @@ -5111,7 +5113,7 @@ null: } -bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_dyncol_get::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DYNAMIC_COLUMN_VALUE val; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -5132,10 +5134,8 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (signed_value || val.x.ulong_value <= LONGLONG_MAX) { longlong llval = (longlong)val.x.ulong_value; - bool neg = llval < 0; - if (int_to_datetime_with_warn(neg, (ulonglong)(neg ? -llval : - llval), - ltime, fuzzy_date, 0 /* TODO */)) + if (int_to_datetime_with_warn(thd, Longlong_hybrid(llval, !signed_value), + ltime, fuzzydate, 0 /* TODO */)) goto null; return 0; } @@ -5143,20 +5143,20 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) val.x.double_value= static_cast<double>(ULONGLONG_MAX); /* fall through */ case DYN_COL_DOUBLE: - if (double_to_datetime_with_warn(val.x.double_value, ltime, fuzzy_date, + if (double_to_datetime_with_warn(thd, val.x.double_value, ltime, fuzzydate, 0 /* TODO */)) goto null; return 0; case DYN_COL_DECIMAL: - if (decimal_to_datetime_with_warn((my_decimal*)&val.x.decimal.value, ltime, - fuzzy_date, 0 /* TODO */)) + if (decimal_to_datetime_with_warn(thd, (my_decimal*)&val.x.decimal.value, + ltime, fuzzydate, 0 /* TODO */)) goto null; return 0; case DYN_COL_STRING: - if (str_to_datetime_with_warn(&my_charset_numeric, + if (str_to_datetime_with_warn(thd, &my_charset_numeric, val.x.string.value.str, val.x.string.value.length, - ltime, fuzzy_date)) + ltime, fuzzydate)) goto null; return 0; case DYN_COL_DATETIME: diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 7c4ab93dc80..762a3c2559e 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -62,8 +62,8 @@ public: longlong val_int(); double val_real(); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_string(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_string(thd, ltime, fuzzydate); } const Type_handler *type_handler() const { return string_type_handler(); } void left_right_max_length(); bool fix_fields(THD *thd, Item **ref); @@ -1465,11 +1465,11 @@ public: return NULL; return res; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { if (args[0]->result_type() == STRING_RESULT) - return Item_str_func::get_date(ltime, fuzzydate); - bool res= args[0]->get_date(ltime, fuzzydate); + return Item_str_func::get_date(thd, ltime, fuzzydate); + bool res= args[0]->get_date(thd, ltime, fuzzydate); if ((null_value= args[0]->null_value)) return 1; return res; @@ -1764,7 +1764,7 @@ public: double val_real(); my_decimal *val_decimal(my_decimal *); bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd) { return get_item_copy<Item_dyncol_get>(thd, this); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index eef6755bc98..7408dc378ef 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1388,15 +1388,15 @@ bool Item_singlerow_subselect::val_bool() } -bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->get_date(ltime, fuzzydate); + return value->get_date(thd, ltime, fuzzydate); if (!exec() && !value->null_value) { null_value= FALSE; - return value->get_date(ltime, fuzzydate); + return value->get_date(thd, ltime, fuzzydate); } else { @@ -1991,8 +1991,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join) print_where(item, "rewrite with MIN/MAX", QT_ORDINARY);); save_allow_sum_func= thd->lex->allow_sum_func; - thd->lex->allow_sum_func|= - (nesting_map)1 << thd->lex->current_select->nest_level; + thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level); /* Item_sum_(max|min) can't substitute other item => we can use 0 as reference, also Item_sum_(max|min) can't be fixed after creation, so diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 4980709b979..443354f4900 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -308,7 +308,7 @@ public: String *val_str (String *); my_decimal *val_decimal(my_decimal *); bool val_bool(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); const Type_handler *type_handler() const; bool fix_length_and_dec(); @@ -405,8 +405,8 @@ public: String *val_str(String*); my_decimal *val_decimal(my_decimal *); bool val_bool(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return get_date_from_int(ltime, fuzzydate); } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { return get_date_from_int(thd, ltime, fuzzydate); } bool fix_fields(THD *thd, Item **ref); bool fix_length_and_dec(); void print(String *str, enum_query_type query_type); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 98a3f6116a6..0e52b2988a3 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -72,14 +72,15 @@ size_t Item_sum::ram_limitation(THD *thd) bool Item_sum::init_sum_func_check(THD *thd) { SELECT_LEX *curr_sel= thd->lex->current_select; - if (curr_sel && !curr_sel->name_visibility_map) + if (curr_sel && curr_sel->name_visibility_map.is_clear_all()) { for (SELECT_LEX *sl= curr_sel; sl; sl= sl->context.outer_select()) { - curr_sel->name_visibility_map|= (1 << sl-> nest_level); + curr_sel->name_visibility_map.set_bit(sl->nest_level); } } - if (!curr_sel || !(thd->lex->allow_sum_func & curr_sel->name_visibility_map)) + if (!curr_sel || + !(thd->lex->allow_sum_func.is_overlapping(curr_sel->name_visibility_map))) { my_message(ER_INVALID_GROUP_FUNC_USE, ER_THD(thd, ER_INVALID_GROUP_FUNC_USE), MYF(0)); @@ -155,10 +156,11 @@ bool Item_sum::init_sum_func_check(THD *thd) bool Item_sum::check_sum_func(THD *thd, Item **ref) { SELECT_LEX *curr_sel= thd->lex->current_select; - nesting_map allow_sum_func= (thd->lex->allow_sum_func & - curr_sel->name_visibility_map); + nesting_map allow_sum_func(thd->lex->allow_sum_func); + allow_sum_func.intersect(curr_sel->name_visibility_map); bool invalid= FALSE; - DBUG_ASSERT(curr_sel->name_visibility_map); // should be set already + // should be set already + DBUG_ASSERT(!curr_sel->name_visibility_map.is_clear_all()); /* Window functions can not be used as arguments to sum functions. @@ -189,10 +191,10 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) If it is there under a construct where it is not allowed we report an error. */ - invalid= !(allow_sum_func & ((nesting_map)1 << max_arg_level)); + invalid= !(allow_sum_func.is_set(max_arg_level)); } else if (max_arg_level >= 0 || - !(allow_sum_func & ((nesting_map)1 << nest_level))) + !(allow_sum_func.is_set(nest_level))) { /* The set function can be aggregated only in outer subqueries. @@ -202,7 +204,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) if (register_sum_func(thd, ref)) return TRUE; invalid= aggr_level < 0 && - !(allow_sum_func & ((nesting_map)1 << nest_level)); + !(allow_sum_func.is_set(nest_level)); if (!invalid && thd->variables.sql_mode & MODE_ANSI) invalid= aggr_level < 0 && max_arg_level < nest_level; } @@ -354,14 +356,14 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) sl= sl->context.outer_select()) { if (aggr_level < 0 && - (allow_sum_func & ((nesting_map)1 << sl->nest_level))) + (allow_sum_func.is_set(sl->nest_level))) { /* Found the most nested subquery where the function can be aggregated */ aggr_level= sl->nest_level; aggr_sel= sl; } } - if (sl && (allow_sum_func & ((nesting_map)1 << sl->nest_level))) + if (sl && (allow_sum_func.is_set(sl->nest_level))) { /* We reached the subquery of level max_arg_level and checked @@ -2305,12 +2307,12 @@ void Item_sum_hybrid::clear() bool -Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +Item_sum_hybrid::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (null_value) return true; - bool retval= value->get_date(ltime, fuzzydate); + bool retval= value->get_date(thd, ltime, fuzzydate); if ((null_value= value->null_value)) DBUG_ASSERT(retval == true); return retval; diff --git a/sql/item_sum.h b/sql/item_sum.h index fe055673328..1a21c257221 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -742,9 +742,9 @@ public: longlong val_int() { return val_int_from_real(); /* Real as default */ } String *val_str(String*str); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } void reset_field(); }; @@ -1067,7 +1067,7 @@ protected: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); void reset_field(); String *val_str(String *); const Type_handler *real_type_handler() const @@ -1363,7 +1363,7 @@ public: void update_field(){DBUG_ASSERT(0);} void clear(); void cleanup(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { return execute() || sp_result_field->get_date(ltime, fuzzydate); } @@ -1403,9 +1403,9 @@ public: { return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } }; @@ -1565,9 +1565,9 @@ public: void update_field() {}; void cleanup(); virtual void print(String *str, enum_query_type query_type); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } }; @@ -1874,9 +1874,9 @@ public: { return val_decimal_from_string(decimal_value); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return get_date_from_string(ltime, fuzzydate); + return get_date_from_string(thd, ltime, fuzzydate); } String* val_str(String* str); Item *copy_or_same(THD* thd); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 84db154566a..2da92f971d1 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -41,6 +41,7 @@ #include "set_var.h" #include "sql_locale.h" // MY_LOCALE my_locale_en_US #include "strfunc.h" // check_word +#include "sql_type_int.h" // Longlong_hybrid #include "sql_time.h" // make_truncated_value_warning, // get_date_from_daynr, // calc_weekday, calc_week, @@ -125,12 +126,12 @@ static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0, 1 error */ -static bool extract_date_time(DATE_TIME_FORMAT *format, +static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format, const char *val, uint length, MYSQL_TIME *l_time, timestamp_type cached_timestamp_type, const char **sub_pattern_end, const char *date_time_type, - ulonglong fuzzy_date) + date_mode_t fuzzydate) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -325,17 +326,17 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, We can't just set error here, as we don't want to generate two warnings in case of errors */ - if (extract_date_time(&time_ampm_format, val, + if (extract_date_time(thd, &time_ampm_format, val, (uint)(val_end - val), l_time, - cached_timestamp_type, &val, "time", fuzzy_date)) + cached_timestamp_type, &val, "time", fuzzydate)) DBUG_RETURN(1); break; /* Time in 24-hour notation */ case 'T': - if (extract_date_time(&time_24hrs_format, val, + if (extract_date_time(thd, &time_24hrs_format, val, (uint)(val_end - val), l_time, - cached_timestamp_type, &val, "time", fuzzy_date)) + cached_timestamp_type, &val, "time", fuzzydate)) DBUG_RETURN(1); break; @@ -441,7 +442,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, goto err; int was_cut; - if (check_date(l_time, fuzzy_date | TIME_INVALID_DATES, &was_cut)) + if (check_date(l_time, fuzzydate | TIME_INVALID_DATES, &was_cut)) goto err; if (val != val_end) @@ -451,8 +452,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, if (!my_isspace(&my_charset_latin1,*val)) { ErrConvString err(val_begin, length, &my_charset_bin); - make_truncated_value_warning(current_thd, - Sql_condition::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, &err, cached_timestamp_type, NullS); break; } @@ -462,7 +462,6 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, err: { - THD *thd= current_thd; char buff[128]; strmake(buff, val_begin, MY_MIN(length, sizeof(buff)-1)); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -809,10 +808,8 @@ longlong Item_func_period_diff::val_int() longlong Item_func_to_days::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day); + Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return (null_value= !d.is_valid_date()) ? 0 : d.daynr(); } @@ -820,42 +817,26 @@ longlong Item_func_to_seconds::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong seconds; - longlong days; - int dummy; /* unused */ - if (get_arg0_date(<ime, TIME_FUZZY_DATES)) + Datetime dt(current_thd, args[0], TIME_FUZZY_DATES); + if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; } - seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second; - seconds= ltime.neg ? -seconds : seconds; - days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day); - seconds+= days * 24L * 3600L; /* Set to NULL if invalid date, but keep the value */ - null_value= check_date(<ime, - (ltime.year || ltime.month || ltime.day), - (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), - &dummy); + null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); /* Even if the evaluation return NULL, seconds is useful for pruning */ - return seconds; + return dt.to_seconds(); } longlong Item_func_to_seconds::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong seconds; - longlong days; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second; - seconds=ltime.neg ? -seconds : seconds; - days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day); - return seconds + days * 24L * 3600L; + THD *thd= current_thd; + Datetime dt(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return (null_value= !dt.is_valid_datetime()) ? 0 : dt.to_seconds(); } /* @@ -899,19 +880,16 @@ enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; + Datetime dt(current_thd, args[0], date_mode_t(0)); longlong res; - int dummy; /* unused */ - if (get_arg0_date(<ime, 0)) + if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; } - res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day); + res= (longlong) dt.daynr(); /* Set to NULL if invalid date, but keep the value */ - null_value= check_date(<ime, - (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), - &dummy); + null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); if (null_value) { /* @@ -940,8 +918,8 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15') */ - if ((!left_endp && !(ltime.hour || ltime.minute || ltime.second || - ltime.second_part)) || + const MYSQL_TIME <ime= dt.get_mysql_time()[0]; + if ((!left_endp && dt.hhmmssff_is_zero()) || (left_endp && ltime.hour == 23 && ltime.minute == 59 && ltime.second == 59)) /* do nothing */ @@ -955,25 +933,22 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) longlong Item_func_dayofyear::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE)) - return 0; - return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) - - calc_daynr(ltime.year,1,1) + 1; + Date d(current_thd, args[0], TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); + return (null_value= !d.is_valid_date()) ? 0 : d.dayofyear(); } longlong Item_func_dayofmonth::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - return get_arg0_date(<ime, 0) ? 0 : (longlong) ltime.day; + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->day; } longlong Item_func_month::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - return get_arg0_date(<ime, 0) ? 0 : (longlong) ltime.month; + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->month; } @@ -995,12 +970,12 @@ String* Item_func_monthname::val_str(String* str) DBUG_ASSERT(fixed == 1); const char *month_name; uint err; - MYSQL_TIME ltime; + Date d(current_thd, args[0], date_mode_t(0)); - if ((null_value= (get_arg0_date(<ime, 0) || !ltime.month))) + if ((null_value= (!d.is_valid_date() || !d.get_mysql_time()->month))) return (String *) 0; - month_name= locale->month_names->type_names[ltime.month - 1]; + month_name= locale->month_names->type_names[d.get_mysql_time()->month - 1]; str->copy(month_name, (uint) strlen(month_name), &my_charset_utf8_bin, collation.collation, &err); return str; @@ -1014,23 +989,21 @@ String* Item_func_monthname::val_str(String* str) longlong Item_func_quarter::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, 0)) - return 0; - return (longlong) ((ltime.month+2)/3); + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.quarter(); } longlong Item_func_hour::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->hour; } longlong Item_func_minute::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->minute; } @@ -1040,7 +1013,7 @@ longlong Item_func_minute::val_int() longlong Item_func_second::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->second; } @@ -1087,43 +1060,34 @@ uint week_mode(uint mode) longlong Item_func_week::val_int() { DBUG_ASSERT(fixed == 1); - uint year, week_format; - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + uint week_format; + THD *thd= current_thd; + Date d(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + if ((null_value= !d.is_valid_date())) return 0; if (arg_count > 1) week_format= (uint)args[1]->val_int(); else - week_format= current_thd->variables.default_week_format; - return (longlong) calc_week(<ime, week_mode(week_format), &year); + week_format= thd->variables.default_week_format; + return d.week(week_mode(week_format)); } longlong Item_func_yearweek::val_int() { DBUG_ASSERT(fixed == 1); - uint year,week; - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - week= calc_week(<ime, - (week_mode((uint) args[1]->val_int()) | WEEK_YEAR), - &year); - return week+year*100; + Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return (null_value= !d.is_valid_date()) ? 0 : + d.yearweek((week_mode((uint) args[1]->val_int()) | WEEK_YEAR)); } longlong Item_func_weekday::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - - if (get_arg0_date(<ime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return 0; - - return (longlong) calc_weekday(calc_daynr(ltime.year, ltime.month, - ltime.day), - odbc_type) + MY_TEST(odbc_type); + Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + return ((null_value= !d.is_valid_date())) ? 0 : + calc_weekday(d.daynr(), odbc_type) + MY_TEST(odbc_type); } bool Item_func_dayname::fix_length_and_dec() @@ -1159,8 +1123,8 @@ String* Item_func_dayname::val_str(String* str) longlong Item_func_year::val_int() { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - return get_arg0_date(<ime, 0) ? 0 : (longlong) ltime.year; + Date d(current_thd, args[0], date_mode_t(0)); + return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->year; } @@ -1191,8 +1155,8 @@ enum_monotonicity_info Item_func_year::get_monotonicity_info() const longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime, 0)) + Datetime dt(current_thd, args[0], date_mode_t(0)); + if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; @@ -1209,8 +1173,9 @@ longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) col < '2007-09-15 23:00:00' -> YEAR(col) <= 2007 */ + const MYSQL_TIME <ime= dt.get_mysql_time()[0]; if (!left_endp && ltime.day == 1 && ltime.month == 1 && - !(ltime.hour || ltime.minute || ltime.second || ltime.second_part)) + dt.hhmmssff_is_zero()) ; /* do nothing */ else *incl_endp= TRUE; @@ -1234,13 +1199,14 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds, } } - MYSQL_TIME ltime; - if (get_arg0_date(<ime, TIME_NO_ZERO_IN_DATE)) - return 1; + THD *thd= current_thd; + Datetime dt(thd, args[0], TIME_NO_ZERO_IN_DATE); + if ((null_value= !dt.is_valid_datetime())) + return true; uint error_code; - *seconds= TIME_to_timestamp(current_thd, <ime, &error_code); - *second_part= ltime.second_part; + *seconds= TIME_to_timestamp(thd, dt.get_mysql_time(), &error_code); + *second_part= dt.get_mysql_time()->second_part; return (null_value= (error_code == ER_WARN_DATA_OUT_OF_RANGE)); } @@ -1298,7 +1264,7 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e longlong Item_func_time_to_sec::int_op() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return ((null_value= !tm.is_valid_time())) ? 0 : tm.to_seconds(); } @@ -1306,7 +1272,7 @@ longlong Item_func_time_to_sec::int_op() my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf) { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); if ((null_value= !tm.is_valid_time())) return 0; const MYSQL_TIME *ltime= tm.get_mysql_time(); @@ -1321,7 +1287,8 @@ my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf) To make code easy, allow interval objects without separators. */ -bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) +bool get_interval_value(THD *thd, Item *args, + interval_type int_type, INTERVAL *interval) { ulonglong array[5]; longlong UNINIT_VAR(value); @@ -1342,7 +1309,7 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) if (d.sec() >= LONGLONG_MAX) { ErrConvDecimal err(val.ptr()); - current_thd->push_warning_truncated_wrong_value("seconds", err.ptr()); + thd->push_warning_truncated_wrong_value("seconds", err.ptr()); return true; } interval->second= d.sec(); @@ -1492,11 +1459,11 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) } -bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_from_days::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { longlong value=args[0]->val_int(); if ((null_value= (args[0]->null_value || - ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0)))) + ((fuzzydate & TIME_NO_ZERO_DATE) && value == 0)))) return true; bzero(ltime, sizeof(MYSQL_TIME)); if (get_date_from_daynr((long) value, <ime->year, <ime->month, @@ -1533,10 +1500,9 @@ void Item_func_curdate_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) } -bool Item_func_curdate::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_curdate::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - THD *thd= current_thd; query_id_t query_id= thd->query_id; /* Cache value for this query */ if (last_query_id != query_id) @@ -1563,10 +1529,9 @@ bool Item_func_curtime::fix_fields(THD *thd, Item **items) return Item_timefunc::fix_fields(thd, items); } -bool Item_func_curtime::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_curtime::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - THD *thd= current_thd; query_id_t query_id= thd->query_id; /* Cache value for this query */ if (last_query_id != query_id) @@ -1695,10 +1660,9 @@ void Item_func_now_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) } -bool Item_func_now::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_now::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - THD *thd= current_thd; query_id_t query_id= thd->query_id; /* Cache value for this query */ if (last_query_id != query_id) @@ -1724,21 +1688,21 @@ void Item_func_sysdate_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) } -bool Item_func_sysdate_local::get_date(MYSQL_TIME *res, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_sysdate_local::get_date(THD *thd, MYSQL_TIME *res, + date_mode_t fuzzydate __attribute__((unused))) { - store_now_in_TIME(current_thd, res); + store_now_in_TIME(thd, res); return 0; } -bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_sec_to_time::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); - VSec6 sec(args[0], "seconds", LONGLONG_MAX); + VSec6 sec(thd, args[0], "seconds", LONGLONG_MAX); if ((null_value= sec.is_null())) return true; if (sec.sec_to_time(ltime, decimals) && !sec.truncated()) - sec.make_truncated_warning(current_thd, "seconds"); + sec.make_truncated_warning(thd, "seconds"); return false; } @@ -1895,7 +1859,9 @@ String *Item_func_date_format::val_str(String *str) const MY_LOCALE *lc= 0; DBUG_ASSERT(fixed == 1); - if ((null_value= args[0]->get_date(&l_time, is_time_format ? TIME_TIME_ONLY : 0))) + if ((null_value= args[0]->get_date(current_thd, &l_time, + is_time_format ? TIME_TIME_ONLY : + date_mode_t(0)))) return 0; if (!(format = args[1]->val_str(str)) || !format->length()) @@ -1946,13 +1912,13 @@ bool Item_func_from_unixtime::fix_length_and_dec() } -bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_from_unixtime::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate __attribute__((unused))) { bzero((char *)ltime, sizeof(*ltime)); ltime->time_type= MYSQL_TIMESTAMP_TIME; - VSec6 sec(args[0], "unixtime", TIMESTAMP_MAX_VALUE); + VSec6 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE); DBUG_ASSERT(sec.sec() <= TIMESTAMP_MAX_VALUE); if (sec.is_null() || sec.truncated() || sec.neg()) @@ -1965,12 +1931,11 @@ bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime, } -bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, - ulonglong fuzzy_date __attribute__((unused))) +bool Item_func_convert_tz::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate __attribute__((unused))) { my_time_t my_time_tmp; String str; - THD *thd= current_thd; if (!from_tz_cached) { @@ -1984,9 +1949,12 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, to_tz_cached= args[2]->const_item(); } - if (from_tz==0 || to_tz==0 || - get_arg0_date(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) - return (null_value= 1); + if ((null_value= (from_tz == 0 || to_tz == 0))) + return true; + + Datetime *dt= new(ltime) Datetime(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + if ((null_value= !dt->is_valid_datetime())) + return true; { uint not_used; @@ -2067,9 +2035,9 @@ bool Item_date_add_interval::fix_length_and_dec() bool Func_handler_date_add_interval_datetime_arg0_time:: - get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { - THD *thd= current_thd; // time_expr + INTERVAL {YEAR|QUARTER|MONTH|WEEK|YEAR_MONTH} push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, @@ -2130,7 +2098,7 @@ bool Item_extract::fix_length_and_dec() case INTERVAL_QUARTER: set_date_length(2); break; // 1..4 case INTERVAL_MONTH: set_date_length(2); break; // MM case INTERVAL_WEEK: set_date_length(2); break; // 0..52 - case INTERVAL_DAY: set_date_length(2); break; // DD + case INTERVAL_DAY: set_day_length(2); break; // DD case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss @@ -2151,69 +2119,45 @@ bool Item_extract::fix_length_and_dec() } -longlong Item_extract::val_int() +uint Extract_source::week(THD *thd) const { - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; + DBUG_ASSERT(is_valid_extract_source()); uint year; - ulong week_format; - long neg; - int is_time_flag = date_value ? 0 : TIME_TIME_ONLY; - - // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion - if ((null_value= args[0]->get_date(<ime, is_time_flag))) - return 0; - - neg= ltime.neg ? -1 : 1; + ulong week_format= current_thd->variables.default_week_format; + return calc_week(this, week_mode(week_format), &year); +} - DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0); - if (ltime.time_type == MYSQL_TIMESTAMP_TIME) - time_to_daytime_interval(<ime); +longlong Item_extract::val_int() +{ + DBUG_ASSERT(fixed == 1); + Extract_source dt(current_thd, args[0], m_date_mode); + if ((null_value= !dt.is_valid_extract_source())) + return 0; switch (int_type) { - case INTERVAL_YEAR: return ltime.year; - case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month; - case INTERVAL_QUARTER: return (ltime.month+2)/3; - case INTERVAL_MONTH: return ltime.month; - case INTERVAL_WEEK: - { - week_format= current_thd->variables.default_week_format; - return calc_week(<ime, week_mode(week_format), &year); - } - case INTERVAL_DAY: return ltime.day; - case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg; - case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+ - ltime.hour*100L+ - ltime.minute)*neg; - case INTERVAL_DAY_SECOND: return ((longlong) ltime.day*1000000L+ - (longlong) (ltime.hour*10000L+ - ltime.minute*100+ - ltime.second))*neg; - case INTERVAL_HOUR: return (long) ltime.hour*neg; - case INTERVAL_HOUR_MINUTE: return (long) (ltime.hour*100+ltime.minute)*neg; - case INTERVAL_HOUR_SECOND: return (long) (ltime.hour*10000+ltime.minute*100+ - ltime.second)*neg; - case INTERVAL_MINUTE: return (long) ltime.minute*neg; - case INTERVAL_MINUTE_SECOND: return (long) (ltime.minute*100+ltime.second)*neg; - case INTERVAL_SECOND: return (long) ltime.second*neg; - case INTERVAL_MICROSECOND: return (long) ltime.second_part*neg; - case INTERVAL_DAY_MICROSECOND: return (((longlong)ltime.day*1000000L + - (longlong)ltime.hour*10000L + - ltime.minute*100 + - ltime.second)*1000000L + - ltime.second_part)*neg; - case INTERVAL_HOUR_MICROSECOND: return (((longlong)ltime.hour*10000L + - ltime.minute*100 + - ltime.second)*1000000L + - ltime.second_part)*neg; - case INTERVAL_MINUTE_MICROSECOND: return (((longlong)(ltime.minute*100+ - ltime.second))*1000000L+ - ltime.second_part)*neg; - case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+ - ltime.second_part)*neg; + case INTERVAL_YEAR: return dt.year(); + case INTERVAL_YEAR_MONTH: return dt.year_month(); + case INTERVAL_QUARTER: return dt.quarter(); + case INTERVAL_MONTH: return dt.month(); + case INTERVAL_WEEK: return dt.week(current_thd); + case INTERVAL_DAY: return dt.day(); + case INTERVAL_DAY_HOUR: return dt.day_hour(); + case INTERVAL_DAY_MINUTE: return dt.day_minute(); + case INTERVAL_DAY_SECOND: return dt.day_second(); + case INTERVAL_HOUR: return dt.hour(); + case INTERVAL_HOUR_MINUTE: return dt.hour_minute(); + case INTERVAL_HOUR_SECOND: return dt.hour_second(); + case INTERVAL_MINUTE: return dt.minute(); + case INTERVAL_MINUTE_SECOND: return dt.minute_second(); + case INTERVAL_SECOND: return dt.second(); + case INTERVAL_MICROSECOND: return dt.microsecond(); + case INTERVAL_DAY_MICROSECOND: return dt.day_microsecond(); + case INTERVAL_HOUR_MICROSECOND: return dt.hour_microsecond(); + case INTERVAL_MINUTE_MICROSECOND: return dt.minute_microsecond(); + case INTERVAL_SECOND_MICROSECOND: return dt.second_microsecond(); case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */ } - return 0; // Impossible + return 0; // Impossible } bool Item_extract::eq(const Item *item, bool binary_cmp) const @@ -2472,45 +2416,28 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs) } -bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) { - Time tm(args[0], Time::Options_for_cast()); - if ((null_value= !tm.is_valid_time())) - return true; - tm.copy_to_mysql_time(ltime); - if (decimals < TIME_SECOND_PART_DIGITS) - my_time_trunc(ltime, decimals); - return (fuzzy_date & TIME_TIME_ONLY) ? 0 : - (null_value= check_date_with_warn(ltime, fuzzy_date, - MYSQL_TIMESTAMP_ERROR)); + Time *tm= new(to) Time(thd, args[0], Time::Options_for_cast(mode), + MY_MIN(decimals, TIME_SECOND_PART_DIGITS)); + return (null_value= !tm->is_valid_time()); } -bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - fuzzy_date |= sql_mode_for_dates(current_thd); - if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) - return 1; - - if (make_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_DATE)) - return (null_value= 1); - - return 0; + date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY; + Date *d= new(ltime) Date(thd, args[0], tmp); + return (null_value= !d->is_valid_date()); } -bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_datetime_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - fuzzy_date |= sql_mode_for_dates(current_thd); - if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) - return 1; - - if (decimals < TIME_SECOND_PART_DIGITS) - my_time_trunc(ltime, decimals); - - DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME); - ltime->time_type= MYSQL_TIMESTAMP_DATETIME; - return 0; + date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY; + Datetime *dt= new(ltime) Datetime(thd, args[0], tmp, + MY_MIN(decimals, TIME_SECOND_PART_DIGITS)); + return (null_value= !dt->is_valid_datetime()); } @@ -2525,7 +2452,7 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) 0099-12-31 */ -bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_makedate::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); long year, days, daynr= (long) args[1]->val_int(); @@ -2603,7 +2530,7 @@ bool Item_func_add_time::fix_length_and_dec() Result: Time value */ -bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_timediff::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); int l_sign= 1; @@ -2611,22 +2538,22 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) ErrConvTime str(&l_time3); /* the following may be true in, for example, date_add(timediff(...), ... */ - if (fuzzy_date & TIME_NO_ZERO_IN_DATE) + if (fuzzydate & TIME_NO_ZERO_IN_DATE) return (null_value= 1); - if (args[0]->get_time(&l_time1) || - args[1]->get_time(&l_time2) || + if (args[0]->get_time(thd, &l_time1) || + args[1]->get_time(thd, &l_time2) || l_time1.time_type != l_time2.time_type) return (null_value= 1); if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzy_date)) + if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzydate)) return (null_value= 1); *ltime= l_time3; - return (null_value= adjust_time_range_with_warn(ltime, decimals)); + return (null_value= adjust_time_range_with_warn(thd, ltime, decimals)); } /** @@ -2635,13 +2562,12 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) Result: Time value */ -bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); - bool overflow= 0; - longlong hour= args[0]->val_int(); + Longlong_hybrid hour(args[0]->val_int(), args[0]->unsigned_flag); longlong minute= args[1]->val_int(); - VSec6 sec(args[2], "seconds", 59); + VSec6 sec(thd, args[2], "seconds", 59); DBUG_ASSERT(sec.sec() <= 59); if (args[0]->null_value || args[1]->null_value || sec.is_null() || @@ -2650,36 +2576,27 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) bzero(ltime, sizeof(*ltime)); ltime->time_type= MYSQL_TIMESTAMP_TIME; + ltime->neg= hour.neg(); - /* Check for integer overflows */ - if (hour < 0) + if (hour.abs() <= TIME_MAX_HOUR) { - if (args[0]->unsigned_flag) - overflow= 1; - else - ltime->neg= 1; - } - if (-hour > TIME_MAX_HOUR || hour > TIME_MAX_HOUR) - overflow= 1; - - if (!overflow) - { - ltime->hour= (uint) ((hour < 0 ? -hour : hour)); + ltime->hour= (uint) hour.abs(); ltime->minute= (uint) minute; ltime->second= (uint) sec.sec(); ltime->second_part= sec.usec(); } else { - ltime->hour= TIME_MAX_HOUR; - ltime->minute= TIME_MAX_MINUTE; - ltime->second= TIME_MAX_SECOND; + // use check_time_range() to set ltime to the max value depending on dec + int unused; + ltime->hour= TIME_MAX_HOUR + 1; + check_time_range(ltime, decimals, &unused); char buf[28]; - char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10); + char *ptr= longlong10_to_str(hour.value(), buf, hour.is_unsigned() ? 10 : -10); int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint) minute, (uint) sec.sec()); ErrConvString err(buf, len, &my_charset_bin); - current_thd->push_warning_truncated_wrong_value("time", err.ptr()); + thd->push_warning_truncated_wrong_value("time", err.ptr()); } return (null_value= 0); @@ -2697,7 +2614,7 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) longlong Item_func_microsecond::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(args[0], Time::Options_for_cast()); + Time tm(current_thd, args[0], Time::Options_for_cast()); return ((null_value= !tm.is_valid_time())) ? 0 : tm.get_mysql_time()->second_part; } @@ -2711,7 +2628,7 @@ longlong Item_func_timestamp_diff::val_int() long months= 0; int neg= 1; THD *thd= current_thd; - ulonglong fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE; + date_mode_t fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE; null_value= 0; @@ -3008,8 +2925,8 @@ bool Item_func_str_to_date::fix_length_and_dec() } -bool Item_func_str_to_date::get_date_common(MYSQL_TIME *ltime, - ulonglong fuzzy_date, +bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate, timestamp_type tstype) { DATE_TIME_FORMAT date_time_format; @@ -3023,19 +2940,19 @@ bool Item_func_str_to_date::get_date_common(MYSQL_TIME *ltime, date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); - if (extract_date_time(&date_time_format, val->ptr(), val->length(), + if (extract_date_time(thd, &date_time_format, val->ptr(), val->length(), ltime, tstype, 0, "datetime", - fuzzy_date | sql_mode_for_dates(current_thd))) + fuzzydate | sql_mode_for_dates(thd))) return (null_value=1); return (null_value= 0); } -bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_last_day::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY) || - (ltime->month == 0)) - return (null_value=1); + Date *d= new(ltime) Date(thd, args[0], fuzzydate & ~TIME_TIME_ONLY); + if ((null_value= (!d->is_valid_date() || ltime->month == 0))) + return true; uint month_idx= ltime->month-1; ltime->day= days_in_month[month_idx]; if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366) diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index f9709b8f72a..d2a4e87a3d1 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -26,7 +26,8 @@ class MY_LOCALE; -bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval); +bool get_interval_value(THD *thd, Item *args, + interval_type int_type, INTERVAL *interval); class Item_long_func_date_field: public Item_long_func @@ -181,9 +182,9 @@ public: str->set(nr, collation.collation); return str; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return get_date_from_int(ltime, fuzzydate); + return get_date_from_int(thd, ltime, fuzzydate); } const char *func_name() const { return "month"; } const Type_handler *type_handler() const { return &type_handler_long; } @@ -454,9 +455,9 @@ public: { return (odbc_type ? "dayofweek" : "weekday"); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } const Type_handler *type_handler() const { return &type_handler_long; } bool fix_length_and_dec() @@ -511,7 +512,7 @@ public: } double real_op() { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } - bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(0); return true; @@ -547,7 +548,8 @@ public: } bool fix_length_and_dec() { - fix_length_and_dec_generic(arg_count ? args[0]->datetime_precision() : 0); + fix_length_and_dec_generic(arg_count ? + args[0]->datetime_precision(current_thd) : 0); return FALSE; } longlong int_op(); @@ -571,7 +573,7 @@ public: } bool fix_length_and_dec() { - fix_length_and_dec_generic(args[0]->time_precision()); + fix_length_and_dec_generic(args[0]->time_precision(current_thd)); return FALSE; } longlong int_op(); @@ -643,7 +645,7 @@ public: { decimals= dec; } bool fix_fields(THD *, Item **); bool fix_length_and_dec() { fix_attributes_time(decimals); return FALSE; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); /* Abstract method that defines which time zone is used for conversion. Converts time current time in my_time_t representation to broken-down @@ -688,7 +690,7 @@ class Item_func_curdate :public Item_datefunc MYSQL_TIME ltime; public: Item_func_curdate(THD *thd): Item_datefunc(thd), last_query_id(0) {} - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; bool check_vcol_func_processor(void *arg) { @@ -731,7 +733,7 @@ public: bool fix_fields(THD *, Item **); bool fix_length_and_dec() { fix_attributes_datetime(decimals); return FALSE;} - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; bool check_vcol_func_processor(void *arg) { @@ -786,7 +788,7 @@ public: bool const_item() const { return 0; } const char *func_name() const { return "sysdate"; } void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); table_map used_tables() const { return RAND_TABLE_BIT; } bool check_vcol_func_processor(void *arg) { @@ -806,7 +808,7 @@ class Item_func_from_days :public Item_datefunc public: Item_func_from_days(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "from_days"; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} bool check_valid_arguments_processor(void *int_arg) @@ -871,7 +873,7 @@ class Item_func_from_unixtime :public Item_datetimefunc Item_func_from_unixtime(THD *thd, Item *a): Item_datetimefunc(thd, a) {} const char *func_name() const { return "from_unixtime"; } bool fix_length_and_dec(); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_from_unixtime>(thd, this); } }; @@ -912,11 +914,11 @@ class Item_func_convert_tz :public Item_datetimefunc const char *func_name() const { return "convert_tz"; } bool fix_length_and_dec() { - fix_attributes_datetime(args[0]->datetime_precision()); + fix_attributes_datetime(args[0]->datetime_precision(current_thd)); maybe_null= true; return FALSE; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); void cleanup(); Item *get_copy(THD *thd) { return get_item_copy<Item_func_convert_tz>(thd, this); } @@ -929,7 +931,7 @@ class Item_func_sec_to_time :public Item_timefunc { return args[0]->check_type_can_return_decimal(func_name()); } public: Item_func_sec_to_time(THD *thd, Item *item): Item_timefunc(thd, item) {} - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); bool fix_length_and_dec() { fix_attributes_time(args[0]->decimals); @@ -962,9 +964,17 @@ public: }; -class Item_extract :public Item_int_func +class Item_extract :public Item_int_func, + public Type_handler_hybrid_field_type { - bool date_value; + date_mode_t m_date_mode; + const Type_handler_int_result *handler_by_length(uint32 length, + uint32 threashold) + { + if (length >= threashold) + return &type_handler_longlong; + return &type_handler_long; + } void set_date_length(uint32 length) { /* @@ -973,48 +983,34 @@ class Item_extract :public Item_int_func because all around the code we assume that max_length is sign inclusive. Another options is to set unsigned_flag to "true". */ - max_length= length; //QQ: see above - date_value= true; + set_handler(handler_by_length(max_length= length, 10)); // QQ: see above + m_date_mode= date_mode_t(0); + } + void set_day_length(uint32 length) + { + /* + Units starting with DAY can be negative: + EXTRACT(DAY FROM '-24:00:00') -> -1 + */ + set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); + m_date_mode= date_mode_t(0); } void set_time_length(uint32 length) { - max_length= length + 1/*sign*/; - date_value= false; + set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); + m_date_mode= TIME_TIME_ONLY; } public: const interval_type int_type; // keep it public Item_extract(THD *thd, interval_type type_arg, Item *a): - Item_int_func(thd, a), int_type(type_arg) {} + Item_int_func(thd, a), + Type_handler_hybrid_field_type(&type_handler_longlong), + m_date_mode(date_mode_t(0)), + int_type(type_arg) + { } const Type_handler *type_handler() const { - switch (int_type) { - case INTERVAL_YEAR: - case INTERVAL_YEAR_MONTH: - case INTERVAL_QUARTER: - case INTERVAL_MONTH: - case INTERVAL_WEEK: - case INTERVAL_DAY: - case INTERVAL_DAY_HOUR: - case INTERVAL_DAY_MINUTE: - case INTERVAL_DAY_SECOND: - case INTERVAL_HOUR: - case INTERVAL_HOUR_MINUTE: - case INTERVAL_HOUR_SECOND: - case INTERVAL_MINUTE: - case INTERVAL_MINUTE_SECOND: - case INTERVAL_SECOND: - case INTERVAL_MICROSECOND: - case INTERVAL_SECOND_MICROSECOND: - return &type_handler_long; - case INTERVAL_DAY_MICROSECOND: - case INTERVAL_HOUR_MICROSECOND: - case INTERVAL_MINUTE_MICROSECOND: - return &type_handler_longlong; - case INTERVAL_LAST: - break; - } - DBUG_ASSERT(0); - return &type_handler_longlong; + return Type_handler_hybrid_field_type::type_handler(); } longlong val_int(); enum Functype functype() const { return EXTRACT_FUNC; } @@ -1119,7 +1115,7 @@ public: { print_cast_temporal(str, query_type); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec() { return args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this); @@ -1139,7 +1135,7 @@ public: { print_cast_temporal(str, query_type); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec() { return args[0]->type_handler()-> @@ -1160,7 +1156,7 @@ public: { print_cast_temporal(str, query_type); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool fix_length_and_dec() { return args[0]->type_handler()-> @@ -1179,7 +1175,7 @@ public: Item_func_makedate(THD *thd, Item *a, Item *b): Item_datefunc(thd, a, b) {} const char *func_name() const { return "makedate"; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_makedate>(thd, this); } }; @@ -1199,19 +1195,23 @@ public: const char *func_name() const { return "timestamp"; } bool fix_length_and_dec() { - uint dec= MY_MAX(args[0]->datetime_precision(), args[1]->time_precision()); - fix_attributes_datetime(dec); + THD *thd= current_thd; + uint dec0= args[0]->datetime_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, args[1]); + fix_attributes_datetime(MY_MAX(dec0, dec1)); maybe_null= true; return false; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Datetime dt(current_thd, args[0], 0); - MYSQL_TIME ltime2; - return (null_value= (!dt.is_valid_datetime() || - args[1]->get_time(<ime2) || - Sec6_add(dt.get_mysql_time(), <ime2, 1). - to_datetime(ltime))); + Datetime dt(thd, args[0], date_mode_t(0)); + if (!dt.is_valid_datetime()) + return null_value= true; + Interval_DDhhmmssff it(thd, args[1]); + if (!it.is_valid_interval_DDhhmmssff()) + return null_value= true; + return (null_value= Sec6_add(dt.get_mysql_time(), it.get_mysql_time(), 1). + to_datetime(ltime)); } Item *get_copy(THD *thd) { return get_item_copy<Item_func_timestamp>(thd, this); } @@ -1251,12 +1251,14 @@ public: const char *func_name() const { return "timediff"; } bool fix_length_and_dec() { - uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision()); + THD *thd= current_thd; + uint dec= MY_MAX(args[0]->time_precision(thd), + args[1]->time_precision(thd)); fix_attributes_time(dec); maybe_null= true; return FALSE; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_timediff>(thd, this); } }; @@ -1279,7 +1281,7 @@ public: return FALSE; } const char *func_name() const { return "maketime"; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_maketime>(thd, this); } }; @@ -1369,7 +1371,8 @@ public: Item_handled_func(thd, a, b), const_item(false), internal_charset(NULL) {} - bool get_date_common(MYSQL_TIME *ltime, ulonglong fuzzy_date, timestamp_type); + bool get_date_common(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate, + timestamp_type); const char *func_name() const { return "str_to_date"; } bool fix_length_and_dec(); Item *get_copy(THD *thd) @@ -1384,7 +1387,7 @@ class Item_func_last_day :public Item_datefunc public: Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "last_day"; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_last_day>(thd, this); } }; @@ -1413,14 +1416,14 @@ protected: { return static_cast<const Item_date_add_interval*>(item)->date_sub_interval; } - bool add(Item *item, interval_type type, bool sub, MYSQL_TIME *to) const + bool add(THD *thd, Item *item, interval_type type, bool sub, MYSQL_TIME *to) const { INTERVAL interval; - if (get_interval_value(item, type, &interval)) + if (get_interval_value(thd, item, type, &interval)) return true; if (sub) interval.neg = !interval.neg; - return date_add_interval(to, type, interval); + return date_add_interval(thd, to, type, interval); } }; @@ -1432,19 +1435,20 @@ class Func_handler_date_add_interval_datetime: public: bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->datetime_precision(), + uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd), interval_dec(item->arguments()[1], int_type(item))); item->fix_attributes_datetime(dec); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { - Datetime dt(current_thd, item->arguments()[0], 0); + Datetime dt(thd, item->arguments()[0], date_mode_t(0)); if (!dt.is_valid_datetime() || - dt.check_date_with_warn(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + dt.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) return (item->null_value= true); dt.copy_to_mysql_time(to); - return (item->null_value= add(item->arguments()[1], + return (item->null_value= add(thd, item->arguments()[1], int_type(item), sub(item), to)); } }; @@ -1454,7 +1458,8 @@ class Func_handler_date_add_interval_datetime_arg0_time: public Func_handler_date_add_interval_datetime { public: - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const; + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const; }; @@ -1463,14 +1468,15 @@ class Func_handler_date_add_interval_date: public Func_handler_date_add_interval { public: - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { - Date d(current_thd, item->arguments()[0], 0); + Date d(thd, item->arguments()[0], date_mode_t(0)); if (!d.is_valid_date() || - d.check_date_with_warn(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + d.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) return (item->null_value= true); d.copy_to_mysql_time(to); - return (item->null_value= add(item->arguments()[1], + return (item->null_value= add(thd, item->arguments()[1], int_type(item), sub(item), to)); } }; @@ -1483,18 +1489,19 @@ class Func_handler_date_add_interval_time: public: bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->time_precision(), + uint dec= MY_MAX(item->arguments()[0]->time_precision(current_thd), interval_dec(item->arguments()[1], int_type(item))); item->fix_attributes_time(dec); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { - Time t(item->arguments()[0]); + Time t(thd, item->arguments()[0]); if (!t.is_valid_time()) return (item->null_value= true); t.copy_to_mysql_time(to); - return (item->null_value= add(item->arguments()[1], + return (item->null_value= add(thd, item->arguments()[1], int_type(item), sub(item), to)); } }; @@ -1507,21 +1514,22 @@ class Func_handler_date_add_interval_string: public: bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->datetime_precision(), + uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd), interval_dec(item->arguments()[1], int_type(item))); item->collation.set(item->default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { - if (item->arguments()[0]->get_date(to, 0) || + if (item->arguments()[0]->get_date(thd, to, date_mode_t(0)) || (to->time_type != MYSQL_TIMESTAMP_TIME && - check_date_with_warn(to, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE, + check_date_with_warn(thd, to, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE, MYSQL_TIMESTAMP_ERROR))) return (item->null_value= true); - return (item->null_value= add(item->arguments()[1], + return (item->null_value= add(thd, item->arguments()[1], int_type(item), sub(item), to)); } }; @@ -1545,19 +1553,24 @@ public: { } bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->datetime_precision(), - item->arguments()[1]->time_precision()); - item->fix_attributes_datetime(dec); + THD *thd= current_thd; + uint dec0= item->arguments()[0]->datetime_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]); + item->fix_attributes_datetime(MY_MAX(dec0, dec1)); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { DBUG_ASSERT(item->is_fixed()); - MYSQL_TIME l_time2; - Datetime dt(current_thd, item->arguments()[0], 0); - return (item->null_value= (!dt.is_valid_datetime() || - item->arguments()[1]->get_time(&l_time2) || - Sec6_add(dt.get_mysql_time(), &l_time2, m_sign). + Datetime dt(thd, item->arguments()[0], date_mode_t(0)); + if (!dt.is_valid_datetime()) + return item->null_value= true; + Interval_DDhhmmssff it(thd, item->arguments()[1]); + if (!it.is_valid_interval_DDhhmmssff()) + return item->null_value= true; + return (item->null_value= (Sec6_add(dt.get_mysql_time(), + it.get_mysql_time(), m_sign). to_datetime(to))); } }; @@ -1573,20 +1586,25 @@ public: { } bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->time_precision(), - item->arguments()[1]->time_precision()); - item->fix_attributes_time(dec); + THD *thd= current_thd; + uint dec0= item->arguments()[0]->time_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]); + item->fix_attributes_time(MY_MAX(dec0, dec1)); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { DBUG_ASSERT(item->is_fixed()); - MYSQL_TIME l_time2; - Time t(item->arguments()[0]); - return (item->null_value= (!t.is_valid_time() || - item->arguments()[1]->get_time(&l_time2) || - Sec6_add(t.get_mysql_time(), &l_time2, m_sign). - to_time(to, item->decimals))); + Time t(thd, item->arguments()[0]); + if (!t.is_valid_time()) + return item->null_value= true; + Interval_DDhhmmssff i(thd, item->arguments()[1]); + if (!i.is_valid_interval_DDhhmmssff()) + return item->null_value= true; + return (item->null_value= (Sec6_add(t.get_mysql_time(), + i.get_mysql_time(), m_sign). + to_time(thd, to, item->decimals))); } }; @@ -1601,24 +1619,29 @@ public: { } bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->decimals, - item->arguments()[1]->decimals); + uint dec0= item->arguments()[0]->decimals; + uint dec1= Interval_DDhhmmssff::fsp(current_thd, item->arguments()[1]); + uint dec= MY_MAX(dec0, dec1); item->collation.set(item->default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { DBUG_ASSERT(item->is_fixed()); // Detect a proper timestamp type based on the argument values - MYSQL_TIME l_time1, l_time2; - if (item->arguments()[0]->get_time(&l_time1) || - item->arguments()[1]->get_time(&l_time2)) + Temporal_hybrid l_time1(thd, item->arguments()[0], TIME_TIME_ONLY); + if (!l_time1.is_valid_temporal()) + return (item->null_value= true); + Interval_DDhhmmssff l_time2(thd, item->arguments()[1]); + if (!l_time2.is_valid_interval_DDhhmmssff()) return (item->null_value= true); - Sec6_add add(&l_time1, &l_time2, m_sign); - return (item->null_value= (l_time1.time_type == MYSQL_TIMESTAMP_TIME ? - add.to_time(to, item->decimals) : + Sec6_add add(l_time1.get_mysql_time(), l_time2.get_mysql_time(), m_sign); + return (item->null_value= (l_time1.get_mysql_time()->time_type == + MYSQL_TIMESTAMP_TIME ? + add.to_time(thd, to, item->decimals) : add.to_datetime(to))); } }; @@ -1633,10 +1656,11 @@ public: item->fix_attributes_datetime(0); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { return static_cast<Item_func_str_to_date*>(item)-> - get_date_common(to, fuzzy, MYSQL_TIMESTAMP_DATETIME); + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATETIME); } }; @@ -1650,10 +1674,11 @@ public: item->fix_attributes_datetime(TIME_SECOND_PART_DIGITS); return false; } - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { return static_cast<Item_func_str_to_date*>(item)-> - get_date_common(to, fuzzy, MYSQL_TIMESTAMP_DATETIME); + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATETIME); } }; @@ -1661,10 +1686,11 @@ public: class Func_handler_str_to_date_date: public Item_handled_func::Handler_date { public: - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { return static_cast<Item_func_str_to_date*>(item)-> - get_date_common(to, fuzzy, MYSQL_TIMESTAMP_DATE); + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATE); } }; @@ -1672,10 +1698,11 @@ public: class Func_handler_str_to_date_time: public Item_handled_func::Handler_time { public: - bool get_date(Item_handled_func *item, MYSQL_TIME *to, ulonglong fuzzy) const + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzy) const { if (static_cast<Item_func_str_to_date*>(item)-> - get_date_common(to, fuzzy, MYSQL_TIMESTAMP_TIME)) + get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_TIME)) return true; if (to->day) { diff --git a/sql/item_vers.cc b/sql/item_vers.cc index d7361f687f9..6946ae0e1e5 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -37,9 +37,8 @@ Item_func_trt_ts::Item_func_trt_ts(THD *thd, Item* a, TR_table::field_id_t _trt_ bool -Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +Item_func_trt_ts::get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate) { - THD *thd= current_thd; // can it differ from constructor's? DBUG_ASSERT(thd); DBUG_ASSERT(args[0]); if (args[0]->result_type() != INT_RESULT) @@ -67,7 +66,7 @@ Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) return true; } - return trt[trt_field]->get_date(res, fuzzy_date); + return trt[trt_field]->get_date(res, fuzzydate); } @@ -143,7 +142,7 @@ Item_func_trt_id::val_int() else { MYSQL_TIME commit_ts; - if (args[0]->get_date(&commit_ts, 0)) + if (args[0]->get_date(current_thd, &commit_ts, date_mode_t(0))) { null_value= true; return 0; diff --git a/sql/item_vers.h b/sql/item_vers.h index 8b9c0e6056c..a42b5a033f2 100644 --- a/sql/item_vers.h +++ b/sql/item_vers.h @@ -35,7 +35,7 @@ public: } return "trt_commit_ts"; } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy<Item_func_trt_ts>(thd, this); } bool fix_length_and_dec() diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc index a17f9482ea0..aa2c7756ab7 100644 --- a/sql/item_windowfunc.cc +++ b/sql/item_windowfunc.cc @@ -439,12 +439,12 @@ Item_sum_hybrid_simple::val_str(String *str) return retval; } -bool Item_sum_hybrid_simple::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Item_sum_hybrid_simple::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); if (null_value) return true; - bool retval= value->get_date(ltime, fuzzydate); + bool retval= value->get_date(thd, ltime, fuzzydate); if ((null_value= value->null_value)) DBUG_ASSERT(retval == true); return retval; @@ -542,6 +542,11 @@ void Item_sum_hybrid_simple::update_field() void Item_window_func::print(String *str, enum_query_type query_type) { + if (only_single_element_order_list()) + { + print_for_percentile_functions(str, query_type); + return; + } window_func()->print(str, query_type); str->append(" over "); #ifndef DBUG_OFF @@ -551,3 +556,15 @@ void Item_window_func::print(String *str, enum_query_type query_type) #endif window_spec->print(str, query_type); } +void Item_window_func::print_for_percentile_functions(String *str, enum_query_type query_type) +{ + window_func()->print(str, query_type); + str->append(" within group "); + str->append('('); + window_spec->print_order(str,query_type); + str->append(')'); + str->append(" over "); + str->append('('); + window_spec->print_partition(str,query_type); + str->append(')'); +} diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h index 48851da7d96..4c704808fe4 100644 --- a/sql/item_windowfunc.h +++ b/sql/item_windowfunc.h @@ -319,7 +319,7 @@ class Item_sum_hybrid_simple : public Item_sum, my_decimal *val_decimal(my_decimal *); void reset_field(); String *val_str(String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); const Type_handler *type_handler() const { return Type_handler_hybrid_field_type::type_handler(); } void update_field(); @@ -1085,6 +1085,8 @@ public: case Item_sum::DENSE_RANK_FUNC: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: + case Item_sum::LAG_FUNC: + case Item_sum::LEAD_FUNC: case Item_sum::PERCENTILE_CONT_FUNC: case Item_sum::PERCENTILE_DISC_FUNC: return true; @@ -1153,6 +1155,7 @@ private: */ bool force_return_blank; bool read_value_from_result_field; + void print_for_percentile_functions(String *str, enum_query_type query_type); public: void set_phase_to_initial() @@ -1270,7 +1273,7 @@ public: return res; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { bool res; if (force_return_blank) @@ -1287,7 +1290,7 @@ public: } else { - res= window_func()->get_date(ltime, fuzzydate); + res= window_func()->get_date(thd, ltime, fuzzydate); null_value= window_func()->null_value; } return res; diff --git a/sql/key.cc b/sql/key.cc index 34196a973c5..9ad1103fbe8 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -146,7 +146,8 @@ void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info, { key_length-= HA_KEY_BLOB_LENGTH; length= MY_MIN(key_length, key_part->length); - uint bytes= key_part->field->get_key_image(to_key, length, Field::itRAW); + uint bytes= key_part->field->get_key_image(to_key, length, + key_info->flags & HA_SPATIAL ? Field::itMBR : Field::itRAW); if (with_zerofill && bytes < length) bzero((char*) to_key + bytes, length - bytes); to_key+= HA_KEY_BLOB_LENGTH; diff --git a/sql/lock.cc b/sql/lock.cc index 17629f17291..5420e9f42b5 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -596,22 +596,6 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) } -/** Abort all other threads waiting to get lock in table. */ - -void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock) -{ - MYSQL_LOCK *locked; - DBUG_ENTER("mysql_lock_abort"); - - if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK | GET_LOCK_ON_THD))) - { - for (uint i=0; i < locked->lock_count; i++) - thr_abort_locks(locked->locks[i]->lock, upgrade_lock); - } - DBUG_VOID_RETURN; -} - - /** Abort one thread / table combination. diff --git a/sql/lock.h b/sql/lock.h index 35cb3043d57..e9324c80d89 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -33,7 +33,6 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag); void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); -void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); /* Lock based on name */ diff --git a/sql/log.cc b/sql/log.cc index 6975b58dfb7..a56117a4ac1 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1162,6 +1162,10 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format, { bool error= FALSE; Log_event_handler **current_handler; + THD *thd= current_thd; + + if (likely(thd)) + thd->error_printed_to_log= 1; /* currently we don't need locking here as there is no error_log table */ for (current_handler= error_log_handler_list ; *current_handler ;) @@ -1703,14 +1707,14 @@ static int binlog_close_connection(handlerton *hton, THD *thd) uchar *buf; size_t len=0; wsrep_write_cache_buf(cache, &buf, &len); - WSREP_WARN("binlog trx cache not empty (%lu bytes) @ connection close %lld", - (ulong) len, (longlong) thd->thread_id); + WSREP_WARN("binlog trx cache not empty (%zu bytes) @ connection close %lld", + len, (longlong) thd->thread_id); if (len > 0) wsrep_dump_rbr_buf(thd, buf, len); cache = cache_mngr->get_binlog_cache_log(false); wsrep_write_cache_buf(cache, &buf, &len); - WSREP_WARN("binlog stmt cache not empty (%lu bytes) @ connection close %lld", - (ulong) len, (longlong) thd->thread_id); + WSREP_WARN("binlog stmt cache not empty (%zu bytes) @ connection close %lld", + len, (longlong) thd->thread_id); if (len > 0) wsrep_dump_rbr_buf(thd, buf, len); } #endif /* WITH_WSREP */ @@ -2746,14 +2750,14 @@ void MYSQL_LOG::close(uint exiting) if (log_type == LOG_BIN && mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error) { write_error= 1; - sql_print_error(ER_THD_OR_DEFAULT(current_thd, ER_ERROR_ON_WRITE), name, errno); + sql_print_error(ER_DEFAULT(ER_ERROR_ON_WRITE), name, errno); } if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) && mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) { write_error= 1; - sql_print_error(ER_THD_OR_DEFAULT(current_thd, ER_ERROR_ON_WRITE), name, errno); + sql_print_error(ER_DEFAULT(ER_ERROR_ON_WRITE), name, errno); } } @@ -2937,7 +2941,7 @@ err: if (!write_error) { write_error= 1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + sql_print_error(ER_DEFAULT(ER_ERROR_ON_WRITE), name, errno); } mysql_mutex_unlock(&LOCK_log); return TRUE; @@ -8474,8 +8478,7 @@ void MYSQL_BIN_LOG::close(uint exiting) ! write_error) { write_error= 1; - sql_print_error(ER_THD_OR_DEFAULT(current_thd, ER_ERROR_ON_WRITE), - index_file_name, errno); + sql_print_error(ER_DEFAULT(ER_ERROR_ON_WRITE), index_file_name, errno); } } log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; diff --git a/sql/log_event.cc b/sql/log_event.cc index 13690a8be60..651fb4ce5b1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -264,6 +264,27 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error, } #endif +#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) +static void set_thd_db(THD *thd, Rpl_filter *rpl_filter, + const char *db, uint32 db_len) +{ + char lcase_db_buf[NAME_LEN +1]; + LEX_CSTRING new_db; + new_db.length= db_len; + if (lower_case_table_names == 1) + { + strmov(lcase_db_buf, db); + my_casedn_str(system_charset_info, lcase_db_buf); + new_db.str= lcase_db_buf; + } + else + new_db.str= db; + /* TODO WARNING this makes rewrite_db respect lower_case_table_names values + * for more info look MDEV-17446 */ + new_db.str= rpl_filter->get_rewrite_db(new_db.str, &new_db.length); + thd->set_db(&new_db); +} +#endif /* Cache that will automatically be written to a dedicated file on destruction. @@ -5374,7 +5395,6 @@ bool test_if_equal_repl_errors(int expected_error, int actual_error) int Query_log_event::do_apply_event(rpl_group_info *rgi, const char *query_arg, uint32 q_len_arg) { - LEX_CSTRING new_db; int expected_error,actual_error= 0; Schema_specification_st db_options; uint64 sub_id= 0; @@ -5406,9 +5426,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, goto end; } - new_db.length= db_len; - new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length); - thd->set_db(&new_db); /* allocates a copy of 'db' */ + set_thd_db(thd, rpl_filter, db, db_len); /* Setting the character set and collation of the current database thd->db. @@ -5563,7 +5581,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, gtid= rgi->current_gtid; if (unlikely(rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, - true, false, + rgi, false, &hton))) { int errcode= thd->get_stmt_da()->sql_errno(); @@ -7238,15 +7256,12 @@ void Load_log_event::set_fields(const char* affected_db, int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, bool use_rli_only_for_errors) { - LEX_CSTRING new_db; Relay_log_info const *rli= rgi->rli; Rpl_filter *rpl_filter= rli->mi->rpl_filter; DBUG_ENTER("Load_log_event::do_apply_event"); - new_db.length= db_len; - new_db.str= rpl_filter->get_rewrite_db(db, &new_db.length); - thd->set_db(&new_db); DBUG_ASSERT(thd->query() == 0); + set_thd_db(thd, rpl_filter, db, db_len); thd->clear_error(1); /* see Query_log_event::do_apply_event() and BUG#13360 */ @@ -7290,6 +7305,8 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, TABLE_LIST tables; LEX_CSTRING db_name= { thd->strmake(thd->db.str, thd->db.length), thd->db.length }; + if (lower_case_table_names) + my_casedn_str(system_charset_info, (char *)table_name); LEX_CSTRING tbl_name= { table_name, strlen(table_name) }; tables.init_one_table(&db_name, &tbl_name, 0, TL_WRITE); tables.updating= 1; @@ -8361,7 +8378,7 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) { if ((ret= rpl_global_gtid_slave_state->record_gtid(thd, &list[i], sub_id_list[i], - false, false, &hton))) + NULL, false, &hton))) return ret; rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i], hton, NULL); @@ -8898,7 +8915,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) rgi->gtid_pending= false; gtid= rgi->current_gtid; - err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true, + err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, rgi, false, &hton); if (unlikely(err)) { @@ -9168,11 +9185,6 @@ User_var_log_event(const char* buf, uint event_len, we keep the flags set to UNDEF_F. */ size_t bytes_read= (val + val_len) - buf_start; - if (bytes_read > size_t(event_len)) - { - error= true; - goto err; - } if ((data_written - bytes_read) > 0) { flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + @@ -12578,7 +12590,7 @@ check_table_map(rpl_group_info *rgi, RPL_TABLE_LIST *table_list) int Table_map_log_event::do_apply_event(rpl_group_info *rgi) { RPL_TABLE_LIST *table_list; - char *db_mem, *tname_mem; + char *db_mem, *tname_mem, *ptr; size_t dummy_len, db_mem_length, tname_mem_length; void *memory; Rpl_filter *filter; @@ -12595,10 +12607,20 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); + db_mem_length= strmov(db_mem, m_dbnam) - db_mem; + tname_mem_length= strmov(tname_mem, m_tblnam) - tname_mem; + if (lower_case_table_names) + { + my_casedn_str(files_charset_info, (char*)tname_mem); + my_casedn_str(files_charset_info, (char*)db_mem); + } + /* call from mysql_client_binlog_statement() will not set rli->mi */ filter= rgi->thd->slave_thread ? rli->mi->rpl_filter : global_rpl_filter; - db_mem_length= strmov(db_mem, filter->get_rewrite_db(m_dbnam, &dummy_len))- db_mem; - tname_mem_length= strmov(tname_mem, m_tblnam)- tname_mem; + + /* rewrite rules changed the database */ + if (((ptr= (char*) filter->get_rewrite_db(db_mem, &dummy_len)) != db_mem)) + db_mem_length= strmov(db_mem, ptr) - db_mem; LEX_CSTRING tmp_db_name= {db_mem, db_mem_length }; LEX_CSTRING tmp_tbl_name= {tname_mem, tname_mem_length }; diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index cf587ef4acd..d6952e71899 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -18,6 +18,7 @@ #include <my_bit.h> #include "sql_select.h" #include "key.h" +#include "sql_statistics.h" /**************************************************************************** * Default MRR implementation (MRR to non-MRR converter) @@ -64,7 +65,12 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, ha_rows rows, total_rows= 0; uint n_ranges=0; THD *thd= table->in_use; + uint limit= thd->variables.eq_range_index_dive_limit; + bool use_statistics_for_eq_range= eq_ranges_exceeds_limit(seq, + seq_init_param, + limit); + /* Default MRR implementation doesn't need buffer */ *bufsz= 0; @@ -88,8 +94,15 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, min_endp= range.start_key.length? &range.start_key : NULL; max_endp= range.end_key.length? &range.end_key : NULL; } + int keyparts_used= my_count_bits(range.start_key.keypart_map); if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE)) rows= 1; /* there can be at most one row */ + else if (use_statistics_for_eq_range && + !(range.range_flag & NULL_RANGE) && + (range.range_flag & EQ_RANGE) && + table->key_info[keyno].actual_rec_per_key(keyparts_used - 1) > 0.5) + rows= + (ha_rows) table->key_info[keyno].actual_rec_per_key(keyparts_used - 1); else { if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp, diff --git a/sql/my_decimal.h b/sql/my_decimal.h index d035641cfa6..b22c686cc90 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -29,6 +29,8 @@ #ifndef my_decimal_h #define my_decimal_h +#include "sql_basic_types.h" + #if defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) #include "sql_string.h" /* String */ #endif @@ -215,8 +217,6 @@ public: { return check_result(mask, decimal_round(this, to, (int) scale, mode)); } - bool to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const char *field_name); int to_binary(uchar *bin, int prec, int scale, uint mask= E_DEC_FATAL_ERROR) const; #endif diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index ce40b5edc2c..fc8bce08276 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -26,6 +26,8 @@ #include <shellapi.h> #include <accctrl.h> #include <aclapi.h> +struct IUnknown; +#include <shlwapi.h> #define USAGETEXT \ "mysql_install_db.exe Ver 1.00 for Windows\n" \ @@ -93,9 +95,7 @@ static struct my_option my_long_options[]= static my_bool -get_one_option(int optid, - const struct my_option *opt __attribute__ ((unused)), - char *argument __attribute__ ((unused))) +get_one_option(int optid, const struct my_option *, char *) { DBUG_ENTER("get_one_option"); switch (optid) { @@ -109,7 +109,7 @@ get_one_option(int optid, } -static void die(const char *fmt, ...) +ATTRIBUTE_NORETURN static void die(const char *fmt, ...) { va_list args; DBUG_ENTER("die"); @@ -290,7 +290,7 @@ static int create_myini() FILE *myini= fopen("my.ini","wt"); if (!myini) { - die("Cannot create my.ini in data directory"); + die("Can't create my.ini in data directory"); } /* Write out server settings. */ @@ -308,7 +308,7 @@ static int create_myini() if (enable_named_pipe) { - fprintf(myini,"enable-named-pipe\n"); + fprintf(myini,"named-pipe=ON\n"); } if (opt_socket && opt_socket[0]) @@ -523,6 +523,7 @@ static int set_directory_permissions(const char *dir, const char *os_user) } + /* Create database instance (including registering as service etc) .*/ static int create_db_instance() @@ -532,19 +533,78 @@ static int create_db_instance() DWORD cwd_len= MAX_PATH; char cmdline[3*MAX_PATH]; FILE *in; + bool cleanup_datadir= true; + DWORD last_error; verbose("Running bootstrap"); GetCurrentDirectory(cwd_len, cwd); - CreateDirectory(opt_datadir, NULL); /*ignore error, it might already exist */ + + /* Create datadir and datadir/mysql, if they do not already exist. */ + + if (!CreateDirectory(opt_datadir, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS)) + { + last_error = GetLastError(); + switch(last_error) + { + case ERROR_ACCESS_DENIED: + die("Can't create data directory '%s' (access denied)\n", + opt_datadir); + break; + case ERROR_PATH_NOT_FOUND: + die("Can't create data directory '%s' " + "(one or more intermediate directories do not exist)\n", + opt_datadir); + break; + default: + die("Can't create data directory '%s', last error %u\n", + opt_datadir, last_error); + break; + } + } if (!SetCurrentDirectory(opt_datadir)) { - die("Cannot set current directory to '%s'\n",opt_datadir); - return -1; + last_error = GetLastError(); + switch (last_error) + { + case ERROR_DIRECTORY: + die("Can't set current directory to '%s', the path is not a valid directory \n", + opt_datadir); + break; + default: + die("Can' set current directory to '%s', last error %u\n", + opt_datadir, last_error); + break; + } } - CreateDirectory("mysql",NULL); + if (PathIsDirectoryEmpty(opt_datadir)) + { + cleanup_datadir= false; + } + + if (!CreateDirectory("mysql",NULL)) + { + last_error = GetLastError(); + DWORD attributes; + switch(last_error) + { + case ERROR_ACCESS_DENIED: + die("Can't create subdirectory 'mysql' in '%s' (access denied)\n",opt_datadir); + break; + case ERROR_ALREADY_EXISTS: + attributes = GetFileAttributes("mysql"); + + if (attributes == INVALID_FILE_ATTRIBUTES) + die("GetFileAttributes() failed for existing file '%s\\mysql', last error %u", + opt_datadir, GetLastError()); + else if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) + die("File '%s\\mysql' exists, but it is not a directory", opt_datadir); + + break; + } + } /* Set data directory permissions for both current user and @@ -565,11 +625,11 @@ static int create_db_instance() if (setvbuf(in, NULL, _IONBF, 0)) { - verbose("WARNING: Cannot disable buffering on mysqld's stdin"); + verbose("WARNING: Can't disable buffering on mysqld's stdin"); } if (fwrite("use mysql;\n",11,1, in) != 1) { - verbose("ERROR: Cannot write to mysqld's stdin"); + verbose("ERROR: Can't write to mysqld's stdin"); ret= 1; goto end; } @@ -580,7 +640,7 @@ static int create_db_instance() /* Write the bootstrap script to stdin. */ if (fwrite(mysql_bootstrap_sql[i], strlen(mysql_bootstrap_sql[i]), 1, in) != 1) { - verbose("ERROR: Cannot write to mysqld's stdin"); + verbose("ERROR: Can't write to mysqld's stdin"); ret= 1; goto end; } @@ -649,7 +709,7 @@ static int create_db_instance() } end: - if (ret) + if (ret && cleanup_datadir) { SetCurrentDirectory(cwd); clean_directory(opt_datadir); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ce14dc678c6..9ff47dc1ff1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -117,6 +117,10 @@ #include <poll.h> #endif +#ifdef _WIN32 +#include <handle_connections_win.h> +#endif + #include <my_service_manager.h> #define mysqld_charset &my_charset_latin1 @@ -319,23 +323,6 @@ MY_TIMER_INFO sys_timer_info; /* static variables */ #ifdef HAVE_PSI_INTERFACE -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) -static PSI_thread_key key_thread_handle_con_namedpipes; -static PSI_cond_key key_COND_handler_count; -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY) -static PSI_thread_key key_thread_handle_con_sharedmem; -#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) -static PSI_thread_key key_thread_handle_con_sockets; -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#ifdef __WIN__ -static PSI_thread_key key_thread_handle_shutdown; -#endif /* __WIN__ */ - #ifdef HAVE_OPENSSL10 static PSI_rwlock_key key_rwlock_openssl; #endif @@ -371,6 +358,7 @@ static char *character_set_filesystem_name; static char *lc_messages; static char *lc_time_names_name; char *my_bind_addr_str; +int server_socket_ai_family; static char *default_collation_name; char *default_storage_engine, *default_tmp_storage_engine; char *enforced_storage_engine=NULL; @@ -484,7 +472,7 @@ my_bool opt_master_verify_checksum= 0; my_bool opt_slave_sql_verify_checksum= 1; const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS}; volatile sig_atomic_t calling_initgroups= 0; /**< Used in SIGSEGV handler. */ -uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; +uint mysqld_port, select_errors, dropping_tables, ha_open_options; uint mysqld_extra_port; uint mysqld_port_timeout; ulong delay_key_write_options; @@ -512,6 +500,7 @@ ulonglong max_binlog_cache_size=0; ulong slave_max_allowed_packet= 0; ulonglong binlog_stmt_cache_size=0; ulonglong max_binlog_stmt_cache_size=0; +ulonglong test_flags; ulonglong query_cache_size=0; ulong query_cache_limit=0; ulong executed_events=0; @@ -743,7 +732,6 @@ mysql_mutex_t LOCK_thread_count; other threads. It also protects these variables: - handler_count in_bootstrap select_thread_in_use slave_init_thread_running @@ -798,6 +786,7 @@ char *master_info_file; char *relay_log_info_file, *report_user, *report_password, *report_host; char *opt_relay_logname = 0, *opt_relaylog_index_name=0; char *opt_logname, *opt_slow_logname, *opt_bin_logname; +char *opt_binlog_index_name=0; /* Static variables */ @@ -807,7 +796,6 @@ my_bool opt_expect_abort= 0, opt_bootstrap= 0; static my_bool opt_myisam_log; static int cleanup_done; static ulong opt_specialflag; -static char *opt_binlog_index_name; char *mysql_home_ptr, *pidfile_name_ptr; /** Initial command line arguments (count), after load_defaults().*/ static int defaults_argc; @@ -1097,9 +1085,6 @@ PSI_cond_key key_COND_ack_receiver; static PSI_cond_info all_server_conds[]= { -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) - { &key_COND_handler_count, "COND_handler_count", PSI_FLAG_GLOBAL}, -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ #ifdef HAVE_MMAP { &key_PAGE_cond, "PAGE::cond", 0}, { &key_COND_active, "TC_LOG_MMAP::COND_active", 0}, @@ -1160,22 +1145,6 @@ PSI_thread_key key_thread_ack_receiver; static PSI_thread_info all_server_threads[]= { -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) - { &key_thread_handle_con_namedpipes, "con_named_pipes", PSI_FLAG_GLOBAL}, -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY) - { &key_thread_handle_con_sharedmem, "con_shared_mem", PSI_FLAG_GLOBAL}, -#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) - { &key_thread_handle_con_sockets, "con_sockets", PSI_FLAG_GLOBAL}, -#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */ - -#ifdef __WIN__ - { &key_thread_handle_shutdown, "shutdown", PSI_FLAG_GLOBAL}, -#endif /* __WIN__ */ - { &key_thread_bootstrap, "bootstrap", PSI_FLAG_GLOBAL}, { &key_thread_delayed_insert, "delayed_insert", 0}, { &key_thread_handle_manager, "manager", PSI_FLAG_GLOBAL}, @@ -1418,10 +1387,10 @@ void Buffered_logs::print() /** Logs reported before a logger is available. */ static Buffered_logs buffered_logs; -static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() #ifndef EMBEDDED_LIBRARY +MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; /** Error reporter that buffer log messages. @param level log message level @@ -1477,27 +1446,18 @@ static pthread_t select_thread; #undef getpid #include <process.h> -static mysql_cond_t COND_handler_count; -static uint handler_count; static bool start_mode=0, use_opt_args; static int opt_argc; static char **opt_argv; #if !defined(EMBEDDED_LIBRARY) -static HANDLE hEventShutdown; +HANDLE hEventShutdown; static char shutdown_event_name[40]; #include "nt_servc.h" static NTService Service; ///< Service object for WinNT #endif /* EMBEDDED_LIBRARY */ #endif /* __WIN__ */ -#ifdef _WIN32 -static char pipe_name[512]; -static SECURITY_ATTRIBUTES saPipeSecurity; -static SECURITY_DESCRIPTOR sdPipeDescriptor; -static HANDLE hPipe = INVALID_HANDLE_VALUE; -#endif - #ifndef EMBEDDED_LIBRARY bool mysqld_embedded=0; #else @@ -1518,11 +1478,7 @@ int deny_severity = LOG_WARNING; ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE; Query_cache query_cache; #endif -#ifdef HAVE_SMEM -const char *shared_memory_base_name= default_shared_memory_base_name; -my_bool opt_enable_shared_memory; -HANDLE smem_event_connect_request= 0; -#endif + my_bool opt_use_ssl = 0; char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL, @@ -1574,19 +1530,13 @@ extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *); static int init_thread_environment(); static char *get_relative_path(const char *path); static int fix_paths(void); +#ifndef _WIN32 void handle_connections_sockets(); -#ifdef _WIN32 -pthread_handler_t handle_connections_sockets_thread(void *arg); #endif + pthread_handler_t kill_server_thread(void *arg); static void bootstrap(MYSQL_FILE *file); static bool read_init_file(char *file_name); -#ifdef _WIN32 -pthread_handler_t handle_connections_namedpipes(void *arg); -#endif -#ifdef HAVE_SMEM -pthread_handler_t handle_connections_shared_memory(void *arg); -#endif pthread_handler_t handle_slave(void *arg); static void clean_up(bool print_message); static int test_if_case_insensitive(const char *dir_name); @@ -1621,6 +1571,7 @@ static void close_connections(void) kill_cached_threads++; flush_thread_cache(); + /* kill connection thread */ #if !defined(__WIN__) DBUG_PRINT("quit", ("waiting for select thread: %lu", @@ -1670,30 +1621,7 @@ static void close_connections(void) extra_ip_sock= MYSQL_INVALID_SOCKET; } } -#ifdef _WIN32 - if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) - { - HANDLE temp; - DBUG_PRINT("quit", ("Closing named pipes") ); - - /* Create connection to the handle named pipe handler to break the loop */ - if ((temp = CreateFile(pipe_name, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL )) != INVALID_HANDLE_VALUE) - { - WaitNamedPipe(pipe_name, 1000); - DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); - CancelIo(temp); - DisconnectNamedPipe(temp); - CloseHandle(temp); - } - } -#endif + #ifdef HAVE_SYS_UN_H if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET) { @@ -1806,7 +1734,14 @@ static void close_connections(void) (ulong) tmp->thread_id, (tmp->main_security_ctx.user ? tmp->main_security_ctx.user : "")); + /* + close_connection() might need a valid current_thd + for memory allocation tracking. + */ + THD* save_thd= current_thd; + set_current_thd(tmp); close_connection(tmp,ER_SERVER_SHUTDOWN); + set_current_thd(save_thd); } #endif @@ -1933,12 +1868,6 @@ void kill_mysql(THD *thd) { DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError())); } - /* - or: - HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown"); - SetEvent(hEventShutdown); - CloseHandle(hEvent); - */ } #endif #elif defined(HAVE_PTHREAD_KILL) @@ -1970,7 +1899,7 @@ void kill_mysql(THD *thd) /** Force server down. Kill all connections and threads and exit. - @param sig_ptr Signal number that caused kill_server to be called. + @param sig Signal number that caused kill_server to be called. @note A signal number of 0 mean that the function was not called @@ -1978,22 +1907,14 @@ void kill_mysql(THD *thd) or stop, we just want to kill the server. */ -#if !defined(__WIN__) -static void *kill_server(void *sig_ptr) -#define RETURN_FROM_KILL_SERVER return 0 -#else -static void __cdecl kill_server(int sig_ptr) -#define RETURN_FROM_KILL_SERVER return -#endif +static void kill_server(int sig) { DBUG_ENTER("kill_server"); #ifndef EMBEDDED_LIBRARY - int sig=(int) (long) sig_ptr; // This is passed a int // if there is a signal during the kill in progress, ignore the other if (kill_in_progress) // Safety { - DBUG_LEAVE; - RETURN_FROM_KILL_SERVER; + DBUG_VOID_RETURN; } kill_in_progress=TRUE; abort_loop=1; // This should be set @@ -2010,21 +1931,6 @@ static void __cdecl kill_server(int sig_ptr) else sql_print_error(ER_DEFAULT(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */ -#ifdef HAVE_SMEM - /* - Send event to smem_event_connect_request for aborting - */ - if (opt_enable_shared_memory) - { - if (!SetEvent(smem_event_connect_request)) - { - DBUG_PRINT("error", - ("Got error: %ld from SetEvent of smem_event_connect_request", - GetLastError())); - } - } -#endif - /* Stop wsrep threads in case they are running. */ if (wsrep_running_threads > 0) { @@ -2042,20 +1948,9 @@ static void __cdecl kill_server(int sig_ptr) else unireg_end(); - /* purecov: begin deadcode */ - DBUG_LEAVE; // Must match DBUG_ENTER() - my_thread_end(); - pthread_exit(0); - /* purecov: end */ - - RETURN_FROM_KILL_SERVER; // Avoid compiler warnings - -#else /* EMBEDDED_LIBRARY*/ - - DBUG_LEAVE; - RETURN_FROM_KILL_SERVER; +#endif /* EMBEDDED_LIBRARY*/ -#endif /* EMBEDDED_LIBRARY */ + DBUG_VOID_RETURN; } @@ -2064,11 +1959,9 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused))) { my_thread_init(); // Initialize new thread kill_server(0); - /* purecov: begin deadcode */ my_thread_end(); pthread_exit(0); return 0; - /* purecov: end */ } #endif @@ -2114,13 +2007,7 @@ static void clean_up_error_log_mutex() void unireg_end(void) { clean_up(1); - my_thread_end(); sd_notify(0, "STATUS=MariaDB server is down"); -#if defined(SIGNALS_DONT_BREAK_READ) - exit(0); -#else - pthread_exit(0); // Exit is in main thread -#endif } @@ -2615,6 +2502,7 @@ static MYSQL_SOCKET activate_tcp_port(uint port) } else { + server_socket_ai_family= a->ai_family; sql_print_information("Server socket created on IP: '%s'.", (const char *) ip_addr); break; @@ -2741,45 +2629,6 @@ static void network_init(void) extra_ip_sock= activate_tcp_port(mysqld_extra_port); } -#ifdef _WIN32 - /* create named pipe */ - if (mysqld_unix_port[0] && !opt_bootstrap && - opt_enable_named_pipe) - { - - strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\", - mysqld_unix_port, NullS); - bzero((char*) &saPipeSecurity, sizeof(saPipeSecurity)); - bzero((char*) &sdPipeDescriptor, sizeof(sdPipeDescriptor)); - if (!InitializeSecurityDescriptor(&sdPipeDescriptor, - SECURITY_DESCRIPTOR_REVISION)) - { - sql_perror("Can't start server : Initialize security descriptor"); - unireg_abort(1); - } - if (!SetSecurityDescriptorDacl(&sdPipeDescriptor, TRUE, NULL, FALSE)) - { - sql_perror("Can't start server : Set security descriptor"); - unireg_abort(1); - } - saPipeSecurity.nLength = sizeof(SECURITY_ATTRIBUTES); - saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor; - saPipeSecurity.bInheritHandle = FALSE; - if ((hPipe= CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables.net_buffer_length, - (int) global_system_variables.net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity)) == INVALID_HANDLE_VALUE) - { - sql_perror("Create named pipe failed"); - unireg_abort(1); - } - } -#endif - #if defined(HAVE_SYS_UN_H) /* ** Create the UNIX socket @@ -3005,10 +2854,6 @@ static bool cache_thread(THD *thd) _db_pop_(); #endif - /* Clear warnings. */ - if (!thd->get_stmt_da()->is_warning_info_empty()) - thd->get_stmt_da()->clear_warning_info(thd->query_id); - set_timespec(abstime, THREAD_CACHE_TIMEOUT); while (!abort_loop && ! wake_thread && ! kill_cached_threads) { @@ -3598,7 +3443,7 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) sql_print_error("Can't create thread to kill server (errno= %d)", error); #else - kill_server((void*) sig); // MIT THREAD has a alarm thread + kill_server(sig); // MIT THREAD has a alarm thread #endif } break; @@ -3714,23 +3559,6 @@ void *my_str_malloc_mysqld(size_t size) } -#ifdef __WIN__ - -pthread_handler_t handle_shutdown(void *arg) -{ - MSG msg; - my_thread_init(); - - /* this call should create the message queue for this thread */ - PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE); -#if !defined(EMBEDDED_LIBRARY) - if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0) -#endif /* EMBEDDED_LIBRARY */ - kill_server(MYSQL_KILL_SIGNAL); - return 0; -} -#endif - #include <mysqld_default_groups.h> #if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) @@ -4090,14 +3918,16 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific) { THD *thd= current_thd; - if (is_thread_specific) /* If thread specific memory */ - { - /* - When thread specfic is set, both mysqld_server_initialized and thd - must be set - */ - DBUG_ASSERT(mysqld_server_initialized && thd); + /* + When thread specific is set, both mysqld_server_initialized and thd + must be set, and we check that with DBUG_ASSERT. + However, do not crash, if current_thd is NULL, in release version. + */ + DBUG_ASSERT(!is_thread_specific || (mysqld_server_initialized && thd)); + + if (is_thread_specific && likely(thd)) /* If thread specific memory */ + { DBUG_PRINT("info", ("thd memory_used: %lld size: %lld", (longlong) thd->status_var.local_memory_used, size)); @@ -4489,6 +4319,11 @@ static int init_common_variables() /* MyISAM requires two file handles per table. */ wanted_files= (extra_files + max_connections + extra_max_connections + tc_size * 2); +#if defined(HAVE_POOL_OF_THREADS) && !defined(__WIN__) + // add epoll or kevent fd for each threadpool group, in case pool of threads is used + wanted_files+= (thread_handling > SCHEDULER_NO_THREADS) ? 0 : threadpool_size; +#endif + min_tc_size= MY_MIN(tc_size, TABLE_OPEN_CACHE_MIN); org_max_connections= max_connections; org_tc_size= tc_size; @@ -5628,100 +5463,17 @@ static int init_server_components() #ifndef EMBEDDED_LIBRARY - -static void create_shutdown_thread() +#ifdef _WIN32 +static void create_shutdown_event() { -#ifdef __WIN__ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); - pthread_t hThread; - int error; - if (unlikely((error= mysql_thread_create(key_thread_handle_shutdown, - &hThread, &connection_attrib, - handle_shutdown, 0)))) - sql_print_warning("Can't create thread to handle shutdown requests" - " (errno= %d)", error); - // On "Stop Service" we have to do regular shutdown Service.SetShutdownEvent(hEventShutdown); -#endif /* __WIN__ */ } - -#endif /* EMBEDDED_LIBRARY */ - -#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) -static void handle_connections_methods() -{ - pthread_t hThread; - int error; - DBUG_ENTER("handle_connections_methods"); - if (hPipe == INVALID_HANDLE_VALUE && - (!have_tcpip || opt_disable_networking) && - !opt_enable_shared_memory) - { - sql_print_error("TCP/IP, --shared-memory, or --named-pipe should be configured on NT OS"); - unireg_abort(1); // Will not return - } - - mysql_mutex_lock(&LOCK_start_thread); - mysql_cond_init(key_COND_handler_count, &COND_handler_count, NULL); - handler_count=0; - if (hPipe != INVALID_HANDLE_VALUE) - { - handler_count++; - if ((error= mysql_thread_create(key_thread_handle_con_namedpipes, - &hThread, &connection_attrib, - handle_connections_namedpipes, 0))) - { - sql_print_warning("Can't create thread to handle named pipes" - " (errno= %d)", error); - handler_count--; - } - } - if (have_tcpip && !opt_disable_networking) - { - handler_count++; - if ((error= mysql_thread_create(key_thread_handle_con_sockets, - &hThread, &connection_attrib, - handle_connections_sockets_thread, 0))) - { - sql_print_warning("Can't create thread to handle TCP/IP", - " (errno= %d)", error); - handler_count--; - } - } -#ifdef HAVE_SMEM - if (opt_enable_shared_memory) - { - handler_count++; - if ((error= mysql_thread_create(key_thread_handle_con_sharedmem, - &hThread, &connection_attrib, - handle_connections_shared_memory, 0))) - { - sql_print_warning("Can't create thread to handle shared memory", - " (errno= %d)", error); - handler_count--; - } - } +#else /*_WIN32*/ +#define create_shutdown_event() #endif - - while (handler_count > 0) - mysql_cond_wait(&COND_handler_count, &LOCK_start_thread); - mysql_mutex_unlock(&LOCK_start_thread); - DBUG_VOID_RETURN; -} - -void decrement_handler_count() -{ - mysql_mutex_lock(&LOCK_start_thread); - if (--handler_count == 0) - mysql_cond_signal(&COND_handler_count); - mysql_mutex_unlock(&LOCK_start_thread); - my_thread_end(); -} -#else -#define decrement_handler_count() -#endif /* defined(_WIN32) || defined(HAVE_SMEM) */ - +#endif /* EMBEDDED_LIBRARY */ #ifndef EMBEDDED_LIBRARY @@ -5769,6 +5521,11 @@ int win_main(int argc, char **argv) int mysqld_main(int argc, char **argv) #endif { +#ifndef _WIN32 + /* We can't close stdin just now, because it may be booststrap mode. */ + bool please_close_stdin= fcntl(STDIN_FILENO, F_GETFD) >= 0; +#endif + /* Perform basic thread library and malloc initialization, to be able to read defaults files and parse options. @@ -6119,7 +5876,7 @@ int mysqld_main(int argc, char **argv) } } - create_shutdown_thread(); + create_shutdown_event(); start_handle_manager(); /* Copy default global rpl_filter to global_rpl_filter */ @@ -6166,7 +5923,7 @@ int mysqld_main(int argc, char **argv) #ifndef _WIN32 // try to keep fd=0 busy - if (!freopen("/dev/null", "r", stdin)) + if (please_close_stdin && !freopen("/dev/null", "r", stdin)) { // fall back on failure fclose(stdin); @@ -6188,11 +5945,12 @@ int mysqld_main(int argc, char **argv) /* Memory used when everything is setup */ start_memory_used= global_status_var.global_memory_used; -#if defined(_WIN32) || defined(HAVE_SMEM) - handle_connections_methods(); +#ifdef _WIN32 + handle_connections_win(); + kill_server(0); #else handle_connections_sockets(); -#endif /* _WIN32 || HAVE_SMEM */ +#endif /* _WIN32 */ /* (void) pthread_attr_destroy(&connection_attrib); */ @@ -6601,7 +6359,7 @@ void create_thread_to_handle_connection(CONNECT *connect) @param[in,out] thd Thread handle of future thread. */ -static void create_new_thread(CONNECT *connect) +void create_new_thread(CONNECT *connect) { DBUG_ENTER("create_new_thread"); @@ -6669,18 +6427,107 @@ inline void kill_broken_server() #ifndef EMBEDDED_LIBRARY +void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock) +{ + CONNECT *connect; + bool is_unix_sock; + +#ifdef FD_CLOEXEC + (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC); +#endif + +#ifdef HAVE_LIBWRAP + { + if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) || + mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) + { + struct request_info req; + signal(SIGCHLD, SIG_DFL); + request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, + mysql_socket_getfd(new_sock), NULL); + my_fromhost(&req); + if (!my_hosts_access(&req)) + { + /* + This may be stupid but refuse() includes an exit(0) + which we surely don't want... + clean_exit() - same stupid thing ... + */ + syslog(deny_severity, "refused connect from %s", + my_eval_client(&req)); + + /* + C++ sucks (the gibberish in front just translates the supplied + sink function pointer in the req structure from a void (*sink)(); + to a void(*sink)(int) if you omit the cast, the C++ compiler + will cry... + */ + if (req.sink) + ((void(*)(int))req.sink)(req.fd); + + (void)mysql_socket_shutdown(new_sock, SHUT_RDWR); + (void)mysql_socket_close(new_sock); + /* + The connection was refused by TCP wrappers. + There are no details (by client IP) available to update the + host_cache. + */ + statistic_increment(connection_errors_tcpwrap, &LOCK_status); + return; + } + } + } +#endif /* HAVE_LIBWRAP */ + + DBUG_PRINT("info", ("Creating CONNECT for new connection")); + + if ((connect= new CONNECT())) + { + is_unix_sock= (mysql_socket_getfd(sock) == + mysql_socket_getfd(unix_sock)); + + if (!(connect->vio= + mysql_socket_vio_new(new_sock, + is_unix_sock ? VIO_TYPE_SOCKET : + VIO_TYPE_TCPIP, + is_unix_sock ? VIO_LOCALHOST : 0))) + { + delete connect; + connect= 0; // Error handling below + } + } + + if (!connect) + { + /* Connect failure */ + (void)mysql_socket_close(new_sock); + statistic_increment(aborted_connects, &LOCK_status); + statistic_increment(connection_errors_internal, &LOCK_status); + return; + } + + if (is_unix_sock) + connect->host= my_localhost; + + if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) + { + connect->extra_port= 1; + connect->scheduler= extra_thread_scheduler; + } + create_new_thread(connect); +} + +#ifndef _WIN32 void handle_connections_sockets() { MYSQL_SOCKET sock= mysql_socket_invalid(); MYSQL_SOCKET new_sock= mysql_socket_invalid(); uint error_count=0; - CONNECT *connect; struct sockaddr_storage cAddr; int ip_flags __attribute__((unused))=0; int socket_flags __attribute__((unused))= 0; int extra_ip_flags __attribute__((unused))=0; int flags=0,retval; - bool is_unix_sock; #ifdef HAVE_POLL int socket_count= 0; struct pollfd fds[3]; // for ip_sock, unix_sock and extra_ip_sock @@ -6812,10 +6659,7 @@ void handle_connections_sockets() } #endif } -#if !defined(NO_FCNTL_NONBLOCK) - if (!(test_flags & TEST_BLOCKING)) - fcntl(mysql_socket_getfd(sock), F_SETFL, flags); -#endif + if (mysql_socket_getfd(new_sock) == INVALID_SOCKET) { /* @@ -6831,442 +6675,18 @@ void handle_connections_sockets() sleep(1); // Give other threads some time continue; } -#ifdef FD_CLOEXEC - (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC); +#if !defined(NO_FCNTL_NONBLOCK) + if (!(test_flags & TEST_BLOCKING)) + fcntl(mysql_socket_getfd(sock), F_SETFL, flags); #endif - -#ifdef HAVE_LIBWRAP - { - if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) || - mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) - { - struct request_info req; - signal(SIGCHLD, SIG_DFL); - request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, - mysql_socket_getfd(new_sock), NULL); - my_fromhost(&req); - if (!my_hosts_access(&req)) - { - /* - This may be stupid but refuse() includes an exit(0) - which we surely don't want... - clean_exit() - same stupid thing ... - */ - syslog(deny_severity, "refused connect from %s", - my_eval_client(&req)); - - /* - C++ sucks (the gibberish in front just translates the supplied - sink function pointer in the req structure from a void (*sink)(); - to a void(*sink)(int) if you omit the cast, the C++ compiler - will cry... - */ - if (req.sink) - ((void (*)(int))req.sink)(req.fd); - - (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); - (void) mysql_socket_close(new_sock); - /* - The connection was refused by TCP wrappers. - There are no details (by client IP) available to update the - host_cache. - */ - statistic_increment(connection_errors_tcpwrap, &LOCK_status); - continue; - } - } - } -#endif /* HAVE_LIBWRAP */ - - DBUG_PRINT("info", ("Creating CONNECT for new connection")); - - if ((connect= new CONNECT())) - { - is_unix_sock= (mysql_socket_getfd(sock) == - mysql_socket_getfd(unix_sock)); - - if (!(connect->vio= - mysql_socket_vio_new(new_sock, - is_unix_sock ? VIO_TYPE_SOCKET : - VIO_TYPE_TCPIP, - is_unix_sock ? VIO_LOCALHOST: 0))) - { - delete connect; - connect= 0; // Error handling below - } - } - - if (!connect) - { - /* Connect failure */ - (void) mysql_socket_shutdown(new_sock, SHUT_RDWR); - (void) mysql_socket_close(new_sock); - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - continue; - } - - if (is_unix_sock) - connect->host= my_localhost; - - if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) - { - connect->extra_port= 1; - connect->scheduler= extra_thread_scheduler; - } - create_new_thread(connect); + handle_accepted_socket(new_sock, sock); } sd_notify(0, "STOPPING=1\n" "STATUS=Shutdown in progress\n"); DBUG_VOID_RETURN; } - -#ifdef _WIN32 -pthread_handler_t handle_connections_sockets_thread(void *arg) -{ - my_thread_init(); - handle_connections_sockets(); - decrement_handler_count(); - return 0; -} - -pthread_handler_t handle_connections_namedpipes(void *arg) -{ - HANDLE hConnectedPipe; - OVERLAPPED connectOverlapped= {0}; - my_thread_init(); - DBUG_ENTER("handle_connections_namedpipes"); - connectOverlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL); - if (!connectOverlapped.hEvent) - { - sql_print_error("Can't create event, last error=%u", GetLastError()); - unireg_abort(1); - } - DBUG_PRINT("general",("Waiting for named pipe connections.")); - while (!abort_loop) - { - /* wait for named pipe connection */ - BOOL fConnected= ConnectNamedPipe(hPipe, &connectOverlapped); - if (!fConnected && (GetLastError() == ERROR_IO_PENDING)) - { - /* - ERROR_IO_PENDING says async IO has started but not yet finished. - GetOverlappedResult will wait for completion. - */ - DWORD bytes; - fConnected= GetOverlappedResult(hPipe, &connectOverlapped,&bytes, TRUE); - } - if (abort_loop) - break; - if (!fConnected) - fConnected = GetLastError() == ERROR_PIPE_CONNECTED; - if (!fConnected) - { - CloseHandle(hPipe); - if ((hPipe= CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX | - FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | - PIPE_READMODE_BYTE | - PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables. - net_buffer_length, - (int) global_system_variables. - net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity)) == - INVALID_HANDLE_VALUE) - { - sql_perror("Can't create new named pipe!"); - break; // Abort - } - } - hConnectedPipe = hPipe; - /* create new pipe for new connection */ - if ((hPipe = CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX | - FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | - PIPE_READMODE_BYTE | - PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables.net_buffer_length, - (int) global_system_variables.net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity)) == - INVALID_HANDLE_VALUE) - { - sql_perror("Can't create new named pipe!"); - hPipe=hConnectedPipe; - continue; // We have to try again - } - CONNECT *connect; - if (!(connect= new CONNECT) || - !(connect->vio= vio_new_win32pipe(hConnectedPipe))) - { - DisconnectNamedPipe(hConnectedPipe); - CloseHandle(hConnectedPipe); - delete connect; - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - continue; - } - connect->host= my_localhost; - create_new_thread(connect); - } - CloseHandle(connectOverlapped.hEvent); - DBUG_LEAVE; - decrement_handler_count(); - return 0; -} -#endif /* _WIN32 */ - - -#ifdef HAVE_SMEM - -/** - Thread of shared memory's service. - - @param arg Arguments of thread -*/ -pthread_handler_t handle_connections_shared_memory(void *arg) -{ - /* file-mapping object, use for create shared memory */ - HANDLE handle_connect_file_map= 0; - char *handle_connect_map= 0; // pointer on shared memory - HANDLE event_connect_answer= 0; - ulong smem_buffer_length= shared_memory_buffer_length + 4; - ulong connect_number= 1; - char *tmp= NULL; - char *suffix_pos; - char connect_number_char[22], *p; - const char *errmsg= 0; - SECURITY_ATTRIBUTES *sa_event= 0, *sa_mapping= 0; - my_thread_init(); - DBUG_ENTER("handle_connections_shared_memorys"); - DBUG_PRINT("general",("Waiting for allocated shared memory.")); - - /* - get enough space base-name + '_' + longest suffix we might ever send - */ - if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L, - MYF(MY_FAE)))) - goto error; - - if (my_security_attr_create(&sa_event, &errmsg, - GENERIC_ALL, SYNCHRONIZE | EVENT_MODIFY_STATE)) - goto error; - - if (my_security_attr_create(&sa_mapping, &errmsg, - GENERIC_ALL, FILE_MAP_READ | FILE_MAP_WRITE)) - goto error; - - /* - The name of event and file-mapping events create agree next rule: - shared_memory_base_name+unique_part - Where: - shared_memory_base_name is unique value for each server - unique_part is unique value for each object (events and file-mapping) - */ - suffix_pos= strxmov(tmp,shared_memory_base_name,"_",NullS); - strmov(suffix_pos, "CONNECT_REQUEST"); - if ((smem_event_connect_request= CreateEvent(sa_event, - FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create request event"; - goto error; - } - strmov(suffix_pos, "CONNECT_ANSWER"); - if ((event_connect_answer= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg="Could not create answer event"; - goto error; - } - strmov(suffix_pos, "CONNECT_DATA"); - if ((handle_connect_file_map= - CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping, - PAGE_READWRITE, 0, sizeof(connect_number), tmp)) == 0) - { - errmsg= "Could not create file mapping"; - goto error; - } - if ((handle_connect_map= (char *)MapViewOfFile(handle_connect_file_map, - FILE_MAP_WRITE,0,0, - sizeof(DWORD))) == 0) - { - errmsg= "Could not create shared memory service"; - goto error; - } - - while (!abort_loop) - { - /* Wait a request from client */ - WaitForSingleObject(smem_event_connect_request,INFINITE); - - /* - it can be after shutdown command - */ - if (abort_loop) - goto error; - - HANDLE handle_client_file_map= 0; - char *handle_client_map= 0; - HANDLE event_client_wrote= 0; - HANDLE event_client_read= 0; // for transfer data server <-> client - HANDLE event_server_wrote= 0; - HANDLE event_server_read= 0; - HANDLE event_conn_closed= 0; - CONNECT *connect= 0; - - p= int10_to_str(connect_number, connect_number_char, 10); - /* - The name of event and file-mapping events create agree next rule: - shared_memory_base_name+unique_part+number_of_connection - Where: - shared_memory_base_name is uniquel value for each server - unique_part is unique value for each object (events and file-mapping) - number_of_connection is connection-number between server and client - */ - suffix_pos= strxmov(tmp,shared_memory_base_name,"_",connect_number_char, - "_",NullS); - strmov(suffix_pos, "DATA"); - if ((handle_client_file_map= - CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping, - PAGE_READWRITE, 0, smem_buffer_length, tmp)) == 0) - { - errmsg= "Could not create file mapping"; - goto errorconn; - } - if ((handle_client_map= (char*)MapViewOfFile(handle_client_file_map, - FILE_MAP_WRITE,0,0, - smem_buffer_length)) == 0) - { - errmsg= "Could not create memory map"; - goto errorconn; - } - strmov(suffix_pos, "CLIENT_WROTE"); - if ((event_client_wrote= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create client write event"; - goto errorconn; - } - strmov(suffix_pos, "CLIENT_READ"); - if ((event_client_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create client read event"; - goto errorconn; - } - strmov(suffix_pos, "SERVER_READ"); - if ((event_server_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create server read event"; - goto errorconn; - } - strmov(suffix_pos, "SERVER_WROTE"); - if ((event_server_wrote= CreateEvent(sa_event, - FALSE, FALSE, tmp)) == 0) - { - errmsg= "Could not create server write event"; - goto errorconn; - } - strmov(suffix_pos, "CONNECTION_CLOSED"); - if ((event_conn_closed= CreateEvent(sa_event, - TRUE, FALSE, tmp)) == 0) - { - errmsg= "Could not create closed connection event"; - goto errorconn; - } - if (abort_loop) - goto errorconn; - - if (!(connect= new CONNECT)) - { - errmsg= "Could not create CONNECT object"; - goto errorconn; - } - - /* Send number of connection to client */ - int4store(handle_connect_map, connect_number); - if (!SetEvent(event_connect_answer)) - { - errmsg= "Could not send answer event"; - goto errorconn; - } - /* Set event that client should receive data */ - if (!SetEvent(event_client_read)) - { - errmsg= "Could not set client to read mode"; - goto errorconn; - } - if (!(connect->vio= vio_new_win32shared_memory(handle_client_file_map, - handle_client_map, - event_client_wrote, - event_client_read, - event_server_wrote, - event_server_read, - event_conn_closed))) - { - errmsg= "Could not create VIO object"; - goto errorconn; - } - connect->host= my_localhost; /* Host is unknown */ - create_new_thread(connect); - connect_number++; - continue; - -errorconn: - /* Could not form connection; Free used handlers/memort and retry */ - if (errmsg) - { - char buff[180]; - strxmov(buff, "Can't create shared memory connection: ", errmsg, ".", - NullS); - sql_perror(buff); - } - if (handle_client_file_map) - CloseHandle(handle_client_file_map); - if (handle_client_map) - UnmapViewOfFile(handle_client_map); - if (event_server_wrote) - CloseHandle(event_server_wrote); - if (event_server_read) - CloseHandle(event_server_read); - if (event_client_wrote) - CloseHandle(event_client_wrote); - if (event_client_read) - CloseHandle(event_client_read); - if (event_conn_closed) - CloseHandle(event_conn_closed); - - delete connect; - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - } - - /* End shared memory handling */ -error: - if (tmp) - my_free(tmp); - - if (errmsg) - { - char buff[180]; - strxmov(buff, "Can't create shared memory service: ", errmsg, ".", NullS); - sql_perror(buff); - } - my_security_attr_free(sa_event); - my_security_attr_free(sa_mapping); - if (handle_connect_map) UnmapViewOfFile(handle_connect_map); - if (handle_connect_file_map) CloseHandle(handle_connect_file_map); - if (event_connect_answer) CloseHandle(event_connect_answer); - if (smem_event_connect_request) CloseHandle(smem_event_connect_request); - DBUG_LEAVE; - decrement_handler_count(); - return 0; -} -#endif /* HAVE_SMEM */ +#endif /* _WIN32*/ #endif /* EMBEDDED_LIBRARY */ @@ -8330,7 +7750,7 @@ my_asn1_time_to_string(const ASN1_TIME *time, char *buf, size_t len) if (bio == NULL) return NULL; - if (!ASN1_TIME_print(bio, time)) + if (!ASN1_TIME_print(bio, const_cast<ASN1_TIME*>(time))) goto end; n_read= BIO_read(bio, buf, (int) (len - 1)); @@ -8956,7 +8376,9 @@ static int mysql_init_variables(void) character_set_filesystem= &my_charset_bin; opt_specialflag= SPECIAL_ENGLISH; +#ifndef EMBEDDED_LIBRARY unix_sock= base_ip_sock= extra_ip_sock= MYSQL_INVALID_SOCKET; +#endif mysql_home_ptr= mysql_home; log_error_file_ptr= log_error_file; protocol_version= PROTOCOL_VERSION; @@ -9070,9 +8492,6 @@ static int mysql_init_variables(void) ssl_acceptor_fd= 0; #endif /* ! EMBEDDED_LIBRARY */ #endif /* HAVE_OPENSSL */ -#ifdef HAVE_SMEM - shared_memory_base_name= default_shared_memory_base_name; -#endif #if defined(__WIN__) /* Allow Win32 users to move MySQL anywhere */ diff --git a/sql/mysqld.h b/sql/mysqld.h index 4a392eaf196..75f35a6df24 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -24,6 +24,7 @@ #include "mysql_com.h" /* SERVER_VERSION_LENGTH */ #include "my_atomic.h" #include "mysql/psi/mysql_file.h" /* MYSQL_FILE */ +#include "mysql/psi/mysql_socket.h" /* MYSQL_SOCKET */ #include "sql_list.h" /* I_List */ #include "sql_cmd.h" #include <my_rnd.h> @@ -92,6 +93,8 @@ void refresh_status(THD *thd); bool is_secure_file_path(char *path); void dec_connection_count(scheduler_functions *scheduler); extern void init_net_server_extension(THD *thd); +extern void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock); +extern void create_new_thread(CONNECT *connect); extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ; @@ -152,6 +155,7 @@ extern ulong opt_replicate_events_marked_for_skip; extern char *default_tz_name; extern Time_zone *default_tz; extern char *my_bind_addr_str; +extern int server_socket_ai_family; extern char *default_storage_engine, *default_tmp_storage_engine; extern char *enforced_storage_engine; extern char *gtid_pos_auto_engines; @@ -169,11 +173,13 @@ extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size; extern ulong tc_log_page_waits; extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb; extern my_bool relay_log_recovery; -extern uint test_flags,select_errors,ha_open_options; +extern uint select_errors,ha_open_options; +extern ulonglong test_flags; extern uint protocol_version, mysqld_port, dropping_tables; extern ulong delay_key_write_options; extern char *opt_logname, *opt_slow_logname, *opt_bin_logname, *opt_relay_logname; +extern char *opt_binlog_index_name; extern char *opt_backup_history_logname, *opt_backup_progress_logname, *opt_backup_settings_name; extern const char *log_output_str; @@ -758,7 +764,7 @@ enum enum_query_type /* query_id */ extern query_id_t global_query_id; -ATTRIBUTE_NORETURN void unireg_end(void); +void unireg_end(void); /* increment query_id and return it. */ inline __attribute__((warn_unused_result)) query_id_t next_query_id() diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 576ef5009b5..c289fb2bc85 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -110,7 +110,7 @@ void sql_print_error(const char *format,...); extern, but as it's hard to include sql_priv.h here, we have to live with this for a while. */ -extern uint test_flags; +extern ulonglong test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; #ifdef HAVE_QUERY_CACHE #define USE_QUERY_CACHE diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 9134ad5ab62..b9b74bdd0c4 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2845,13 +2845,19 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, for (field_ptr= table->field; *field_ptr; field_ptr++) { - if (bitmap_is_set(used_fields, (*field_ptr)->field_index)) + Column_statistics* col_stats= (*field_ptr)->read_stats; + if (bitmap_is_set(used_fields, (*field_ptr)->field_index) + && col_stats && !col_stats->no_stat_values_provided() + && !((*field_ptr)->type() == MYSQL_TYPE_GEOMETRY)) parts++; } KEY_PART *key_part; uint keys= 0; + if (!parts) + return TRUE; + if (!(key_part= (KEY_PART *) alloc_root(param->mem_root, sizeof(KEY_PART) * parts))) return TRUE; @@ -2863,6 +2869,9 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, if (bitmap_is_set(used_fields, (*field_ptr)->field_index)) { Field *field= *field_ptr; + if (field->type() == MYSQL_TYPE_GEOMETRY) + continue; + uint16 store_length; uint16 max_key_part_length= (uint16) table->file->max_key_part_length(); key_part->key= keys; @@ -3020,7 +3029,18 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond) table->cond_selectivity= 1.0; - if (!*cond || table_records == 0) + if (table_records == 0) + DBUG_RETURN(FALSE); + + QUICK_SELECT_I *quick; + if ((quick=table->reginfo.join_tab->quick) && + quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) + { + table->cond_selectivity*= (quick->records/table_records); + DBUG_RETURN(FALSE); + } + + if (!*cond) DBUG_RETURN(FALSE); if (table->pos_in_table_list->schema_table) @@ -3137,7 +3157,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond) */ if (thd->variables.optimizer_use_condition_selectivity > 2 && - !bitmap_is_clear_all(used_fields)) + !bitmap_is_clear_all(used_fields) && + thd->variables.use_stat_tables > 0) { PARAM param; MEM_ROOT alloc; @@ -3226,6 +3247,12 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond) } + if (quick && (quick->get_type() == QUICK_SELECT_I::QS_TYPE_ROR_UNION || + quick->get_type() == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)) + { + table->cond_selectivity*= (quick->records/table_records); + } + bitmap_union(used_fields, &handled_columns); /* Check if we can improve selectivity estimates by using sampling */ @@ -7628,9 +7655,8 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param, param->current_table); #ifdef HAVE_SPATIAL Field::geometry_type sav_geom_type; - LINT_INIT_STRUCT(sav_geom_type); - - if (field_item->field->type() == MYSQL_TYPE_GEOMETRY) + const bool geometry= field_item->field->type() == MYSQL_TYPE_GEOMETRY; + if (geometry) { sav_geom_type= ((Field_geom*) field_item->field)->geom_type; /* We have to be able to store all sorts of spatial features here */ @@ -7665,7 +7691,7 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param, } #ifdef HAVE_SPATIAL - if (field_item->field->type() == MYSQL_TYPE_GEOMETRY) + if (geometry) { ((Field_geom*) field_item->field)->geom_type= sav_geom_type; } @@ -14833,6 +14859,32 @@ void QUICK_GROUP_MIN_MAX_SELECT::add_keys_and_lengths(String *key_names, } +/* Check whether the number for equality ranges exceeds the set threshold */ + +bool eq_ranges_exceeds_limit(RANGE_SEQ_IF *seq, void *seq_init_param, + uint limit) +{ + KEY_MULTI_RANGE range; + range_seq_t seq_it; + uint count = 0; + + if (limit == 0) + { + /* 'Statistics instead of index dives' feature is turned off */ + return false; + } + seq_it= seq->init(seq_init_param, 0, 0); + while (!seq->next(seq_it, &range)) + { + if ((range.range_flag & EQ_RANGE) && !(range.range_flag & NULL_RANGE)) + { + if (++count >= limit) + return true; + } + } + return false; +} + #ifndef DBUG_OFF static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, diff --git a/sql/opt_range.h b/sql/opt_range.h index bd85a12d4a1..d5416988b88 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -242,7 +242,7 @@ public: Number of children of this element in the RB-tree, plus 1 for this element itself. */ - uint16 elements; + uint32 elements; /* Valid only for elements which are RB-tree roots: Number of times this RB-tree is referred to (it is referred by SEL_ARG::next_key_part or by @@ -1724,6 +1724,9 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond); +bool eq_ranges_exceeds_limit(RANGE_SEQ_IF *seq, void *seq_init_param, + uint limit); + #ifdef WITH_PARTITION_STORAGE_ENGINE bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond); #endif diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc index 515d94e8748..2981c8182ea 100644 --- a/sql/opt_range_mrr.cc +++ b/sql/opt_range_mrr.cc @@ -72,6 +72,7 @@ typedef struct st_sel_arg_range_seq range_seq_t sel_arg_range_seq_init(void *init_param, uint n_ranges, uint flags) { SEL_ARG_RANGE_SEQ *seq= (SEL_ARG_RANGE_SEQ*)init_param; + seq->param->range_count=0; seq->at_start= TRUE; seq->stack[0].key_tree= NULL; seq->stack[0].min_key= seq->param->min_key; @@ -272,25 +273,44 @@ walk_up_n_right: key_info= NULL; else key_info= &seq->param->table->key_info[seq->real_keyno]; - + /* - Conditions below: - (1) - range analysis is used for estimating condition selectivity - (2) - This is a unique key, and we have conditions for all its - user-defined key parts. - (3) - The table uses extended keys, this key covers all components, - and we have conditions for all key parts. + This is an equality range (keypart_0=X and ... and keypart_n=Z) if + (1) - There are no flags indicating open range (e.g., + "keypart_x > y") or GIS. + (2) - The lower bound and the upper bound of the range has the + same value (min_key == max_key). */ - if (!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag && - (!key_info || // (1) - ((uint)key_tree->part+1 == key_info->user_defined_key_parts && // (2) - key_info->flags & HA_NOSAME) || // (2) - ((key_info->flags & HA_EXT_NOSAME) && // (3) - (uint)key_tree->part+1 == key_info->ext_key_parts) // (3) - ) && - range->start_key.length == range->end_key.length && - !memcmp(seq->param->min_key,seq->param->max_key,range->start_key.length)) - range->range_flag= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE); + const uint is_open_range = + (NO_MIN_RANGE | NO_MAX_RANGE | NEAR_MIN | NEAR_MAX | GEOM_FLAG); + const bool is_eq_range_pred = + !(cur->min_key_flag & is_open_range) && // (1) + !(cur->max_key_flag & is_open_range) && // (1) + range->start_key.length == range->end_key.length && // (2) + !memcmp(seq->param->min_key, seq->param->max_key, // (2) + range->start_key.length); + + if (is_eq_range_pred) + { + range->range_flag = EQ_RANGE; + + /* + Conditions below: + (1) - Range analysis is used for estimating condition selectivity + (2) - This is a unique key, and we have conditions for all its + user-defined key parts. + (3) - The table uses extended keys, this key covers all components, + and we have conditions for all key parts. + */ + if ( + !key_info || // (1) + ((uint)key_tree->part+1 == key_info->user_defined_key_parts && // (2) + key_info->flags & HA_NOSAME) || // (2) + ((key_info->flags & HA_EXT_NOSAME) && // (3) + (uint)key_tree->part+1 == key_info->ext_key_parts) // (3) + ) + range->range_flag |= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE); + } if (seq->param->is_ror_scan) { diff --git a/sql/opt_split.cc b/sql/opt_split.cc index 37853bdbbe9..fc3f08464f4 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -267,6 +267,13 @@ void TABLE::deny_splitting() } +double TABLE::get_materialization_cost() +{ + DBUG_ASSERT(spl_opt_info != NULL); + return spl_opt_info->unsplit_cost; +} + + /* This structure is auxiliary and used only in the function that follows it */ struct SplM_field_ext_info: public SplM_field_info { @@ -413,7 +420,8 @@ bool JOIN::check_for_splittable_materialized() for (cand= cand_start; cand < cand_end; cand++) { - if (cand->underlying_field->field_index + 1 == fldnr) + if (cand->underlying_field->table == table && + cand->underlying_field->field_index + 1 == fldnr) { cand->is_usable_for_ref_access= true; break; @@ -887,6 +895,8 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count, continue; JOIN_TAB *tab= join->map2table[tablenr]; TABLE *table= tab->table; + if (keyuse_ext->table != table) + continue; do { uint key= keyuse_ext->key; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 06358b3cc20..c4c30c9b50d 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -441,6 +441,7 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs); static bool replace_where_subcondition(JOIN *, Item **, Item *, Item *, bool); static int subq_sj_candidate_cmp(Item_in_subselect* el1, Item_in_subselect* el2, void *arg); +static void reset_equality_number_for_subq_conds(Item * cond); static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred); static bool convert_subq_to_jtbm(JOIN *parent_join, Item_in_subselect *subq_pred, bool *remove); @@ -816,6 +817,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) details) * require that compared columns have exactly the same type. This is a temporary measure to avoid BUG#36752-type problems. + + JOIN_TAB::keyuse_is_valid_for_access_in_chosen_plan expects that for Semi Join Materialization + Scan all the items in the select list of the IN Subquery are of the type Item::FIELD_ITEM. */ static @@ -1430,6 +1434,67 @@ static int subq_sj_candidate_cmp(Item_in_subselect* el1, Item_in_subselect* el2, } +/** + @brief + reset the value of the field in_eqaulity_no for all Item_func_eq + items in the where clause of the subquery. + + Look for in_equality_no description in Item_func_eq class + + DESCRIPTION + Lets have an example: + SELECT t1.a FROM t1 WHERE t1.a IN + (SELECT t2.a FROM t2 where t2.b IN + (select t3.b from t3 where t3.c=27 )) + + So for such a query we have the parent, child and + grandchild select. + + So for the equality t2.b = t3.b we set the value for in_equality_no to + 0 according to its description. Wewe do the same for t1.a = t2.a. + But when we look at the child select (with the grandchild select merged), + the query would be + + SELECT t1.a FROM t1 WHERE t1.a IN + (SELECT t2.a FROM t2 where t2.b = t3.b and t3.c=27) + + and then when the child select is merged into the parent select the query + would look like + + SELECT t1.a FROM t1, semi-join-nest(t2,t3) + WHERE t1.a =t2.a and t2.b = t3.b and t3.c=27 + + Still we would have in_equality_no set for t2.b = t3.b + though it does not take part in the semi-join equality for the parent select, + so we should reset its value to UINT_MAX. + + @param cond WHERE clause of the subquery +*/ + +static void reset_equality_number_for_subq_conds(Item * cond) +{ + if (!cond) + return; + if (cond->type() == Item::COND_ITEM) + { + List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + Item *item; + while ((item=li++)) + { + if (item->type() == Item::FUNC_ITEM && + ((Item_func*)item)->functype()== Item_func::EQ_FUNC) + ((Item_func_eq*)item)->in_equality_no= UINT_MAX; + } + } + else + { + if (cond->type() == Item::FUNC_ITEM && + ((Item_func*)cond)->functype()== Item_func::EQ_FUNC) + ((Item_func_eq*)cond)->in_equality_no= UINT_MAX; + } + return; +} + /* Convert a subquery predicate into a TABLE_LIST semi-join nest @@ -1694,6 +1759,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) */ sj_nest->sj_in_exprs= subq_pred->left_expr->cols(); sj_nest->nested_join->sj_outer_expr_list.empty(); + reset_equality_number_for_subq_conds(sj_nest->sj_on_expr); if (subq_pred->left_expr->cols() == 1) { @@ -3506,7 +3572,8 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) first= tablenr - sjm->tables + 1; join->best_positions[first].n_sj_tables= sjm->tables; join->best_positions[first].sj_strategy= SJ_OPT_MATERIALIZE; - join->sjm_lookup_tables|= s->table->map; + for (uint i= first; i < first+ sjm->tables; i++) + join->sjm_lookup_tables |= join->best_positions[i].table->table->map; } else if (pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN) { @@ -5445,7 +5512,7 @@ int select_value_catcher::send_data(List<Item> &items) /** @brief - Conjugate conditions after optimize_cond() call + Add new conditions after optimize_cond() call @param thd the thread handle @param cond the condition where to attach new conditions @@ -5494,8 +5561,8 @@ Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond, Item::Context(Item::ANY_SUBST, ((Item_func_equal *)item)->compare_type_handler(), ((Item_func_equal *)item)->compare_collation()), - ((Item_func *)item)->arguments()[0]->real_item(), - ((Item_func *)item)->arguments()[1]->real_item(), + ((Item_func *)item)->arguments()[0], + ((Item_func *)item)->arguments()[1], &new_cond_equal)) li.remove(); } @@ -5600,31 +5667,31 @@ Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond, } } + if (is_mult_eq) + { + Item_equal *eq_cond= (Item_equal *)cond; + eq_cond->upper_levels= 0; + eq_cond->merge_into_list(thd, &new_cond_equal.current_level, + false, false); + + while ((equality= it++)) + { + if (equality->const_item() && !equality->val_int()) + is_simplified_cond= true; + } + (*cond_eq)->copy(new_cond_equal); + } + if (new_cond_equal.current_level.elements > 0) { - if (is_mult_eq) + if (new_cond_equal.current_level.elements + + new_conds_list.elements == 1) { - Item_equal *eq_cond= (Item_equal *)cond; - eq_cond->upper_levels= 0; - eq_cond->merge_into_list(thd, &new_cond_equal.current_level, - false, false); - - while ((equality= it++)) - { - if (equality->const_item() && !equality->val_int()) - is_simplified_cond= true; - } - - if (new_cond_equal.current_level.elements + - new_conds_list.elements == 1) - { - it.rewind(); - equality= it++; - equality->fixed= 0; - if (equality->fix_fields(thd, NULL)) - return NULL; - } - (*cond_eq)->copy(new_cond_equal); + it.rewind(); + equality= it++; + equality->fixed= 0; + if (equality->fix_fields(thd, NULL)) + return NULL; } new_conds_list.append((List<Item> *)&new_cond_equal.current_level); } diff --git a/sql/partition_info.h b/sql/partition_info.h index e00a2c44341..a0cde570d03 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -394,12 +394,13 @@ public: bool has_unique_name(partition_element *element); bool vers_init_info(THD *thd); - bool vers_set_interval(Item *item, interval_type int_type, my_time_t start) + bool vers_set_interval(THD *thd, Item *item, + interval_type int_type, my_time_t start) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); vers_info->interval.type= int_type; vers_info->interval.start= start; - return get_interval_value(item, int_type, &vers_info->interval.step) || + return get_interval_value(thd, item, int_type, &vers_info->interval.step) || vers_info->interval.step.neg || vers_info->interval.step.second_part || !(vers_info->interval.step.year || vers_info->interval.step.month || vers_info->interval.step.day || vers_info->interval.step.hour || diff --git a/sql/procedure.h b/sql/procedure.h index 8826b397fb2..050cc3817c0 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -69,9 +69,9 @@ public: DBUG_ASSERT(0); // impossible return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE); } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - return type_handler()->Item_get_date(this, ltime, fuzzydate); + return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate); } Item* get_copy(THD *thd) { return 0; } }; diff --git a/sql/protocol.cc b/sql/protocol.cc index 29004fe24e4..7eee9283989 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -711,7 +711,7 @@ uchar *net_store_data(uchar *to, const uchar *from, size_t length) uchar *net_store_data(uchar *to,int32 from) { - char buff[20]; + char buff[22]; uint length=(uint) (int10_to_str(from,buff,10)-buff); to=net_store_length_fast(to,length); memcpy(to,buff,length); @@ -1137,7 +1137,7 @@ bool Protocol_text::store_tiny(longlong from) DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY); field_pos++; #endif - char buff[20]; + char buff[22]; return net_store_data((uchar*) buff, (size_t) (int10_to_str((int) from, buff, -10) - buff)); } @@ -1151,7 +1151,7 @@ bool Protocol_text::store_short(longlong from) field_types[field_pos] == MYSQL_TYPE_SHORT); field_pos++; #endif - char buff[20]; + char buff[22]; return net_store_data((uchar*) buff, (size_t) (int10_to_str((int) from, buff, -10) - buff)); @@ -1166,7 +1166,7 @@ bool Protocol_text::store_long(longlong from) field_types[field_pos] == MYSQL_TYPE_LONG); field_pos++; #endif - char buff[20]; + char buff[22]; return net_store_data((uchar*) buff, (size_t) (int10_to_str((long int)from, buff, (from <0)?-10:10)-buff)); diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index ad885c925d9..322b84130f2 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -79,7 +79,7 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi) rgi->gtid_pending= false; if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE) { - if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false, &hton)) + if (record_gtid(thd, &rgi->current_gtid, sub_id, NULL, false, &hton)) DBUG_RETURN(1); update_state_hash(sub_id, &rgi->current_gtid, hton, rgi); } @@ -331,6 +331,10 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, } } rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL; + +#ifdef HAVE_REPLICATION + rgi->pending_gtid_deletes_clear(); +#endif } if (!(list_elem= (list_element *)my_malloc(sizeof(*list_elem), MYF(MY_WME)))) @@ -381,15 +385,24 @@ int rpl_slave_state::put_back_list(uint32 domain_id, list_element *list) { element *e; + int err= 0; + + mysql_mutex_lock(&LOCK_slave_state); if (!(e= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0))) - return 1; + { + err= 1; + goto end; + } while (list) { list_element *next= list->next; e->add(list); list= next; } - return 0; + +end: + mysql_mutex_unlock(&LOCK_slave_state); + return err; } @@ -403,6 +416,8 @@ rpl_slave_state::truncate_state_table(THD *thd) tlist.init_one_table(&MYSQL_SCHEMA_NAME, &rpl_gtid_slave_state_table_name, NULL, TL_WRITE); if (!(err= open_and_lock_tables(thd, &tlist, FALSE, 0))) { + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, "mysql", + rpl_gtid_slave_state_table_name.str, false); err= tlist.table->file->ha_truncate(); if (err) @@ -557,12 +572,12 @@ rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename) /* Write a gtid to the replication slave state table. - Do it as part of the transaction, to get slave crash safety, or as a separate - transaction if !in_transaction (eg. MyISAM or DDL). - gtid The global transaction id for this event group. sub_id Value allocated within the sub_id when the event group was read (sub_id must be consistent with commit order in master binlog). + rgi rpl_group_info context, if we are recording the gtid transactionally + as part of replicating a transactional event. NULL if called from + outside of a replicated transaction. Note that caller must later ensure that the new gtid and sub_id is inserted into the appropriate HASH element with rpl_slave_state.add(), so that it can @@ -570,7 +585,7 @@ rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename) */ int rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, - bool in_transaction, bool in_statement, + rpl_group_info *rgi, bool in_statement, void **out_hton) { TABLE_LIST tlist; @@ -669,7 +684,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, thd->wsrep_ignore_table= true; #endif - if (!in_transaction) + if (!rgi) { DBUG_PRINT("info", ("resetting OPTION_BEGIN")); thd->variables.option_bits&= @@ -774,7 +789,8 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, table->file->print_error(err, MYF(0)); goto end; } - while (delete_list) + cur = delete_list; + while (cur) { uchar key_buffer[4+8]; @@ -784,9 +800,9 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, /* `break' does not work inside DBUG_EXECUTE_IF */ goto dbug_break; }); - next= delete_list->next; + next= cur->next; - table->field[1]->store(delete_list->sub_id, true); + table->field[1]->store(cur->sub_id, true); /* domain_id is already set in table->record[0] from write_row() above. */ key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false); if (table->file->ha_index_read_map(table->record[1], key_buffer, @@ -800,8 +816,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, not want to endlessly error on the same element in case of table corruption or such. */ - my_free(delete_list); - delete_list= next; + cur= next; if (err) break; } @@ -824,18 +839,35 @@ end: */ if (delete_list) { - mysql_mutex_lock(&LOCK_slave_state); put_back_list(gtid->domain_id, delete_list); - mysql_mutex_unlock(&LOCK_slave_state); + delete_list = 0; } ha_rollback_trans(thd, FALSE); } close_thread_tables(thd); - if (in_transaction) + if (rgi) + { thd->mdl_context.release_statement_locks(); + /* + Save the list of old gtid entries we deleted. If this transaction + fails later for some reason and is rolled back, the deletion of those + entries will be rolled back as well, and we will need to put them back + on the to-be-deleted list so we can re-do the deletion. Otherwise + redundant rows in mysql.gtid_slave_pos may accumulate if transactions + are rolled back and retried after record_gtid(). + */ +#ifdef HAVE_REPLICATION + rgi->pending_gtid_deletes_save(gtid->domain_id, delete_list); +#endif + } else + { thd->mdl_context.release_transactional_locks(); +#ifdef HAVE_REPLICATION + rpl_group_info::pending_gtid_deletes_free(delete_list); +#endif + } } thd->lex->restore_backup_query_tables_list(&lex_backup); thd->variables.option_bits= thd_saved_option; @@ -1219,7 +1251,7 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len, if (gtid_parser_helper(&state_from_master, end, >id) || !(sub_id= next_sub_id(gtid.domain_id)) || - record_gtid(thd, >id, sub_id, false, in_statement, &hton) || + record_gtid(thd, >id, sub_id, NULL, in_statement, &hton) || update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL)) return 1; if (state_from_master == end) @@ -2022,10 +2054,10 @@ rpl_binlog_state::drop_domain(DYNAMIC_ARRAY *ids, for (ulong i= 0; i < ids->elements; i++) { rpl_binlog_state::element *elem= NULL; - ulong *ptr_domain_id; + uint32 *ptr_domain_id; bool not_match; - ptr_domain_id= (ulong*) dynamic_array_ptr(ids, i); + ptr_domain_id= (uint32*) dynamic_array_ptr(ids, i); elem= (rpl_binlog_state::element *) my_hash_search(&hash, (const uchar *) ptr_domain_id, 0); if (!elem) @@ -2046,7 +2078,7 @@ rpl_binlog_state::drop_domain(DYNAMIC_ARRAY *ids, if (not_match) { - sprintf(errbuf, "binlog files may contain gtids from the domain ('%lu') " + sprintf(errbuf, "binlog files may contain gtids from the domain ('%u') " "being deleted. Make sure to first purge those files", *ptr_domain_id); errmsg= errbuf; diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index 604289acd6b..0fc92d5e33c 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -233,7 +233,7 @@ struct rpl_slave_state int truncate_state_table(THD *thd); void select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename); int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, - bool in_transaction, bool in_statement, void **out_hton); + rpl_group_info *rgi, bool in_statement, void **out_hton); uint64 next_sub_id(uint32 domain_id); int iterate(int (*cb)(rpl_gtid *, void *), void *data, rpl_gtid *extra_gtids, uint32 num_extra, diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index a12bbba5a1f..144b12a9fdf 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -1657,21 +1657,31 @@ int rpl_parallel_resize_pool_if_no_slaves(void) /** - Resize pool if not active or busy (in which case we may be in - resize to 0 + Pool activation is preceeded by taking a "lock" of pool_mark_busy + which guarantees the number of running slaves drops to zero atomicly + with the number of pool workers. + This resolves race between the function caller thread and one + that may be attempting to deactivate the pool. */ - int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool) { - bool resize; - mysql_mutex_lock(&pool->LOCK_rpl_thread_pool); - resize= !pool->count || pool->busy; - mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); - if (resize) - return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads, - 0); - return 0; + int rc= 0; + + if ((rc= pool_mark_busy(pool, current_thd))) + return rc; // killed + + if (!pool->count) + { + pool_mark_not_busy(pool); + rc= rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads, + 0); + } + else + { + pool_mark_not_busy(pool); + } + return rc; } diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 9e09a5cf067..b275ad884bd 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -2086,6 +2086,7 @@ rpl_group_info::reinit(Relay_log_info *rli) long_find_row_note_printed= false; did_mark_start_commit= false; gtid_ev_flags2= 0; + pending_gtid_delete_list= NULL; last_master_timestamp = 0; gtid_ignore_duplicate_state= GTID_DUPLICATE_NULL; speculation= SPECULATE_NO; @@ -2216,6 +2217,12 @@ void rpl_group_info::cleanup_context(THD *thd, bool error) erroneously update the GTID position. */ gtid_pending= false; + + /* + Rollback will have undone any deletions of old rows we might have made + in mysql.gtid_slave_pos. Put those rows back on the list to be deleted. + */ + pending_gtid_deletes_put_back(); } m_table_map.clear_tables(); slave_close_thread_tables(thd); @@ -2441,6 +2448,78 @@ rpl_group_info::unmark_start_commit() } +/* + When record_gtid() has deleted any old rows from the table + mysql.gtid_slave_pos as part of a replicated transaction, save the list of + rows deleted here. + + If later the transaction fails (eg. optimistic parallel replication), the + deletes will be undone when the transaction is rolled back. Then we can + put back the list of rows into the rpl_global_gtid_slave_state, so that + we can re-do the deletes and avoid accumulating old rows in the table. +*/ +void +rpl_group_info::pending_gtid_deletes_save(uint32 domain_id, + rpl_slave_state::list_element *list) +{ + /* + We should never get to a state where we try to save a new pending list of + gtid deletes while we still have an old one. But make sure we handle it + anyway just in case, so we avoid leaving stray entries in the + mysql.gtid_slave_pos table. + */ + DBUG_ASSERT(!pending_gtid_delete_list); + if (unlikely(pending_gtid_delete_list)) + pending_gtid_deletes_put_back(); + + pending_gtid_delete_list= list; + pending_gtid_delete_list_domain= domain_id; +} + + +/* + Take the list recorded by pending_gtid_deletes_save() and put it back into + rpl_global_gtid_slave_state. This is needed if deletion of the rows was + rolled back due to transaction failure. +*/ +void +rpl_group_info::pending_gtid_deletes_put_back() +{ + if (pending_gtid_delete_list) + { + rpl_global_gtid_slave_state->put_back_list(pending_gtid_delete_list_domain, + pending_gtid_delete_list); + pending_gtid_delete_list= NULL; + } +} + + +/* + Free the list recorded by pending_gtid_deletes_save(). Done when the deletes + in the list have been permanently committed. +*/ +void +rpl_group_info::pending_gtid_deletes_clear() +{ + pending_gtid_deletes_free(pending_gtid_delete_list); + pending_gtid_delete_list= NULL; +} + + +void +rpl_group_info::pending_gtid_deletes_free(rpl_slave_state::list_element *list) +{ + rpl_slave_state::list_element *next; + + while (list) + { + next= list->next; + my_free(list); + list= next; + } +} + + rpl_sql_thread_info::rpl_sql_thread_info(Rpl_filter *filter) : rpl_filter(filter) { diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index b8b153c34be..d9f0e0e5d3b 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -757,6 +757,11 @@ struct rpl_group_info /* Needs room for "Gtid D-S-N\x00". */ char gtid_info_buf[5+10+1+10+1+20+1]; + /* List of not yet committed deletions in mysql.gtid_slave_pos. */ + rpl_slave_state::list_element *pending_gtid_delete_list; + /* Domain associated with pending_gtid_delete_list. */ + uint32 pending_gtid_delete_list_domain; + /* The timestamp, from the master, of the commit event. Used to do delayed update of rli->last_master_timestamp, for getting @@ -898,6 +903,12 @@ struct rpl_group_info char *gtid_info(); void unmark_start_commit(); + static void pending_gtid_deletes_free(rpl_slave_state::list_element *list); + void pending_gtid_deletes_save(uint32 domain_id, + rpl_slave_state::list_element *list); + void pending_gtid_deletes_put_back(); + void pending_gtid_deletes_clear(); + longlong get_row_stmt_start_timestamp() { return row_stmt_start_timestamp; diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index 31035fb5dcc..e170b4772c1 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -817,14 +817,44 @@ can_convert_field_to(Field *field, case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_NULL: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_TIMESTAMP2: - case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_TIME2: DBUG_RETURN(false); + case MYSQL_TYPE_NEWDATE: + { + if (field->real_type() == MYSQL_TYPE_DATETIME2 || + field->real_type() == MYSQL_TYPE_DATETIME) + { + *order_var= -1; + DBUG_RETURN(is_conversion_ok(*order_var, rli)); + } + else + { + DBUG_RETURN(false); + } + } + break; + + //case MYSQL_TYPE_DATETIME: TODO: fix MDEV-17394 and uncomment. + // + //The "old" type does not specify the fraction part size which is required + //for correct conversion. + case MYSQL_TYPE_DATETIME2: + { + if (field->real_type() == MYSQL_TYPE_NEWDATE) + { + *order_var= 1; + DBUG_RETURN(is_conversion_ok(*order_var, rli)); + } + else + { + DBUG_RETURN(false); + } + } + break; } DBUG_RETURN(false); // To keep GCC happy } diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc index ac17c7de40b..fc36ee35d5d 100644 --- a/sql/semisync_master_ack_receiver.cc +++ b/sql/semisync_master_ack_receiver.cc @@ -43,8 +43,7 @@ Ack_receiver::Ack_receiver() DBUG_ENTER("Ack_receiver::Ack_receiver"); m_status= ST_DOWN; - mysql_mutex_init(key_LOCK_ack_receiver, &m_mutex, - MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_LOCK_ack_receiver, &m_mutex, NULL); mysql_cond_init(key_COND_ack_receiver, &m_cond, NULL); m_pid= 0; diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc index 86d0176dac1..df8baf045ac 100644 --- a/sql/semisync_slave.cc +++ b/sql/semisync_slave.cc @@ -144,8 +144,7 @@ void Repl_semi_sync_slave::kill_connection(MYSQL *mysql) { sql_print_information("cannot connect to master to kill slave io_thread's " "connection"); - if (!ret) - mysql_close(kill_mysql); + mysql_close(kill_mysql); return; } size_t kill_buffer_length = my_snprintf(kill_buffer, 30, "KILL %lu", diff --git a/sql/share/CMakeLists.txt b/sql/share/CMakeLists.txt index 1461c57c5c3..2980e6153f5 100644 --- a/sql/share/CMakeLists.txt +++ b/sql/share/CMakeLists.txt @@ -45,12 +45,14 @@ SET(files errmsg-utf8.txt ) -FOREACH (dir ${dirs}) - INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${dir} - DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server) -ENDFOREACH() INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Common PATTERN "languages.html" EXCLUDE ) -INSTALL(FILES ${files} DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server) +IF (NOT WITHOUT_SERVER) + FOREACH (dir ${dirs}) + INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${dir} + DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server) + ENDFOREACH() + INSTALL(FILES ${files} DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server) +ENDIF() diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 77347472521..e73666cfb58 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6570,7 +6570,7 @@ ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000 ukr "Доступ заборонено для користувача: '%s'@'%s'" ER_SET_PASSWORD_AUTH_PLUGIN - eng "SET PASSWORD has no significance for users authenticating via plugins" + eng "SET PASSWORD is ignored for users authenticating via %s plugin" ER_GRANT_PLUGIN_USER_EXISTS eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists" @@ -7919,3 +7919,5 @@ ER_VERS_QUERY_IN_PARTITION eng "SYSTEM_TIME partitions in table %`s does not support historical query" ER_KEY_DOESNT_SUPPORT eng "%s index %`s does not support this operation" +ER_ALTER_OPERATION_TABLE_OPTIONS_NEED_REBUILD + eng "Changing table options requires the table to be rebuilt" diff --git a/sql/slave.cc b/sql/slave.cc index bb1300d36e6..16fa890d86c 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4304,6 +4304,19 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, This is the case for pre-10.0 events without GTID, and for handling slave_skip_counter. */ + if (!(ev->is_artificial_event() || ev->is_relay_log_event() || (ev->when == 0))) + { + /* + Ignore FD's timestamp as it does not reflect the slave execution + state but likely to reflect a deep past. Consequently when the first + data modification event execution last long all this time + Seconds_Behind_Master is zero. + */ + if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT) + rli->last_master_timestamp= ev->when + (time_t) ev->exec_time; + + DBUG_ASSERT(rli->last_master_timestamp >= 0); + } } if (typ == GTID_EVENT) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d00ee3e07ef..052c5ada3a2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. Copyright (c) 2009, 2018, MariaDB This program is free software; you can redistribute it and/or modify @@ -57,6 +57,8 @@ #include "sql_plugin_compat.h" +#define MAX_SCRAMBLE_LENGTH 1024 + bool mysql_user_table_is_in_short_password_format= false; static LEX_CSTRING native_password_plugin_name= { @@ -85,11 +87,19 @@ LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") }; LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") }; -#ifndef NO_EMBEDDED_ACCESS_CHECKS static plugin_ref old_password_plugin; -#endif static plugin_ref native_password_plugin; +static plugin_ref get_auth_plugin(THD *thd, const LEX_CSTRING &name, bool *locked) +{ + if (name.str == native_password_plugin_name.str) + return native_password_plugin; + else if (name.str == old_password_plugin_name.str) + return old_password_plugin; + *locked=true; + return my_plugin_lock_by_name(thd, &name, MYSQL_AUTHENTICATION_PLUGIN); +} + /* Classes */ struct acl_host_and_ip @@ -119,13 +129,10 @@ public: char *db; }; -class ACL_USER_BASE :public ACL_ACCESS +class ACL_USER_BASE :public ACL_ACCESS, public Sql_alloc { public: - static void *operator new(size_t size, MEM_ROOT *mem_root) - { return (void*) alloc_root(mem_root, size); } - static void operator delete(void *, MEM_ROOT *){} uchar flags; // field used to store various state information LEX_CSTRING user; /* list to hold references to granted roles (ACL_ROLE instances) */ @@ -138,13 +145,12 @@ public: acl_host_and_ip host; size_t hostname_length; USER_RESOURCES user_resource; - uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form - uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_CSTRING plugin; LEX_CSTRING auth_string; LEX_CSTRING default_rolename; + LEX_CSTRING salt; ACL_USER *copy(MEM_ROOT *root) { @@ -152,8 +158,7 @@ public: if (!dst) return 0; *dst= *this; - dst->user.str= safe_strdup_root(root, user.str); - dst->user.length= user.length; + dst->user= safe_lexcstrdup_root(root, user); dst->ssl_cipher= safe_strdup_root(root, ssl_cipher); dst->x509_issuer= safe_strdup_root(root, x509_issuer); dst->x509_subject= safe_strdup_root(root, x509_subject); @@ -161,11 +166,11 @@ public: plugin.str == old_password_plugin_name.str) dst->plugin= plugin; else - dst->plugin.str= strmake_root(root, plugin.str, plugin.length); - dst->auth_string.str= safe_strdup_root(root, auth_string.str); + dst->plugin= safe_lexcstrdup_root(root, plugin); + dst->auth_string= safe_lexcstrdup_root(root, auth_string); + dst->salt= safe_lexcstrdup_root(root, salt); dst->host.hostname= safe_strdup_root(root, host.hostname); - dst->default_rolename.str= safe_strdup_root(root, default_rolename.str); - dst->default_rolename.length= default_rolename.length; + dst->default_rolename= safe_lexcstrdup_root(root, default_rolename); bzero(&dst->role_grants, sizeof(role_grants)); return dst; } @@ -174,7 +179,7 @@ public: { CHARSET_INFO *cs= system_charset_info; int res; - res= strcmp(safe_str(user.str), safe_str(user2)); + res= strcmp(user.str, user2); if (!res) res= my_strcasecmp(cs, host.hostname, host2); return res; @@ -184,7 +189,7 @@ public: bool wild_eq(const char *user2, const char *host2, const char *ip2) { - if (strcmp(safe_str(user.str), safe_str(user2))) + if (strcmp(user.str, user2)) return false; return compare_hostname(&host, host2, ip2 ? ip2 : host2); @@ -275,10 +280,9 @@ public: const char *proxied_host_arg, const char *proxied_user_arg, bool with_grant_arg) { - user= (user_arg && *user_arg) ? user_arg : NULL; + user= user_arg; update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL); - proxied_user= (proxied_user_arg && *proxied_user_arg) ? - proxied_user_arg : NULL; + proxied_user= proxied_user_arg; update_hostname (&proxied_host, (proxied_host_arg && *proxied_host_arg) ? proxied_host_arg : NULL); @@ -291,11 +295,10 @@ public: bool with_grant_arg) { init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL, - (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL, + strdup_root (mem, user_arg), (proxied_host_arg && *proxied_host_arg) ? strdup_root (mem, proxied_host_arg) : NULL, - (proxied_user_arg && *proxied_user_arg) ? - strdup_root (mem, proxied_user_arg) : NULL, + strdup_root (mem, proxied_user_arg), with_grant_arg); } @@ -308,7 +311,7 @@ public: const char *get_proxied_host() { return proxied_host.hostname; } void set_user(MEM_ROOT *mem, const char *user_arg) { - user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL; + user= *user_arg ? strdup_root(mem, user_arg) : ""; } void set_host(MEM_ROOT *mem, const char *host_arg) { @@ -323,9 +326,8 @@ public: { sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' " "ignored in --skip-name-resolve mode.", - safe_str(proxied_user), - safe_str(proxied_host.hostname), - safe_str(user), + proxied_user, + safe_str(proxied_host.hostname), user, safe_str(host.hostname)); return TRUE; } @@ -345,11 +347,10 @@ public: proxied_user_arg, proxied_user)); DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) && compare_hostname(&proxied_host, host_arg, ip_arg) && - (!user || + (!*user || (user_arg && !wild_compare(user_arg, user, TRUE))) && - (!proxied_user || - (proxied_user && !wild_compare(proxied_user_arg, - proxied_user, TRUE)))); + (!*proxied_user || + !wild_compare(proxied_user_arg, proxied_user, TRUE))); } @@ -381,8 +382,7 @@ public: bool granted_on(const char *host_arg, const char *user_arg) { - return (((!user && (!user_arg || !user_arg[0])) || - (user && user_arg && !strcmp(user, user_arg))) && + return (!strcmp(user, user_arg) && ((!host.hostname && (!host_arg || !host_arg[0])) || (host.hostname && host_arg && !strcmp(host.hostname, host_arg)))); } @@ -391,17 +391,15 @@ public: void print_grant(String *str) { str->append(STRING_WITH_LEN("GRANT PROXY ON '")); - if (proxied_user) - str->append(proxied_user, strlen(proxied_user)); + str->append(proxied_user); str->append(STRING_WITH_LEN("'@'")); if (proxied_host.hostname) str->append(proxied_host.hostname, strlen(proxied_host.hostname)); str->append(STRING_WITH_LEN("' TO '")); - if (user) - str->append(user, strlen(user)); + str->append(user); str->append(STRING_WITH_LEN("'@'")); if (host.hostname) - str->append(host.hostname, strlen(host.hostname)); + str->append(host.hostname); str->append(STRING_WITH_LEN("'")); if (with_grant) str->append(STRING_WITH_LEN(" WITH GRANT OPTION")); @@ -622,8 +620,8 @@ static ACL_USER *find_user_wild(const char *host, const char *user, const char * static ACL_ROLE *find_acl_role(const char *user); static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r); static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host); -static bool update_user_table(THD *, const User_table &, const char *, const char *, const - char *, size_t new_password_len); +static bool update_user_table_password(THD *, const User_table&, + const ACL_USER &); static bool acl_load(THD *thd, const Grant_tables& grant_tables); static inline void get_grantor(THD *thd, char* grantor); static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname); @@ -1306,9 +1304,9 @@ void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem) { init(get_field(mem, proxies_priv_table.host()), - get_field(mem, proxies_priv_table.user()), + safe_str(get_field(mem, proxies_priv_table.user())), get_field(mem, proxies_priv_table.proxied_host()), - get_field(mem, proxies_priv_table.proxied_user()), + safe_str(get_field(mem, proxies_priv_table.proxied_user())), proxies_priv_table.with_grant()->val_int() != 0); } @@ -1337,8 +1335,7 @@ ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0) access= user->access; /* set initial role access the same as the table row privileges */ initial_role_access= user->access; - this->user.str= safe_strdup_root(root, user->user.str); - this->user.length= user->user.length; + this->user= user->user; bzero(&role_grants, sizeof(role_grants)); bzero(&parent_grantee, sizeof(parent_grantee)); flags= IS_ROLE; @@ -1388,7 +1385,7 @@ static bool has_validation_plugins() MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); } -struct validation_data { LEX_CSTRING *user, *password; }; +struct validation_data { const LEX_CSTRING *user, *password; }; static my_bool do_validate(THD *, plugin_ref plugin, void *arg) { @@ -1399,13 +1396,13 @@ static my_bool do_validate(THD *, plugin_ref plugin, void *arg) } -static bool validate_password(LEX_USER *user, THD *thd) +static bool validate_password(THD *thd, const LEX_CSTRING &user, + const LEX_CSTRING &pwtext, bool has_hash) { - if (user->pwtext.length || !user->pwhash.length) + if (pwtext.length || !has_hash) { - struct validation_data data= { &user->user, - user->pwtext.str ? &user->pwtext : - const_cast<LEX_CSTRING *>(&empty_clex_str) }; + struct validation_data data= { &user, + pwtext.str ? &pwtext : &empty_clex_str }; if (plugin_foreach(NULL, do_validate, MariaDB_PASSWORD_VALIDATION_PLUGIN, &data)) { @@ -1426,45 +1423,87 @@ static bool validate_password(LEX_USER *user, THD *thd) } /** - Convert scrambled password to binary form, according to scramble type, - Binary form is stored in user.salt. - - @param acl_user The object where to store the salt - @param password The password hash containing the salt - @param password_len The length of the password hash - - Despite the name of the function it is used when loading ACLs from disk - to store the password hash in the ACL_USER object. -*/ + Fills in ACL_USER::auth_string and ACL_USER::salt fields, as needed -static void -set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len) + hashes the plain-text password (if provided) to auth_string, + converts auth_string to salt. + + Fails if the plain-text password fails validation, if the plugin is + not loaded, if the auth_string is invalid. + + Using NULL for a password disables validation + (needed for loading from mysql.user table). +*/ +static int set_user_auth(THD *thd, ACL_USER *acl_user, const LEX_CSTRING *pwtext) { - if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH) + const char *plugin_name= acl_user->plugin.str; + bool unlock_plugin= false; + plugin_ref plugin= get_auth_plugin(thd, acl_user->plugin, &unlock_plugin); + int res= 1; + + if (!plugin) { - get_salt_from_password(acl_user->salt, password); - acl_user->salt_len= SCRAMBLE_LENGTH; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_PLUGIN_IS_NOT_LOADED, + ER_THD(thd, ER_PLUGIN_IS_NOT_LOADED), plugin_name); + return res; } - else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + + acl_user->salt= acl_user->auth_string; + + st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info; + if (auth->interface_version >= 0x0202) { - get_salt_from_password_323((ulong *) acl_user->salt, password); - acl_user->salt_len= SCRAMBLE_LENGTH_323; + if (pwtext) + { + if (auth->hash_password && + validate_password(thd, acl_user->user, *pwtext, + acl_user->auth_string.length)) + goto end; + if (pwtext->length) + { + if (auth->hash_password) + { + char buf[MAX_SCRAMBLE_LENGTH]; + size_t len= sizeof(buf) - 1; + if (auth->hash_password(pwtext->str, pwtext->length, buf, &len)) + goto end; // OOM? + buf[len] = 0; + acl_user->auth_string.str= (char*)memdup_root(&acl_memroot, buf, len+1); + acl_user->auth_string.length= len; + } + else + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_SET_PASSWORD_AUTH_PLUGIN, + ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN), + acl_user->plugin.str); + } + } + } + + if (acl_user->auth_string.length) + { + if (auth->preprocess_hash) + { + uchar buf[MAX_SCRAMBLE_LENGTH]; + size_t len= sizeof(buf); + if (auth->preprocess_hash(acl_user->auth_string.str, + acl_user->auth_string.length, buf, &len)) + goto end; // ER_PASSWD_LENGTH? + acl_user->salt.str= (char*)memdup_root(&acl_memroot, buf, len); + acl_user->salt.length= len; + } + else + acl_user->salt= acl_user->auth_string; + } } - else - acl_user->salt_len= 0; -} -static const char *fix_plugin_ptr(const char *name) -{ - if (my_strcasecmp(system_charset_info, name, - native_password_plugin_name.str) == 0) - return native_password_plugin_name.str; - else - if (my_strcasecmp(system_charset_info, name, - old_password_plugin_name.str) == 0) - return old_password_plugin_name.str; - else - return name; + res= 0; +end: + if (unlock_plugin) + plugin_unlock(thd, plugin); + return res; } /** @@ -1475,8 +1514,6 @@ static const char *fix_plugin_ptr(const char *name) authentication, we want to be able to detect built-ins by comparing pointers, not strings. - Additionally - update the salt if the plugin is built-in. - @retval 0 the pointers were fixed @retval 1 this ACL_USER uses a not built-in plugin */ @@ -1489,98 +1526,6 @@ static bool fix_user_plugin_ptr(ACL_USER *user) user->plugin= old_password_plugin_name; else return true; - - if (user->auth_string.length) - set_user_salt(user, user->auth_string.str, user->auth_string.length); - return false; -} - - -/* - Validates the password, calculates password hash, transforms - equivalent LEX_USER representations. - - Upon entering this function: - - - if user->plugin is specified, user->auth is the plugin auth data. - - if user->plugin is mysql_native_password or mysql_old_password, - user->auth is the password hash, and LEX_USER is transformed - to match the next case (that is, user->plugin is cleared). - - if user->plugin is NOT specified, built-in auth is assumed, that is - mysql_native_password or mysql_old_password. In that case, - user->pwhash is the password hash. And user->pwtext is the original - plain-text password. Either one can be set or both. - - Upon exiting this function: - - - user->pwtext is left untouched - - user->pwhash is the password hash, as the mysql.user.password column - - user->plugin is the plugin name, as the mysql.user.plugin column - - user->auth is the plugin auth data, as the mysql.user.authentication_string column -*/ -static bool fix_lex_user(THD *thd, LEX_USER *user) -{ - size_t check_length; - - DBUG_ASSERT(user->plugin.length || !user->auth.length); - DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length))); - - if (lex_string_eq(&user->plugin, &native_password_plugin_name)) - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - else - if (lex_string_eq(&user->plugin, &old_password_plugin_name)) - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - else - if (user->plugin.length) - return false; // nothing else to do - else if (thd->variables.old_passwords == 1 || - user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - else - check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - - if (user->plugin.length) - { - user->pwhash= user->auth; - user->plugin= empty_clex_str; - user->auth= empty_clex_str; - } - - if (user->pwhash.length && user->pwhash.length != check_length) - { - my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length); - return true; - } - - if (user->pwtext.length && !user->pwhash.length) - { - size_t scramble_length; - void (*make_scramble)(char *, const char *, size_t); - - if (thd->variables.old_passwords == 1) - { - scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; - make_scramble= my_make_scrambled_password_323; - } - else - { - scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - make_scramble= my_make_scrambled_password; - } - - Query_arena *arena, backup; - arena= thd->activate_stmt_arena_if_needed(&backup); - char *buff= (char *) thd->alloc(scramble_length + 1); - if (arena) - thd->restore_active_arena(arena, &backup); - - if (buff == NULL) - return true; - make_scramble(buff, user->pwtext.str, user->pwtext.length); - user->pwhash.str= buff; - user->pwhash.length= scramble_length; - } - return false; } @@ -1661,23 +1606,22 @@ bool acl_init(bool dont_read_acl_tables) Choose from either native or old password plugins when assigning a password */ -static bool set_user_plugin (ACL_USER *user, size_t password_len) +static LEX_CSTRING &guess_auth_plugin(THD *thd, size_t password_len) { - switch (password_len) - { - case 0: /* no password */ - case SCRAMBLED_PASSWORD_CHAR_LENGTH: - user->plugin= native_password_plugin_name; - return FALSE; - case SCRAMBLED_PASSWORD_CHAR_LENGTH_323: - user->plugin= old_password_plugin_name; - return FALSE; - default: - sql_print_warning("Found invalid password for user: '%s@%s'; " - "Ignoring user", safe_str(user->user.str), - safe_str(user->host.hostname)); - return TRUE; - } + if (thd->variables.old_passwords == 1 || + password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + return old_password_plugin_name; + else + return native_password_plugin_name; +} + + +static void push_new_user(const ACL_USER &user) +{ + push_dynamic(&acl_users, &user); + if (!user.host.hostname || + (user.host.hostname[0] == wild_many && !user.host.hostname[1])) + allow_all_hosts=1; // Anyone can connect } @@ -1702,7 +1646,6 @@ static bool acl_load(THD *thd, const Grant_tables& tables) READ_RECORD read_record_info; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; char tmp_name[SAFE_NAME_LEN+1]; - int password_length; Sql_mode_save old_mode_save(thd); DBUG_ENTER("acl_load"); @@ -1775,8 +1718,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables) USERNAME_CHAR_LENGTH); if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+) { - password_length= user_table.password()->field_length / - user_table.password()->charset()->mbmaxlen; + int password_length= user_table.password()->field_length / + user_table.password()->charset()->mbmaxlen; if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { sql_print_error("Fatal error: mysql.user table is damaged or in " @@ -1826,9 +1769,9 @@ static bool acl_load(THD *thd, const Grant_tables& tables) bool is_role= FALSE; bzero(&user, sizeof(user)); update_hostname(&user.host, get_field(&acl_memroot, user_table.host())); - char *username= get_field(&acl_memroot, user_table.user()); + char *username= safe_str(get_field(&acl_memroot, user_table.user())); user.user.str= username; - user.user.length= safe_strlen(username); + user.user.length= strlen(username); /* If the user entry is a role, skip password and hostname checks @@ -1846,186 +1789,189 @@ static bool acl_load(THD *thd, const Grant_tables& tables) hostname_requires_resolving(user.host.hostname)) { sql_print_warning("'user' entry '%s@%s' " - "ignored in --skip-name-resolve mode.", - safe_str(user.user.str), + "ignored in --skip-name-resolve mode.", user.user.str, safe_str(user.host.hostname)); continue; } - char *password= const_cast<char*>(""); if (user_table.password()) - password= get_field(&acl_memroot, user_table.password()); - size_t password_len= safe_strlen(password); - user.auth_string.str= safe_str(password); - user.auth_string.length= password_len; - set_user_salt(&user, password, password_len); + { + const char *p= safe_str(get_field(&acl_memroot, user_table.password())); + user.auth_string.str= p; + user.auth_string.length= strlen(p); + } + else + user.auth_string= empty_clex_str; - if (!is_role && set_user_plugin(&user, password_len)) - continue; + user.plugin= guess_auth_plugin(thd, user.auth_string.length); - { - user.access= user_table.get_access() & GLOBAL_ACLS; - /* - if it is pre 5.0.1 privilege table then map CREATE privilege on - CREATE VIEW & SHOW VIEW privileges - */ - if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL)) - user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); + user.access= user_table.get_access() & GLOBAL_ACLS; + /* + if it is pre 5.0.1 privilege table then map CREATE privilege on + CREATE VIEW & SHOW VIEW privileges + */ + if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL)) + user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); - /* - if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on - CREATE PROCEDURE & ALTER PROCEDURE privileges - */ - if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL)) - user.access|= CREATE_PROC_ACL; - if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL)) - user.access|= ALTER_PROC_ACL; + /* + if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on + CREATE PROCEDURE & ALTER PROCEDURE privileges + */ + if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL)) + user.access|= CREATE_PROC_ACL; + if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL)) + user.access|= ALTER_PROC_ACL; - /* - pre 5.0.3 did not have CREATE_USER_ACL - */ - if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL)) - user.access|= CREATE_USER_ACL; + /* + pre 5.0.3 did not have CREATE_USER_ACL + */ + if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL)) + user.access|= CREATE_USER_ACL; - /* - if it is pre 5.1.6 privilege table then map CREATE privilege on - CREATE|ALTER|DROP|EXECUTE EVENT - */ - if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL)) - user.access|= EVENT_ACL; + /* + if it is pre 5.1.6 privilege table then map CREATE privilege on + CREATE|ALTER|DROP|EXECUTE EVENT + */ + if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL)) + user.access|= EVENT_ACL; - /* - if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. - */ - if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) - user.access|= TRIGGER_ACL; + /* + if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. + */ + if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) + user.access|= TRIGGER_ACL; - if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL)) - user.access|= DELETE_HISTORY_ACL; + if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL)) + user.access|= DELETE_HISTORY_ACL; - user.sort= get_sort(2, user.host.hostname, user.user.str); - user.hostname_length= safe_strlen(user.host.hostname); - user.user_resource.user_conn= 0; - user.user_resource.max_statement_time= 0.0; + user.sort= get_sort(2, user.host.hostname, user.user.str); + user.hostname_length= safe_strlen(user.host.hostname); + user.user_resource.user_conn= 0; + user.user_resource.max_statement_time= 0.0; - /* Starting from 4.0.2 we have more fields */ - if (user_table.ssl_type()) + /* Starting from 4.0.2 we have more fields */ + if (user_table.ssl_type()) + { + char *ssl_type=get_field(thd->mem_root, user_table.ssl_type()); + if (!ssl_type) + user.ssl_type=SSL_TYPE_NONE; + else if (!strcmp(ssl_type, "ANY")) + user.ssl_type=SSL_TYPE_ANY; + else if (!strcmp(ssl_type, "X509")) + user.ssl_type=SSL_TYPE_X509; + else /* !strcmp(ssl_type, "SPECIFIED") */ + user.ssl_type=SSL_TYPE_SPECIFIED; + + user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher()); + user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer()); + user.x509_subject= get_field(&acl_memroot, user_table.x509_subject()); + + char *ptr = get_field(thd->mem_root, user_table.max_questions()); + user.user_resource.questions=ptr ? atoi(ptr) : 0; + ptr = get_field(thd->mem_root, user_table.max_updates()); + user.user_resource.updates=ptr ? atoi(ptr) : 0; + ptr = get_field(thd->mem_root, user_table.max_connections()); + user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0; + if (user.user_resource.questions || user.user_resource.updates || + user.user_resource.conn_per_hour) + mqh_used=1; + + if (user_table.max_user_connections()) { - char *ssl_type=get_field(thd->mem_root, user_table.ssl_type()); - if (!ssl_type) - user.ssl_type=SSL_TYPE_NONE; - else if (!strcmp(ssl_type, "ANY")) - user.ssl_type=SSL_TYPE_ANY; - else if (!strcmp(ssl_type, "X509")) - user.ssl_type=SSL_TYPE_X509; - else /* !strcmp(ssl_type, "SPECIFIED") */ - user.ssl_type=SSL_TYPE_SPECIFIED; - - user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher()); - user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer()); - user.x509_subject= get_field(&acl_memroot, user_table.x509_subject()); - - char *ptr = get_field(thd->mem_root, user_table.max_questions()); - user.user_resource.questions=ptr ? atoi(ptr) : 0; - ptr = get_field(thd->mem_root, user_table.max_updates()); - user.user_resource.updates=ptr ? atoi(ptr) : 0; - ptr = get_field(thd->mem_root, user_table.max_connections()); - user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0; - if (user.user_resource.questions || user.user_resource.updates || - user.user_resource.conn_per_hour) - mqh_used=1; - - if (user_table.max_user_connections()) - { - /* Starting from 5.0.3 we have max_user_connections field */ - ptr= get_field(thd->mem_root, user_table.max_user_connections()); - user.user_resource.user_conn= ptr ? atoi(ptr) : 0; - } + /* Starting from 5.0.3 we have max_user_connections field */ + ptr= get_field(thd->mem_root, user_table.max_user_connections()); + user.user_resource.user_conn= ptr ? atoi(ptr) : 0; + } - if (!is_role && user_table.plugin()) + if (!is_role && user_table.plugin()) + { + /* We may have plugin & auth_string fields */ + char *tmpstr= get_field(&acl_memroot, user_table.plugin()); + if (tmpstr) { - /* We may have plugin & auth_String fields */ - char *tmpstr= get_field(&acl_memroot, user_table.plugin()); - if (tmpstr) + LEX_CSTRING password= user.auth_string; + user.plugin.str= tmpstr; + user.plugin.length= strlen(user.plugin.str); + user.auth_string.str= + safe_str(get_field(&acl_memroot, + user_table.authentication_string())); + user.auth_string.length= strlen(user.auth_string.str); + + if (password.length) { - user.plugin.str= tmpstr; - user.plugin.length= strlen(user.plugin.str); - user.auth_string.str= - safe_str(get_field(&acl_memroot, - user_table.authentication_string())); - user.auth_string.length= strlen(user.auth_string.str); - - if (user.auth_string.length && password_len) + if (user.auth_string.length && + (user.auth_string.length != password.length || + memcmp(user.auth_string.str, password.str, password.length))) { sql_print_warning("'user' entry '%s@%s' has both a password " "and an authentication plugin specified. The " "password will be ignored.", - safe_str(user.user.str), - safe_str(user.host.hostname)); + user.user.str, safe_str(user.host.hostname)); } - - fix_user_plugin_ptr(&user); + else + user.auth_string= password; } - } - if (user_table.max_statement_time()) - { - /* Starting from 10.1.1 we can have max_statement_time */ - ptr= get_field(thd->mem_root, - user_table.max_statement_time()); - user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0; + fix_user_plugin_ptr(&user); } } - else + + if (user_table.max_statement_time()) { - user.ssl_type=SSL_TYPE_NONE; + /* Starting from 10.1.1 we can have max_statement_time */ + ptr= get_field(thd->mem_root, + user_table.max_statement_time()); + user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0; + } + } + else + { + user.ssl_type=SSL_TYPE_NONE; #ifndef TO_BE_REMOVED - if (user_table.num_fields() <= 13) - { // Without grant - if (user.access & CREATE_ACL) - user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; - } - /* Convert old privileges */ - user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; - if (user.access & FILE_ACL) - user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; - if (user.access & PROCESS_ACL) - user.access|= SUPER_ACL | EXECUTE_ACL; -#endif + if (user_table.num_fields() <= 13) + { // Without grant + if (user.access & CREATE_ACL) + user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; } + /* Convert old privileges */ + user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; + if (user.access & FILE_ACL) + user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; + if (user.access & PROCESS_ACL) + user.access|= SUPER_ACL | EXECUTE_ACL; +#endif + } - (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *), - 8, 8, MYF(0)); + my_init_dynamic_array(&user.role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0)); - /* check default role, if any */ - if (!is_role && user_table.default_role()) - { - user.default_rolename.str= - get_field(&acl_memroot, user_table.default_role()); - user.default_rolename.length= safe_strlen(user.default_rolename.str); - } + /* check default role, if any */ + if (!is_role && user_table.default_role()) + { + user.default_rolename.str= + get_field(&acl_memroot, user_table.default_role()); + user.default_rolename.length= safe_strlen(user.default_rolename.str); + } - if (is_role) - { - DBUG_PRINT("info", ("Found role %s", user.user.str)); - ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot); - entry->role_grants = user.role_grants; - (void) my_init_dynamic_array(&entry->parent_grantee, - sizeof(ACL_USER_BASE *), 8, 8, MYF(0)); - my_hash_insert(&acl_roles, (uchar *)entry); + if (set_user_auth(thd, &user, NULL)) + { + thd->clear_error(); // the warning is still issued + continue; + } - continue; - } - else - { - DBUG_PRINT("info", ("Found user %s", user.user.str)); - (void) push_dynamic(&acl_users,(uchar*) &user); - } - if (!user.host.hostname || - (user.host.hostname[0] == wild_many && !user.host.hostname[1])) - allow_all_hosts=1; // Anyone can connect + if (is_role) + { + DBUG_PRINT("info", ("Found role %s", user.user.str)); + ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot); + entry->role_grants = user.role_grants; + my_init_dynamic_array(&entry->parent_grantee, + sizeof(ACL_USER_BASE *), 0, 8, MYF(0)); + my_hash_insert(&acl_roles, (uchar *)entry); + + continue; } + DBUG_PRINT("info", ("Found user %s", user.user.str)); + push_new_user(user); } my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, sizeof(ACL_USER),(qsort_cmp) acl_compare); @@ -2039,7 +1985,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { ACL_DB db; char *db_name; - db.user=get_field(&acl_memroot, db_table.user()); + db.user=safe_str(get_field(&acl_memroot, db_table.user())); const char *hostname= get_field(&acl_memroot, db_table.host()); if (!hostname && find_acl_role(db.user)) hostname= ""; @@ -2054,7 +2000,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { sql_print_warning("'db' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - db.db, safe_str(db.user), safe_str(db.host.hostname)); + db.db, db.user, safe_str(db.host.hostname)); continue; } db.access= db_table.get_access(); @@ -2079,7 +2025,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) "case that has been forced to lowercase because " "lower_case_table_names is set. It will not be " "possible to remove this privilege using REVOKE.", - db.db, safe_str(db.user), safe_str(db.host.hostname)); + db.db, db.user, safe_str(db.host.hostname)); } } db.sort=get_sort(3,db.host.hostname,db.db,db.user); @@ -2407,7 +2353,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'", host, ip, user, db)); - sctx->user= user; + sctx->user= *user ? user : NULL; sctx->host= host; sctx->ip= ip; sctx->host_or_ip= host ? host : (safe_str(ip)); @@ -2437,8 +2383,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, for (i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); - if (!acl_db->user || - (user && user[0] && !strcmp(user, acl_db->user))) + if (!*acl_db->user || !strcmp(user, acl_db->user)) { if (compare_hostname(&acl_db->host, host, ip)) { @@ -2452,8 +2397,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, } sctx->master_access= acl_user->access; - if (acl_user->user.str) - strmake_buf(sctx->priv_user, user); + strmake_buf(sctx->priv_user, user); if (acl_user->host.hostname) strmake_buf(sctx->priv_host, acl_user->host.hostname); @@ -2468,8 +2412,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, for (i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); - if (!acl_db->user || - (user && user[0] && !strcmp(user, acl_db->user))) + if (!*acl_db->user || !strcmp(user, acl_db->user)) { if (compare_hostname(&acl_db->host, "", "")) { @@ -2483,8 +2426,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, } sctx->master_access= acl_role->access; - if (acl_role->user.str) - strmake_buf(sctx->priv_role, user); + strmake_buf(sctx->priv_role, user); } } @@ -2607,66 +2549,55 @@ static void acl_update_role(const char *rolename, ulong privileges) } -static void acl_update_user(const char *user, const char *host, - const char *password, size_t password_len, - enum SSL_type ssl_type, - const char *ssl_cipher, - const char *x509_issuer, - const char *x509_subject, - USER_RESOURCES *mqh, - ulong privileges, - const LEX_CSTRING *plugin, - const LEX_CSTRING *auth) +static int acl_user_update(THD *thd, ACL_USER *acl_user, const ACL_USER *from, + const LEX_USER &combo, enum SSL_type ssl_type, + const char *ssl_cipher, const char *x509_issuer, + const char *x509_subject, const USER_RESOURCES *mqh, + ulong privileges) { - mysql_mutex_assert_owner(&acl_cache->lock); + if (from) + *acl_user= *from; + else + { + bzero(acl_user, sizeof(*acl_user)); + acl_user->user= safe_lexcstrdup_root(&acl_memroot, combo.user); + update_hostname(&acl_user->host, safe_strdup_root(&acl_memroot, combo.host.str)); + acl_user->hostname_length= combo.host.length; + acl_user->sort= get_sort(2, acl_user->host.hostname, acl_user->user.str); + acl_user->plugin= native_password_plugin_name; + acl_user->salt= acl_user->auth_string= empty_clex_str; + my_init_dynamic_array(&acl_user->role_grants, sizeof(ACL_USER *), + 0, 8, MYF(0)); + } - for (uint i=0 ; i < acl_users.elements ; i++) + if (combo.plugin.length) { - ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); - if (acl_user->eq(user, host)) - { - if (plugin->str[0]) - { - acl_user->plugin= *plugin; - acl_user->auth_string.str= auth->str ? - strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>(""); - acl_user->auth_string.length= auth->length; - if (fix_user_plugin_ptr(acl_user)) - acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length); - } - else - if (password[0]) - { - acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len); - acl_user->auth_string.length= password_len; - set_user_salt(acl_user, password, password_len); - set_user_plugin(acl_user, password_len); - } - acl_user->access=privileges; - if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) - acl_user->user_resource.questions=mqh->questions; - if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) - acl_user->user_resource.updates=mqh->updates; - if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) - acl_user->user_resource.conn_per_hour= mqh->conn_per_hour; - if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS) - acl_user->user_resource.user_conn= mqh->user_conn; - if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) - acl_user->user_resource.max_statement_time= mqh->max_statement_time; - if (ssl_type != SSL_TYPE_NOT_SPECIFIED) - { - acl_user->ssl_type= ssl_type; - acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : - 0); - acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : - 0); - acl_user->x509_subject= (x509_subject ? - strdup_root(&acl_memroot,x509_subject) : 0); - } - /* search complete: */ - break; - } + acl_user->plugin= combo.plugin; + acl_user->auth_string= safe_lexcstrdup_root(&acl_memroot, combo.auth); + if (fix_user_plugin_ptr(acl_user)) + acl_user->plugin= safe_lexcstrdup_root(&acl_memroot, combo.plugin); + if (set_user_auth(thd, acl_user, &combo.pwtext)) + return 1; + } + acl_user->access= privileges; + if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) + acl_user->user_resource.questions= mqh->questions; + if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) + acl_user->user_resource.updates= mqh->updates; + if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) + acl_user->user_resource.conn_per_hour= mqh->conn_per_hour; + if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS) + acl_user->user_resource.user_conn= mqh->user_conn; + if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) + acl_user->user_resource.max_statement_time= mqh->max_statement_time; + if (ssl_type != SSL_TYPE_NOT_SPECIFIED) + { + acl_user->ssl_type= ssl_type; + acl_user->ssl_cipher= safe_strdup_root(&acl_memroot, ssl_cipher); + acl_user->x509_issuer= safe_strdup_root(&acl_memroot,x509_issuer); + acl_user->x509_subject= safe_strdup_root(&acl_memroot,x509_subject); } + return 0; } @@ -2676,82 +2607,14 @@ static void acl_insert_role(const char *rolename, ulong privileges) mysql_mutex_assert_owner(&acl_cache->lock); entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot); - (void) my_init_dynamic_array(&entry->parent_grantee, - sizeof(ACL_USER_BASE *), 8, 8, MYF(0)); - (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *), - 8, 8, MYF(0)); + my_init_dynamic_array(&entry->parent_grantee, + sizeof(ACL_USER_BASE *), 0, 8, MYF(0)); + my_init_dynamic_array(&entry->role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0)); my_hash_insert(&acl_roles, (uchar *)entry); } -static void acl_insert_user(const char *user, const char *host, - const char *password, size_t password_len, - enum SSL_type ssl_type, - const char *ssl_cipher, - const char *x509_issuer, - const char *x509_subject, - USER_RESOURCES *mqh, - ulong privileges, - const LEX_CSTRING *plugin, - const LEX_CSTRING *auth) -{ - ACL_USER acl_user; - - mysql_mutex_assert_owner(&acl_cache->lock); - - bzero(&acl_user, sizeof(acl_user)); - acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0; - acl_user.user.length= strlen(user); - update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host)); - if (plugin->str[0]) - { - acl_user.plugin= *plugin; - acl_user.auth_string.str= auth->str ? - strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>(""); - acl_user.auth_string.length= auth->length; - if (fix_user_plugin_ptr(&acl_user)) - acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length); - } - else - { - acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len); - acl_user.auth_string.length= password_len; - set_user_salt(&acl_user, password, password_len); - set_user_plugin(&acl_user, password_len); - } - - acl_user.flags= 0; - acl_user.access=privileges; - acl_user.user_resource = *mqh; - acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str); - acl_user.hostname_length=(uint) strlen(host); - acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ? - ssl_type : SSL_TYPE_NONE); - acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0; - acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0; - acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0; - (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *), - 8, 8, MYF(0)); - - (void) push_dynamic(&acl_users,(uchar*) &acl_user); - if (!acl_user.host.hostname || - (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) - allow_all_hosts=1; // Anyone can connect /* purecov: tested */ - my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, - sizeof(ACL_USER),(qsort_cmp) acl_compare); - - /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ - rebuild_check_host(); - - /* - Rebuild every user's role_grants since 'acl_users' has been sorted - and old pointers to ACL_USER elements are no longer valid - */ - rebuild_role_grants(); -} - - static bool acl_update_db(const char *user, const char *host, const char *db, ulong privileges) { @@ -2762,13 +2625,10 @@ static bool acl_update_db(const char *user, const char *host, const char *db, for (uint i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); - if ((!acl_db->user && !user[0]) || - (acl_db->user && - !strcmp(user,acl_db->user))) + if (!strcmp(user, acl_db->user)) { if ((!acl_db->host.hostname && !host[0]) || - (acl_db->host.hostname && - !strcmp(host, acl_db->host.hostname))) + (acl_db->host.hostname && !strcmp(host, acl_db->host.hostname))) { if ((!acl_db->db && !db[0]) || (acl_db->db && !strcmp(db,acl_db->db))) @@ -2866,7 +2726,7 @@ ulong acl_get(const char *host, const char *ip, for (i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); - if (!acl_db->user || !strcmp(user,acl_db->user)) + if (!*acl_db->user || !strcmp(user, acl_db->user)) { if (compare_hostname(&acl_db->host,host,ip)) { @@ -3223,8 +3083,7 @@ bool check_change_password(THD *thd, LEX_USER *user) { LEX_USER *real_user= get_current_user(thd, user); - if (fix_and_copy_user(real_user, user, thd) || - validate_password(real_user, thd)) + if (fix_and_copy_user(real_user, user, thd)) return true; *user= *real_user; @@ -3254,8 +3113,8 @@ bool change_password(THD *thd, LEX_USER *user) const CSET_STRING query_save __attribute__((unused)) = thd->query_string; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", - user->host.str, user->user.str, user->pwhash.str)); - DBUG_ASSERT(user->host.str != 0); // Ensured by parent + user->host.str, user->user.str, user->auth.str)); + DBUG_ASSERT(user->host.str != 0); // Ensured by caller /* This statement will be replicated as a statement, even when using @@ -3266,19 +3125,8 @@ bool change_password(THD *thd, LEX_USER *user) */ save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); - if (mysql_bin_log.is_open() || - (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))) - { - query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", - safe_str(user->user.str), safe_str(user->host.str), - safe_str(user->pwhash.str)); - } - if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) - { - thd->set_query(buff, query_length, system_charset_info); WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); - } if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); @@ -3295,25 +3143,21 @@ bool change_password(THD *thd, LEX_USER *user) goto end; } - /* update loaded acl entry: */ if (acl_user->plugin.str == native_password_plugin_name.str || acl_user->plugin.str == old_password_plugin_name.str) { - acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length); - acl_user->auth_string.length= user->pwhash.length; - set_user_salt(acl_user, user->pwhash.str, user->pwhash.length); + /* historical hack of auto-changing the plugin */ + acl_user->plugin= guess_auth_plugin(thd, user->auth.length); + } - set_user_plugin(acl_user, user->pwhash.length); + acl_user->auth_string= safe_lexcstrdup_root(&acl_memroot, user->auth); + if (set_user_auth(thd, acl_user, &user->pwtext)) + { + mysql_mutex_unlock(&acl_cache->lock); + goto end; } - else - push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_SET_PASSWORD_AUTH_PLUGIN, - ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN)); - if (update_user_table(thd, tables.user_table(), - safe_str(acl_user->host.hostname), - safe_str(acl_user->user.str), - user->pwhash.str, user->pwhash.length)) + if (update_user_table_password(thd, tables.user_table(), *acl_user)) { mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */ goto end; @@ -3324,6 +3168,8 @@ bool change_password(THD *thd, LEX_USER *user) result= 0; if (mysql_bin_log.is_open()) { + query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", + user->user.str, safe_str(user->host.str), acl_user->auth_string.str); DBUG_ASSERT(query_length); thd->clear_error(); result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, @@ -3333,7 +3179,7 @@ end: close_mysql_tables(thd); #ifdef WITH_WSREP -error: // this label is used in WSREP_TO_ISOLATION_BEGIN +wsrep_error_label: if (WSREP(thd) && !thd->wsrep_applier) { WSREP_TO_ISOLATION_END; @@ -3362,12 +3208,12 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, ulong query_length= 0; bool clear_role= FALSE; char buff[512]; - enum_binlog_format save_binlog_format; + enum_binlog_format save_binlog_format= thd->get_current_stmt_binlog_format(); const CSET_STRING query_save __attribute__((unused)) = thd->query_string; DBUG_ENTER("acl_set_default_role"); DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'", - safe_str(user), safe_str(host), safe_str(rolename))); + user, safe_str(host), safe_str(rolename))); if (rolename == current_role.str) { if (!thd->security_ctx->priv_role[0]) @@ -3387,7 +3233,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, { query_length= sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'", - safe_str(rolename), safe_str(user), safe_str(host)); + safe_str(rolename), user, safe_str(host)); } /* @@ -3402,6 +3248,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) { thd->set_query(buff, query_length, system_charset_info); + // Attention!!! here is implicit goto error; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); } @@ -3494,7 +3341,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, } #ifdef WITH_WSREP -error: // this label is used in WSREP_TO_ISOLATION_END +wsrep_error_label: if (WSREP(thd) && !thd->wsrep_applier) { WSREP_TO_ISOLATION_END; @@ -3555,7 +3402,7 @@ static ACL_USER *find_user_or_anon(const char *host, const char *user, const cha for (uint i=0; i < acl_users.elements; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); - if ((!acl_user_tmp->user.str || + if ((!acl_user_tmp->user.length || !strcmp(user, acl_user_tmp->user.str)) && compare_hostname(&acl_user_tmp->host, host, ip)) { @@ -3570,13 +3417,13 @@ static ACL_USER *find_user_or_anon(const char *host, const char *user, const cha /* Find first entry that matches the specified user@host pair */ -static ACL_USER * find_user_exact(const char *host, const char *user) +static ACL_USER *find_user_exact(const char *host, const char *user) { mysql_mutex_assert_owner(&acl_cache->lock); for (uint i=0 ; i < acl_users.elements ; i++) { - ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); + ACL_USER *acl_user=dynamic_element(&acl_users, i, ACL_USER*); if (acl_user->eq(user, host)) return acl_user; } @@ -3611,7 +3458,7 @@ static ACL_ROLE *find_acl_role(const char *role) mysql_mutex_assert_owner(&acl_cache->lock); ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role, - safe_strlen(role)); + strlen(role)); DBUG_RETURN(r); } @@ -3763,53 +3610,24 @@ bool hostname_requires_resolving(const char *hostname) } -void set_authentication_plugin_from_password(const User_table& user_table, - const char* password, size_t password_length) -{ - if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH || - password_length == 0) - { - user_table.plugin()->store(native_password_plugin_name.str, - native_password_plugin_name.length, - system_charset_info); - } - else - { - DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323); - user_table.plugin()->store(old_password_plugin_name.str, - old_password_plugin_name.length, - system_charset_info); - } - user_table.authentication_string()->store(password, - password_length, - system_charset_info); -} /** Update record for user in mysql.user privilege table with new password. - @param thd THD - @param table Pointer to TABLE object for open mysql.user table - @param host Hostname - @param user Username - @param new_password New password hash - @param new_password_len Length of new password hash - @see change_password */ -static bool update_user_table(THD *thd, const User_table& user_table, - const char *host, const char *user, - const char *new_password, size_t new_password_len) +static bool update_user_table_password(THD *thd, const User_table& user_table, + const ACL_USER &user) { + CHARSET_INFO *cs= system_charset_info; char user_key[MAX_KEY_LENGTH]; int error; - DBUG_ENTER("update_user_table"); - DBUG_PRINT("enter",("user: %s host: %s",user,host)); + DBUG_ENTER("update_user_table_password"); TABLE *table= user_table.table(); table->use_all_columns(); - user_table.host()->store(host,(uint) strlen(host), system_charset_info); - user_table.user()->store(user,(uint) strlen(user), system_charset_info); + user_table.host()->store(user.host.hostname, user.hostname_length, cs); + user_table.user()->store(user.user.str, user.user.length, cs); key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); @@ -3825,14 +3643,13 @@ static bool update_user_table(THD *thd, const User_table& user_table, if (user_table.plugin()) { - set_authentication_plugin_from_password(user_table, new_password, - new_password_len); - new_password_len= 0; + if (user_table.password()) + user_table.password()->reset(); + user_table.plugin()->store(user.plugin.str, user.plugin.length, cs); + user_table.authentication_string()->store(user.auth_string.str, user.auth_string.length, cs); } - - if (user_table.password()) - user_table.password()->store(new_password, new_password_len, system_charset_info); - + else + user_table.password()->store(user.auth_string.str, user.auth_string.length, cs); if (unlikely(error= table->file->ha_update_row(table->record[1], table->record[0])) && @@ -3884,7 +3701,7 @@ static bool test_if_create_new_users(THD *thd) ****************************************************************************/ static int replace_user_table(THD *thd, const User_table &user_table, - LEX_USER &combo, + LEX_USER *combo, ulong rights, bool revoke_grant, bool can_create_user, bool no_auto_create) { @@ -3892,26 +3709,14 @@ static int replace_user_table(THD *thd, const User_table &user_table, bool old_row_exists=0; char what= (revoke_grant) ? 'N' : 'Y'; uchar user_key[MAX_KEY_LENGTH]; - bool handle_as_role= combo.is_role(); + bool handle_as_role= combo->is_role(); LEX *lex= thd->lex; TABLE *table= user_table.table(); + ACL_USER new_acl_user, *old_acl_user; DBUG_ENTER("replace_user_table"); mysql_mutex_assert_owner(&acl_cache->lock); - if (combo.pwhash.str && combo.pwhash.str[0]) - { - if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH && - combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) - { - DBUG_ASSERT(0); - my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); - DBUG_RETURN(-1); - } - } - else - combo.pwhash= empty_clex_str; - /* if the user table is not up to date, we can't handle role updates */ if (!user_table.is_role() && handle_as_role) { @@ -3922,9 +3727,9 @@ static int replace_user_table(THD *thd, const User_table &user_table, } table->use_all_columns(); - user_table.host()->store(combo.host.str,combo.host.length, + user_table.host()->store(combo->host.str,combo->host.length, system_charset_info); - user_table.user()->store(combo.user.str,combo.user.length, + user_table.user()->store(combo->user.str,combo->user.length, system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); @@ -3936,7 +3741,7 @@ static int replace_user_table(THD *thd, const User_table &user_table, /* what == 'N' means revoke */ if (what == 'N') { - my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); + my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str, combo->host.str); goto end; } /* @@ -3952,7 +3757,8 @@ static int replace_user_table(THD *thd, const User_table &user_table, see also test_if_create_new_users() */ - else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create) + else if (!combo->auth.length && !combo->plugin.length && + !combo->pwtext.length && no_auto_create) { my_error(ER_PASSWORD_NO_MATCH, MYF(0)); goto end; @@ -3962,32 +3768,37 @@ static int replace_user_table(THD *thd, const User_table &user_table, my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0)); goto end; } - else if (combo.plugin.str[0]) + else if (combo->plugin.length) { - if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN)) + if (!plugin_is_ready(&combo->plugin, MYSQL_AUTHENTICATION_PLUGIN)) { - my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str); + my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo->plugin.str); goto end; } } + else /* combo->plugin.length == 0 */ + { + combo->plugin= guess_auth_plugin(thd, combo->auth.length); + } old_row_exists = 0; restore_record(table,s->default_values); - user_table.host()->store(combo.host.str,combo.host.length, + user_table.host()->store(combo->host.str,combo->host.length, system_charset_info); - user_table.user()->store(combo.user.str,combo.user.length, + user_table.user()->store(combo->user.str,combo->user.length, system_charset_info); } else { old_row_exists = 1; store_record(table,record[1]); // Save copy for update + if (!combo->plugin.length && (combo->auth.length || combo->pwtext.length)) + { + /* GRANT ... IDENTIFIED BY */ + combo->plugin= guess_auth_plugin(thd, combo->auth.length); + } } - if (!old_row_exists || combo.pwtext.length || combo.pwhash.length) - if (!handle_as_role && validate_password(&combo, thd)) - goto end; - /* Update table columns with new privileges */ ulong priv; @@ -4000,122 +3811,112 @@ static int replace_user_table(THD *thd, const User_table &user_table, rights= user_table.get_access(); - DBUG_PRINT("info",("table fields: %d", user_table.num_fields())); - /* If we don't have a password column, we'll use the authentication_string - column later. */ - if (combo.pwhash.str[0] && user_table.password()) - user_table.password()->store(combo.pwhash.str, combo.pwhash.length, - system_charset_info); - /* We either have the password column, the plugin column, or both. Otherwise - we have a corrupt user table. */ - DBUG_ASSERT(user_table.password() || user_table.plugin()); - if (user_table.ssl_type()) /* From 4.0.0 we have more fields */ - { - /* We write down SSL related ACL stuff */ - switch (lex->ssl_type) { - case SSL_TYPE_ANY: - user_table.ssl_type()->store(STRING_WITH_LEN("ANY"), - &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - break; - case SSL_TYPE_X509: - user_table.ssl_type()->store(STRING_WITH_LEN("X509"), - &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - break; - case SSL_TYPE_SPECIFIED: - user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"), - &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - if (lex->ssl_cipher) - user_table.ssl_cipher()->store(lex->ssl_cipher, - strlen(lex->ssl_cipher), - system_charset_info); - if (lex->x509_issuer) - user_table.x509_issuer()->store(lex->x509_issuer, - strlen(lex->x509_issuer), - system_charset_info); - if (lex->x509_subject) - user_table.x509_subject()->store(lex->x509_subject, - strlen(lex->x509_subject), - system_charset_info); - break; - case SSL_TYPE_NOT_SPECIFIED: - break; - case SSL_TYPE_NONE: - user_table.ssl_type()->store("", 0, &my_charset_latin1); - user_table.ssl_cipher()->store("", 0, &my_charset_latin1); - user_table.x509_issuer()->store("", 0, &my_charset_latin1); - user_table.x509_subject()->store("", 0, &my_charset_latin1); - break; + if (handle_as_role) + { + if (old_row_exists && !user_table.check_is_role()) + { + goto end; + } + user_table.is_role()->store("Y", 1, system_charset_info); + } + else + { + old_acl_user= find_user_exact(combo->host.str, combo->user.str); + if ((old_acl_user != NULL) != old_row_exists) + { + my_error(ER_PASSWORD_NO_MATCH, MYF(0)); + goto end; } + if (acl_user_update(thd, &new_acl_user, + old_row_exists ? old_acl_user : NULL, + *combo, lex->ssl_type, lex->ssl_cipher, + lex->x509_issuer, lex->x509_subject, &lex->mqh, + rights)) + goto end; - USER_RESOURCES mqh= lex->mqh; - if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) - user_table.max_questions()->store((longlong) mqh.questions, TRUE); - if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) - user_table.max_updates()->store((longlong) mqh.updates, TRUE); - if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) - user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE); - if (user_table.max_user_connections() && - (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE); - if (user_table.plugin()) - { - user_table.plugin()->set_notnull(); - user_table.authentication_string()->set_notnull(); - if (combo.plugin.str[0]) - { - DBUG_ASSERT(combo.pwhash.str[0] == 0); - if (user_table.password()) - user_table.password()->reset(); - user_table.plugin()->store(combo.plugin.str, combo.plugin.length, - system_charset_info); - user_table.authentication_string()->store(combo.auth.str, combo.auth.length, - system_charset_info); + DBUG_PRINT("info",("table fields: %d", user_table.num_fields())); + /* We either have the password column, the plugin column, or both. Otherwise + we have a corrupt user table. */ + DBUG_ASSERT(user_table.password() || user_table.plugin()); + if (user_table.ssl_type()) /* From 4.0.0 we have more fields */ + { + /* We write down SSL related ACL stuff */ + switch (lex->ssl_type) { + case SSL_TYPE_ANY: + user_table.ssl_type()->store(STRING_WITH_LEN("ANY"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + break; + case SSL_TYPE_X509: + user_table.ssl_type()->store(STRING_WITH_LEN("X509"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + break; + case SSL_TYPE_SPECIFIED: + user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + if (lex->ssl_cipher) + user_table.ssl_cipher()->store(lex->ssl_cipher, + strlen(lex->ssl_cipher), + system_charset_info); + if (lex->x509_issuer) + user_table.x509_issuer()->store(lex->x509_issuer, + strlen(lex->x509_issuer), + system_charset_info); + if (lex->x509_subject) + user_table.x509_subject()->store(lex->x509_subject, + strlen(lex->x509_subject), + system_charset_info); + break; + case SSL_TYPE_NOT_SPECIFIED: + break; + case SSL_TYPE_NONE: + user_table.ssl_type()->store("", 0, &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); + break; } - if (combo.pwhash.str[0]) + + USER_RESOURCES mqh= lex->mqh; + if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) + user_table.max_questions()->store((longlong) mqh.questions, TRUE); + if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) + user_table.max_updates()->store((longlong) mqh.updates, TRUE); + if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) + user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE); + if (user_table.max_user_connections() && + (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) + user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE); + if (user_table.plugin()) { - DBUG_ASSERT(combo.plugin.str[0] == 0); - /* We have Password column. */ - if (user_table.password()) + user_table.plugin()->set_notnull(); + user_table.authentication_string()->set_notnull(); + if (new_acl_user.plugin.length) { - user_table.plugin()->reset(); - user_table.authentication_string()->reset(); + if (user_table.password()) + user_table.password()->reset(); + user_table.plugin()->store(new_acl_user.plugin.str, + new_acl_user.plugin.length, system_charset_info); + user_table.authentication_string()->store(new_acl_user.auth_string.str, + new_acl_user.auth_string.length, system_charset_info); } - else + + if (user_table.max_statement_time()) { - /* We do not have Password column. Use PLUGIN && Authentication_string - columns instead. */ - set_authentication_plugin_from_password(user_table, - combo.pwhash.str, - combo.pwhash.length); + if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) + user_table.max_statement_time()->store(mqh.max_statement_time); } } - - if (user_table.max_statement_time()) - { - if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) - user_table.max_statement_time()->store(mqh.max_statement_time); - } - } - mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour || - mqh.user_conn || mqh.max_statement_time != 0.0); - - /* table format checked earlier */ - if (handle_as_role) - { - if (old_row_exists && !user_table.check_is_role()) - { - goto end; - } - user_table.is_role()->store("Y", 1, system_charset_info); + mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour || + mqh.user_conn || mqh.max_statement_time != 0.0); } } @@ -4155,37 +3956,32 @@ end: if (likely(!error)) { acl_cache->clear(1); // Clear privilege cache - if (old_row_exists) + if (handle_as_role) { - if (handle_as_role) - acl_update_role(combo.user.str, rights); + if (old_row_exists) + acl_update_role(combo->user.str, rights); else - acl_update_user(combo.user.str, combo.host.str, - combo.pwhash.str, combo.pwhash.length, - lex->ssl_type, - lex->ssl_cipher, - lex->x509_issuer, - lex->x509_subject, - &lex->mqh, - rights, - &combo.plugin, - &combo.auth); + acl_insert_role(combo->user.str, rights); } else { - if (handle_as_role) - acl_insert_role(combo.user.str, rights); + if (old_acl_user) + *old_acl_user= new_acl_user; else - acl_insert_user(combo.user.str, combo.host.str, - combo.pwhash.str, combo.pwhash.length, - lex->ssl_type, - lex->ssl_cipher, - lex->x509_issuer, - lex->x509_subject, - &lex->mqh, - rights, - &combo.plugin, - &combo.auth); + { + push_new_user(new_acl_user); + my_qsort(dynamic_element(&acl_users, 0, ACL_USER*), acl_users.elements, + sizeof(ACL_USER),(qsort_cmp) acl_compare); + + /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ + rebuild_check_host(); + + /* + Rebuild every user's role_grants since 'acl_users' has been sorted + and old pointers to ACL_USER elements are no longer valid + */ + rebuild_role_grants(); + } } } DBUG_RETURN(error); @@ -6288,7 +6084,7 @@ static bool merge_one_role_privileges(ACL_ROLE *grantee) static bool has_auth(LEX_USER *user, LEX *lex) { - return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length || + return user->pwtext.length || user->plugin.length || user->auth.length || lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher || lex->x509_issuer || lex->x509_subject || lex->mqh.specified_limits; @@ -6300,14 +6096,9 @@ static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd) { /* preserve authentication information, if LEX_USER was reallocated */ to->pwtext= from->pwtext; - to->pwhash= from->pwhash; to->plugin= from->plugin; to->auth= from->auth; } - - if (fix_lex_user(thd, to)) - return true; - return false; } @@ -6471,7 +6262,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ error= copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables.user_table(), *Str, + replace_user_table(thd, tables.user_table(), Str, 0, revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER)); @@ -6650,7 +6441,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ if (copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables.user_table(), *Str, + replace_user_table(thd, tables.user_table(), Str, 0, revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER))) @@ -6926,7 +6717,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) user_combo.user = username; if (copy_and_check_auth(&user_combo, &user_combo, thd) || - replace_user_table(thd, tables.user_table(), user_combo, 0, + replace_user_table(thd, tables.user_table(), &user_combo, 0, false, create_new_user, no_auto_create_user)) { @@ -7096,7 +6887,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } if (copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables.user_table(), *Str, + replace_user_table(thd, tables.user_table(), Str, (!db ? rights : 0), revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER))) @@ -7260,8 +7051,7 @@ static bool grant_load(THD *thd, { sql_print_warning("'tables_priv' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - mem_check->tname, - safe_str(mem_check->user), + mem_check->tname, mem_check->user, safe_str(mem_check->host.hostname)); continue; } @@ -8346,7 +8136,6 @@ static void add_user_parameters(String *result, ACL_USER* acl_user, { if (acl_user->auth_string.length) { - DBUG_ASSERT(acl_user->salt_len); result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); result->append(&acl_user->auth_string); result->append('\''); @@ -8866,7 +8655,7 @@ static bool show_database_privileges(THD *thd, const char *username, const char *user, *host; acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - user= safe_str(acl_db->user); + user= acl_db->user; host=acl_db->host.hostname; /* @@ -8952,7 +8741,7 @@ static bool show_table_and_column_privileges(THD *thd, const char *username, GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); - user= safe_str(grant_table->user); + user= grant_table->user; host= grant_table->host.hostname; /* @@ -9094,7 +8883,7 @@ static int show_routine_grants(THD* thd, const char *user, *host; GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index); - user= safe_str(grant_proc->user); + user= grant_proc->user; host= grant_proc->host.hostname; /* @@ -9606,8 +9395,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, my_hash_delete(&acl_roles, (uchar*) acl_role); DBUG_RETURN(1); } - acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str); - acl_role->user.length= user_to->user.length; + acl_role->user= safe_lexcstrdup_root(&acl_memroot, user_to->user); my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key, old_key_length); @@ -9703,8 +9491,6 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, default: DBUG_ASSERT(0); } - if (! user) - user= ""; if (! host) host= ""; @@ -9798,8 +9584,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, { switch ( struct_no ) { case USER_ACL: - acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str); - acl_user->user.length= user_to->user.length; + acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user); update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str)); acl_user->hostname_length= strlen(acl_user->host.hostname); break; @@ -10182,13 +9967,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) if (!user_name->host.str) user_name->host= host_not_specified; - if (fix_lex_user(thd, user_name)) - { - append_user(thd, &wrong_users, user_name); - result= TRUE; - continue; - } - /* Search all in-memory structures and grant tables for a mention of the new user/role name. @@ -10231,7 +10009,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) } } - if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0)) + if (replace_user_table(thd, tables.user_table(), user_name, 0, 0, 1, 0)) { append_user(thd, &wrong_users, user_name); result= TRUE; @@ -10520,10 +10298,8 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) while ((tmp_lex_user= users_list_iterator++)) { LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false); - if (!lex_user || - fix_lex_user(thd, lex_user) || - replace_user_table(thd, tables.user_table(), *lex_user, 0, - false, false, true)) + if (!lex_user || replace_user_table(thd, tables.user_table(), lex_user, 0, + false, false, true)) { thd->clear_error(); append_user(thd, &wrong_users, tmp_lex_user); @@ -10572,7 +10348,7 @@ mysql_revoke_sp_privs(THD *thd, { const char *user,*host; GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); - user= safe_str(grant_proc->user); + user= grant_proc->user; host= safe_str(grant_proc->host.hostname); if (!strcmp(lex_user->user.str, user) && @@ -10647,7 +10423,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) continue; } - if (replace_user_table(thd, tables.user_table(), *lex_user, + if (replace_user_table(thd, tables.user_table(), lex_user, ~(ulong)0, 1, 0, 0)) { result= -1; @@ -10668,7 +10444,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - user= safe_str(acl_db->user); + user= acl_db->user; host= safe_str(acl_db->host.hostname); if (!strcmp(lex_user->user.str, user) && @@ -10700,7 +10476,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) const char *user,*host; GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter); - user= safe_str(grant_table->user); + user= grant_table->user; host= safe_str(grant_table->host.hostname); if (!strcmp(lex_user->user.str,user) && @@ -10984,17 +10760,11 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) DBUG_RETURN(TRUE); - combo->user.str= sctx->user; + combo->user.str= (char *) sctx->priv_user; mysql_mutex_lock(&acl_cache->lock); - - if ((au= find_user_wild(combo->host.str=(char*)sctx->host_or_ip, combo->user.str))) - goto found_acl; - if ((au= find_user_wild(combo->host.str=(char*)sctx->host, combo->user.str))) - goto found_acl; - if ((au= find_user_wild(combo->host.str=(char*)sctx->ip, combo->user.str))) - goto found_acl; - if ((au= find_user_wild(combo->host.str=(char*)"%", combo->user.str))) + if ((au= find_user_exact(combo->host.str= (char *) sctx->priv_host, + combo->user.str))) goto found_acl; mysql_mutex_unlock(&acl_cache->lock); @@ -11316,7 +11086,7 @@ bool check_role_is_granted(const char *username, ACL_USER_BASE *root; mysql_mutex_lock(&acl_cache->lock); if (hostname) - root= find_user_exact(username, hostname); + root= find_user_exact(hostname, username); else root= find_acl_role(username); @@ -11490,7 +11260,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) { const char *user,*host, *is_grantable="YES"; acl_user=dynamic_element(&acl_users,counter,ACL_USER*); - user= safe_str(acl_user->user.str); + user= acl_user->user.str; host= safe_str(acl_user->host.hostname); if (no_global_access && @@ -11564,7 +11334,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) const char *user, *host, *is_grantable="YES"; acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - user= safe_str(acl_db->user); + user= acl_db->user; host= safe_str(acl_db->host.hostname); if (no_global_access && @@ -11636,7 +11406,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) const char *user, *host, *is_grantable= "YES"; GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); - user= safe_str(grant_table->user); + user= grant_table->user; host= safe_str(grant_table->host.hostname); if (no_global_access && @@ -11718,7 +11488,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) const char *user, *host, *is_grantable= "YES"; GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash, index); - user= safe_str(grant_table->user); + user= grant_table->user; host= safe_str(grant_table->host.hostname); if (no_global_access && @@ -12289,7 +12059,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, const char *client_auth_plugin= ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin; - DBUG_EXECUTE_IF("auth_disconnect", { vio_close(net->vio); DBUG_RETURN(1); }); + DBUG_EXECUTE_IF("auth_disconnect", { DBUG_RETURN(1); }); DBUG_ASSERT(client_auth_plugin); /* @@ -12302,8 +12072,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, user account, it's the plugin that the client need to use to login. */ bool switch_from_long_to_short_scramble= - native_password_plugin_name.str == mpvio->cached_client_reply.plugin && - client_auth_plugin == old_password_plugin_name.str; + client_auth_plugin == old_password_plugin_name.str && + my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin, + native_password_plugin_name.str) == 0; if (switch_from_long_to_short_scramble) DBUG_RETURN (secure_auth(mpvio->auth_info.thd) || @@ -12316,8 +12087,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, ask an old 4.0 client to use the new 4.1 authentication protocol. */ bool switch_from_short_to_long_scramble= - old_password_plugin_name.str == mpvio->cached_client_reply.plugin && - client_auth_plugin == native_password_plugin_name.str; + client_auth_plugin == native_password_plugin_name.str && + my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin, + old_password_plugin_name.str) == 0; if (switch_from_short_to_long_scramble) { @@ -12407,9 +12179,9 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) mpvio->auth_info.user_name= sctx->user; mpvio->auth_info.user_name_length= (uint)strlen(sctx->user); - mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str; - mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length; - strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str)); + mpvio->auth_info.auth_string= mpvio->acl_user->salt.str; + mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->salt.length; + strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user.str); DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s" "plugin=%s", @@ -12560,7 +12332,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) MYF(0)); DBUG_RETURN(1); } - client_plugin= fix_plugin_ptr(next_field); + client_plugin= next_field; next_field+= strlen(next_field) + 1; } else @@ -12805,7 +12577,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) && (client_plugin < (char *)net->read_pos + pkt_len)) { - client_plugin= fix_plugin_ptr(client_plugin); next_field+= strlen(next_field) + 1; } else @@ -13144,17 +12915,7 @@ static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name, { int res= CR_OK, old_status= MPVIO_EXT::FAILURE; bool unlock_plugin= false; - plugin_ref plugin= NULL; - - if (auth_plugin_name->str == native_password_plugin_name.str) - plugin= native_password_plugin; -#ifndef EMBEDDED_LIBRARY - else if (auth_plugin_name->str == old_password_plugin_name.str) - plugin= old_password_plugin; - else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name, - MYSQL_AUTHENTICATION_PLUGIN))) - unlock_plugin= true; -#endif + plugin_ref plugin= get_auth_plugin(thd, *auth_plugin_name, &unlock_plugin); mpvio->plugin= plugin; old_status= mpvio->status; @@ -13342,7 +13103,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) { #ifndef NO_EMBEDDED_ACCESS_CHECKS bool is_proxy_user= FALSE; - const char *auth_user = safe_str(acl_user->user.str); + const char *auth_user = acl_user->user.str; ACL_PROXY_USER *proxy_user; /* check if the user is allowed to proxy as another user */ proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip, @@ -13388,10 +13149,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) #endif sctx->master_access= acl_user->access; - if (acl_user->user.str) - strmake_buf(sctx->priv_user, acl_user->user.str); - else - *sctx->priv_user= 0; + strmake_buf(sctx->priv_user, acl_user->user.str); if (acl_user->host.hostname) strmake_buf(sctx->priv_host, acl_user->host.hostname); @@ -13603,15 +13361,16 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; }); if (pkt_len == 0) /* no password */ - DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK); + DBUG_RETURN(info->auth_string_length != 0 + ? CR_AUTH_USER_CREDENTIALS : CR_OK); info->password_used= PASSWORD_USED_YES; if (pkt_len == SCRAMBLE_LENGTH) { - if (!mpvio->acl_user->salt_len) + if (!info->auth_string_length) DBUG_RETURN(CR_AUTH_USER_CREDENTIALS); - if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt)) + if (check_scramble(pkt, thd->scramble, (uchar*)info->auth_string)) DBUG_RETURN(CR_AUTH_USER_CREDENTIALS); else DBUG_RETURN(CR_OK); @@ -13621,6 +13380,41 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, DBUG_RETURN(CR_AUTH_HANDSHAKE); } +static int native_password_make_scramble(const char *password, + size_t password_length, char *hash, size_t *hash_length) +{ + DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH); + if (password_length == 0) + *hash_length= 0; + else + { + *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + my_make_scrambled_password(hash, password, password_length); + } + return 0; +} + +static int native_password_get_salt(const char *hash, size_t hash_length, + unsigned char *out, size_t *out_length) +{ + DBUG_ASSERT(*out_length >= SCRAMBLE_LENGTH); + if (hash_length == 0) + { + *out_length= 0; + return 0; + } + + if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH) + { + my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); + return 1; + } + + *out_length= SCRAMBLE_LENGTH; + get_salt_from_password(out, hash); + return 0; +} + static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { @@ -13664,30 +13458,64 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, if (pkt_len == SCRAMBLE_LENGTH_323) { - if (!mpvio->acl_user->salt_len) + if (!info->auth_string_length) return CR_AUTH_USER_CREDENTIALS; - return check_scramble_323(pkt, thd->scramble, - (ulong *) mpvio->acl_user->salt) ? - CR_AUTH_USER_CREDENTIALS : CR_OK; + return check_scramble_323(pkt, thd->scramble, (ulong *) info->auth_string) + ? CR_AUTH_USER_CREDENTIALS : CR_OK; } my_error(ER_HANDSHAKE_ERROR, MYF(0)); return CR_AUTH_HANDSHAKE; } +static int old_password_make_scramble(const char *password, + size_t password_length, char *hash, size_t *hash_length) +{ + DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH_323); + if (password_length == 0) + *hash_length= 0; + else + { + *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + my_make_scrambled_password_323(hash, password, password_length); + } + return 0; +} + +#define SALT_LENGTH_323 (sizeof(ulong)*2) +static int old_password_get_salt(const char *hash, size_t hash_length, + unsigned char *out, size_t *out_length) +{ + DBUG_ASSERT(*out_length >= SALT_LENGTH_323); + + if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + { + my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323); + return 1; + } + + *out_length= SALT_LENGTH_323; + get_salt_from_password_323((ulong*)out, hash); + return 0; +} + static struct st_mysql_auth native_password_handler= { MYSQL_AUTHENTICATION_INTERFACE_VERSION, native_password_plugin_name.str, - native_password_authenticate + native_password_authenticate, + native_password_make_scramble, + native_password_get_salt }; static struct st_mysql_auth old_password_handler= { MYSQL_AUTHENTICATION_INTERFACE_VERSION, old_password_plugin_name.str, - old_password_authenticate + old_password_authenticate, + old_password_make_scramble, + old_password_get_salt }; maria_declare_plugin(mysql_password) diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 9f92af61eec..d0d959de8f9 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -1325,6 +1325,9 @@ bool Sql_cmd_analyze_table::execute(THD *thd) m_lex->query_tables= first_table; error: +#ifdef WITH_WSREP +wsrep_error_label: +#endif DBUG_RETURN(res); } @@ -1382,6 +1385,9 @@ bool Sql_cmd_optimize_table::execute(THD *thd) m_lex->query_tables= first_table; error: +#ifdef WITH_WSREP +wsrep_error_label: +#endif DBUG_RETURN(res); } @@ -1417,5 +1423,8 @@ bool Sql_cmd_repair_table::execute(THD *thd) m_lex->query_tables= first_table; error: +#ifdef WITH_WSREP +wsrep_error_label: +#endif DBUG_RETURN(res); } diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 700b4699430..05a71d7785d 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -476,15 +476,17 @@ bool Sql_cmd_alter_table::execute(THD *thd) thd->work_part_info= 0; #endif -#ifdef WITH_WSREP - if ((!thd->is_current_stmt_binlog_format_row() || + if (WSREP(thd) && + (!thd->is_current_stmt_binlog_format_row() || !thd->find_temporary_table(first_table))) { - WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db.str : NULL), - ((lex->name.str) ? lex->name.str : NULL), - first_table); + WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL), + (lex->name.str ? lex->name.str : NULL), + first_table, &alter_info); + + thd->variables.auto_increment_offset = 1; + thd->variables.auto_increment_increment = 1; } -#endif /* WITH_WSREP */ result= mysql_alter_table(thd, &select_lex->db, &lex->name, &create_info, @@ -495,12 +497,11 @@ bool Sql_cmd_alter_table::execute(THD *thd) lex->ignore); DBUG_RETURN(result); - #ifdef WITH_WSREP -error: +wsrep_error_label: WSREP_WARN("ALTER TABLE isolation failure"); DBUG_RETURN(TRUE); -#endif /* WITH_WSREP */ +#endif } bool Sql_cmd_discard_import_tablespace::execute(THD *thd) diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 108b98afdd7..14242015bd2 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -83,7 +83,7 @@ public: // Columns and keys to be dropped. List<Alter_drop> drop_list; - // Columns for ALTER_COLUMN_CHANGE_DEFAULT. + // Columns for ALTER_CHANGE_COLUMN_DEFAULT. List<Alter_column> alter_list; // List of keys, used by both CREATE and ALTER TABLE. List<Key> key_list; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 98fd601b6d5..fc8a20404a3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -414,9 +414,10 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, for (TABLE_LIST *table_list= tables_to_reopen; table_list; table_list= table_list->next_global) { + int err; /* A check that the table was locked for write is done by the caller. */ TABLE *table= find_table_for_mdl_upgrade(thd, table_list->db.str, - table_list->table_name.str, TRUE); + table_list->table_name.str, &err); /* May return NULL if this table has already been closed via an alias. */ if (! table) @@ -597,6 +598,7 @@ bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection) static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) { + DBUG_ENTER("mark_used_tables_as_free_for_reuse"); for (; table ; table= table->next) { DBUG_ASSERT(table->pos_in_locked_tables == NULL || @@ -607,6 +609,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) table->file->ha_reset(); } } + DBUG_VOID_RETURN; } @@ -648,6 +651,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, size_t key_length= share->table_cache_key.length; const char *db= key; const char *table_name= db + share->db.length + 1; + bool remove_from_locked_tables= extra != HA_EXTRA_NOT_USED; memcpy(key, share->table_cache_key.str, key_length); @@ -661,7 +665,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, { thd->locked_tables_list.unlink_from_list(thd, table->pos_in_locked_tables, - extra != HA_EXTRA_NOT_USED); + remove_from_locked_tables); /* Inform handler that there is a drop table or a rename going on */ if (extra != HA_EXTRA_NOT_USED && table->db_stat) { @@ -1499,6 +1503,65 @@ static int set_partitions_as_used(TABLE_LIST *tl, TABLE *t) /** + Check if the given table is actually a VIEW that was LOCK-ed + + @param thd Thread context. + @param t Table to check. + + @retval TRUE The 't'-table is a locked view + needed to remedy problem before retrying again. + @retval FALSE 't' was not locked, not a VIEW or an error happened. +*/ +bool is_locked_view(THD *thd, TABLE_LIST *t) +{ + DBUG_ENTER("check_locked_view"); + /* + Is this table a view and not a base table? + (it is work around to allow to open view with locked tables, + real fix will be made after definition cache will be made) + + Since opening of view which was not explicitly locked by LOCK + TABLES breaks metadata locking protocol (potentially can lead + to deadlocks) it should be disallowed. + */ + if (thd->mdl_context.is_lock_owner(MDL_key::TABLE, t->db.str, + t->table_name.str, MDL_SHARED)) + { + char path[FN_REFLEN + 1]; + build_table_filename(path, sizeof(path) - 1, + t->db.str, t->table_name.str, reg_ext, 0); + /* + Note that we can't be 100% sure that it is a view since it's + possible that we either simply have not found unused TABLE + instance in THD::open_tables list or were unable to open table + during prelocking process (in this case in theory we still + should hold shared metadata lock on it). + */ + if (dd_frm_is_view(thd, path)) + { + /* + If parent_l of the table_list is non null then a merge table + has this view as child table, which is not supported. + */ + if (t->parent_l) + { + my_error(ER_WRONG_MRG_TABLE, MYF(0)); + DBUG_RETURN(FALSE); + } + + if (!tdc_open_view(thd, t, CHECK_METADATA_VERSION)) + { + DBUG_ASSERT(t->view != 0); + DBUG_RETURN(TRUE); // VIEW + } + } + } + + DBUG_RETURN(FALSE); +} + + +/** Open a base table. @param thd Thread context. @@ -1652,49 +1715,10 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) #endif goto reset; } - /* - Is this table a view and not a base table? - (it is work around to allow to open view with locked tables, - real fix will be made after definition cache will be made) - Since opening of view which was not explicitly locked by LOCK - TABLES breaks metadata locking protocol (potentially can lead - to deadlocks) it should be disallowed. - */ - if (thd->mdl_context.is_lock_owner(MDL_key::TABLE, - table_list->db.str, - table_list->table_name.str, - MDL_SHARED)) - { - char path[FN_REFLEN + 1]; - build_table_filename(path, sizeof(path) - 1, - table_list->db.str, table_list->table_name.str, reg_ext, 0); - /* - Note that we can't be 100% sure that it is a view since it's - possible that we either simply have not found unused TABLE - instance in THD::open_tables list or were unable to open table - during prelocking process (in this case in theory we still - should hold shared metadata lock on it). - */ - if (dd_frm_is_view(thd, path)) - { - /* - If parent_l of the table_list is non null then a merge table - has this view as child table, which is not supported. - */ - if (table_list->parent_l) - { - my_error(ER_WRONG_MRG_TABLE, MYF(0)); - DBUG_RETURN(true); - } + if (is_locked_view(thd, table_list)) + DBUG_RETURN(FALSE); // VIEW - if (!tdc_open_view(thd, table_list, CHECK_METADATA_VERSION)) - { - DBUG_ASSERT(table_list->view != 0); - DBUG_RETURN(FALSE); // VIEW - } - } - } /* No table in the locked tables list. In case of explicit LOCK TABLES this can happen if a user did not include the table into the list. @@ -2064,8 +2088,9 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name) @param thd Thread context @param db Database name. @param table_name Name of table. - @param no_error Don't emit error if no suitable TABLE - instance were found. + @param p_error In the case of an error (when the function returns NULL) + the error number is stored there. + If the p_error is NULL, function launches the error itself. @note This function checks if the connection holds a global IX metadata lock. If no such lock is found, it is not safe to @@ -2078,15 +2103,15 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name) */ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, - const char *table_name, bool no_error) + const char *table_name, int *p_error) { TABLE *tab= find_locked_table(thd->open_tables, db, table_name); + int error; if (unlikely(!tab)) { - if (!no_error) - my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_name); - return NULL; + error= ER_TABLE_NOT_LOCKED; + goto err_exit; } /* @@ -2098,9 +2123,8 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, if (unlikely(!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE))) { - if (!no_error) - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name); - return NULL; + error= ER_TABLE_NOT_LOCKED_FOR_WRITE; + goto err_exit; } while (tab->mdl_ticket != NULL && @@ -2108,10 +2132,21 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, (tab= find_locked_table(tab->next, db, table_name))) continue; - if (unlikely(!tab && !no_error)) - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name); + if (unlikely(!tab)) + { + error= ER_TABLE_NOT_LOCKED_FOR_WRITE; + goto err_exit; + } return tab; + +err_exit: + if (p_error) + *p_error= error; + else + my_error(error, MYF(0), table_name); + + return NULL; } @@ -3399,6 +3434,15 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, */ if (tables->with) { + if (tables->is_recursive_with_table() && + !tables->is_with_table_recursive_reference()) + { + tables->with->rec_outer_references++; + With_element *with_elem= tables->with; + while ((with_elem= with_elem->get_next_mutually_recursive()) != + tables->with) + with_elem->rec_outer_references++; + } if (tables->set_as_with_table(thd, tables->with)) DBUG_RETURN(1); else @@ -3813,6 +3857,10 @@ lock_table_names(THD *thd, const DDL_options_st &options, mdl_requests.push_front(&global_request); if (create_table) +#ifdef WITH_WSREP + if (thd->lex->sql_command != SQLCOM_CREATE_TABLE && + thd->wsrep_exec_mode != REPL_RECV) +#endif lock_wait_timeout= 0; // Don't wait for timeout } @@ -3917,7 +3965,8 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, Note that find_table_for_mdl_upgrade() will report an error if no suitable ticket is found. */ - if (!find_table_for_mdl_upgrade(thd, table->db.str, table->table_name.str, false)) + if (!find_table_for_mdl_upgrade(thd, table->db.str, table->table_name.str, + NULL)) return TRUE; } @@ -4248,6 +4297,9 @@ restart: } error: +#ifdef WITH_WSREP +wsrep_error_label: +#endif THD_STAGE_INFO(thd, stage_after_opening_tables); thd_proc_info(thd, 0); @@ -4314,9 +4366,8 @@ handle_routine(THD *thd, Query_tables_list *prelocking_ctx, @note this can be changed to use a hash, instead of scanning the linked list, if the performance of this function will ever become an issue */ -static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, - LEX_CSTRING *table, - thr_lock_type lock_type) +bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, + LEX_CSTRING *table, thr_lock_type lock_type) { for (; tl; tl= tl->next_global ) { @@ -6835,6 +6886,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, Query_arena *arena, backup; bool result= TRUE; List<Natural_join_column> *non_join_columns; + List<Natural_join_column> *join_columns; DBUG_ENTER("store_natural_using_join_columns"); DBUG_ASSERT(!natural_using_join->join_columns); @@ -6842,7 +6894,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, arena= thd->activate_stmt_arena_if_needed(&backup); if (!(non_join_columns= new List<Natural_join_column>) || - !(natural_using_join->join_columns= new List<Natural_join_column>)) + !(join_columns= new List<Natural_join_column>)) goto err; /* Append the columns of the first join operand. */ @@ -6851,7 +6903,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, nj_col_1= it_1.get_natural_column_ref(); if (nj_col_1->is_common) { - natural_using_join->join_columns->push_back(nj_col_1, thd->mem_root); + join_columns->push_back(nj_col_1, thd->mem_root); /* Reset the common columns for the next call to mark_common_columns. */ nj_col_1->is_common= FALSE; } @@ -6872,7 +6924,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, { const char *using_field_name_ptr= using_field_name->c_ptr(); List_iterator_fast<Natural_join_column> - it(*(natural_using_join->join_columns)); + it(*join_columns); Natural_join_column *common_field; for (;;) @@ -6905,15 +6957,28 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, } if (non_join_columns->elements > 0) - natural_using_join->join_columns->append(non_join_columns); + join_columns->append(non_join_columns); + natural_using_join->join_columns= join_columns; natural_using_join->is_join_columns_complete= TRUE; result= FALSE; -err: if (arena) thd->restore_active_arena(arena, &backup); DBUG_RETURN(result); + +err: + /* + Actually we failed to build join columns list, so we have to + clear it to avoid problems with half-build join on next run. + The list was created in mark_common_columns(). + */ + table_ref_1->remove_join_columns(); + table_ref_2->remove_join_columns(); + + if (arena) + thd->restore_active_arena(arena, &backup); + DBUG_RETURN(TRUE); } @@ -7137,7 +7202,6 @@ static bool setup_natural_join_row_types(THD *thd, DBUG_PRINT("info", ("using cached setup_natural_join_row_types")); DBUG_RETURN(false); } - context->select_lex->first_natural_join_processing= false; List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause); TABLE_LIST *table_ref; /* Current table reference. */ @@ -7182,6 +7246,7 @@ static bool setup_natural_join_row_types(THD *thd, change on re-execution */ context->natural_join_first_table= context->first_name_resolution_table; + context->select_lex->first_natural_join_processing= false; DBUG_RETURN (false); } @@ -7295,8 +7360,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, thd->column_usage= column_usage; DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); if (allow_sum_func) - thd->lex->allow_sum_func|= - (nesting_map)1 << thd->lex->current_select->nest_level; + thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level); thd->where= THD::DEFAULT_WHERE; save_is_item_list_lookup= thd->lex->current_select->is_item_list_lookup; thd->lex->current_select->is_item_list_lookup= 0; @@ -8768,6 +8832,13 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, void close_system_tables(THD *thd, Open_tables_backup *backup) { + /* + Inform the transaction handler that we are closing the + system tables and we don't need the read view anymore. + */ + for (TABLE *table= thd->open_tables ; table ; table= table->next) + table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE); + close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); } diff --git a/sql/sql_base.h b/sql/sql_base.h index 59c849086e6..0ebed4159ab 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -126,6 +126,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, MYSQL_OPEN_GET_NEW_TABLE |\ MYSQL_OPEN_HAS_MDL_LOCK) +bool is_locked_view(THD *thd, TABLE_LIST *t); bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx); bool get_key_map_from_key_list(key_map *map, TABLE *table, @@ -141,6 +142,8 @@ thr_lock_type read_lock_type_for_table(THD *thd, my_bool mysql_rm_tmp_tables(void); void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, const MDL_savepoint &start_of_statement_svp); +bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, + LEX_CSTRING *table, thr_lock_type lock_type); TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, const LEX_CSTRING *db_name, @@ -300,7 +303,8 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, uint flags); TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, const char *table_name, - bool no_error); + int *p_error); +void mark_tmp_table_for_reuse(TABLE *table); int dynamic_column_error_message(enum_dyncol_func_result rc); diff --git a/sql/sql_basic_types.h b/sql/sql_basic_types.h index 1e97262cdf0..362ab0f1259 100644 --- a/sql/sql_basic_types.h +++ b/sql/sql_basic_types.h @@ -22,4 +22,87 @@ typedef ulonglong sql_mode_t; typedef int64 query_id_t; + + +/* + "fuzzydate" with strict data type control. + Please keep "explicit" in constructors and conversion methods. +*/ +class date_mode_t +{ +public: + enum value_t + { + FUZZY_DATES= 1U, // C_TIME_FUZZY_DATES + TIME_ONLY= 4U, // C_TIME_TIME_ONLY + NO_ZERO_IN_DATE= (1UL << 23), // MODE_NO_ZERO_IN_DATE + NO_ZERO_DATE= (1UL << 24), // MODE_NO_ZERO_DATE + INVALID_DATES= (1UL << 25) // MODE_INVALID_DATES + }; + +private: + value_t m_mode; +public: + + // Constructors + explicit date_mode_t(ulonglong fuzzydate) + :m_mode((value_t) fuzzydate) + { } + + // Conversion operators + explicit operator ulonglong() const + { + return m_mode; + } + explicit operator bool() const + { + return m_mode != 0; + } + + // Unary operators + date_mode_t operator~() const + { + return date_mode_t(~m_mode); + } + + // Dyadic bitwise operators + date_mode_t operator&(const date_mode_t &other) const + { + return date_mode_t(m_mode & other.m_mode); + } + + date_mode_t operator|(const date_mode_t &other) const + { + return date_mode_t(m_mode | other.m_mode); + } + + // Dyadic bitwise assignment operators + date_mode_t &operator&=(const date_mode_t &other) + { + m_mode= value_t(m_mode & other.m_mode); + return *this; + } + + date_mode_t &operator|=(const date_mode_t &other) + { + m_mode= value_t(m_mode | other.m_mode); + return *this; + } +}; + + +const date_mode_t + TIME_FUZZY_DATES (date_mode_t::value_t::FUZZY_DATES), + TIME_TIME_ONLY (date_mode_t::value_t::TIME_ONLY), + TIME_NO_ZERO_IN_DATE (date_mode_t::value_t::NO_ZERO_IN_DATE), + TIME_NO_ZERO_DATE (date_mode_t::value_t::NO_ZERO_DATE), + TIME_INVALID_DATES (date_mode_t::value_t::INVALID_DATES); + +// Flags understood by str_to_datetime, str_to_time, number_to_time, check_date +static const date_mode_t + TIME_MODE_FOR_XXX_TO_DATE (date_mode_t::NO_ZERO_IN_DATE | + date_mode_t::NO_ZERO_DATE | + date_mode_t::INVALID_DATES | + date_mode_t::TIME_ONLY); + #endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d6b2cd6fd76..0dd82351719 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1363,6 +1363,11 @@ void THD::change_user(void) cleanup_done= 0; reset_killed(); thd_clear_errors(this); + + /* Clear warnings. */ + if (!get_stmt_da()->is_warning_info_empty()) + get_stmt_da()->clear_warning_info(0); + init(); stmt_map.reset(); my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, @@ -4781,6 +4786,14 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd) return(thd->slave_thread); } + +extern "C" int thd_rpl_stmt_based(const MYSQL_THD thd) +{ + return !thd->is_current_stmt_binlog_format_row() && + !thd->is_current_stmt_binlog_disabled(); +} + + /* Returns high resolution timestamp for the start of the current query. */ extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd) @@ -7696,7 +7709,7 @@ Query_arena_stmt::~Query_arena_stmt() bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, - ulong sec_part, ulonglong fuzzydate) + ulong sec_part, date_mode_t fuzzydate) { time_zone_used= 1; if (ts == 0 && sec_part == 0) diff --git a/sql/sql_class.h b/sql/sql_class.h index aaa54447c0b..d9ff1743fd6 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -581,6 +581,7 @@ typedef struct system_variables ha_rows max_join_size; ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; + uint eq_range_index_dive_limit; ulong column_compression_zlib_strategy; ulong lock_wait_timeout; ulong join_cache_level; @@ -3124,6 +3125,9 @@ public: it returned an error on master, and this is OK on the slave. */ bool is_slave_error; + /* True if we have printed something to the error log for this statement */ + bool error_printed_to_log; + /* True when a transaction is queued up for binlog group commit. Used so that if another transaction needs to wait for a row lock held by @@ -3407,7 +3411,7 @@ public: } const Type_handler *type_handler_for_date() const; bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, - ulong sec_part, ulonglong fuzzydate); + ulong sec_part, date_mode_t fuzzydate); inline my_time_t query_start() { return start_time; } inline ulong query_start_sec_part() { query_start_sec_part_used=1; return start_time_sec_part; } @@ -4037,6 +4041,10 @@ public: *format= (enum_binlog_format) variables.binlog_format; *current_format= current_stmt_binlog_format; } + inline enum_binlog_format get_current_stmt_binlog_format() + { + return current_stmt_binlog_format; + } inline void set_binlog_format(enum_binlog_format format, enum_binlog_format current_format) { @@ -4082,11 +4090,6 @@ public: DBUG_VOID_RETURN; } - inline enum_binlog_format get_current_stmt_binlog_format() - { - return current_stmt_binlog_format; - } - inline void set_current_stmt_binlog_format(enum_binlog_format format) { current_stmt_binlog_format= format; @@ -4693,6 +4696,7 @@ public: TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table); void restore_tmp_table_share(TMP_TABLE_SHARE *share); + void close_unused_temporary_table_instances(const TABLE_LIST *tl); private: /* Whether a lock has been acquired? */ @@ -4950,10 +4954,17 @@ my_eof(THD *thd) (A)->variables.sql_log_bin_off= 0;} -inline sql_mode_t sql_mode_for_dates(THD *thd) +inline date_mode_t sql_mode_for_dates(THD *thd) { - return thd->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES); + static_assert(C_TIME_FUZZY_DATES == date_mode_t::FUZZY_DATES && + C_TIME_TIME_ONLY == date_mode_t::TIME_ONLY, + "sql_mode_t and pure C library date flags must be equal"); + static_assert(MODE_NO_ZERO_DATE == date_mode_t::NO_ZERO_DATE && + MODE_NO_ZERO_IN_DATE == date_mode_t::NO_ZERO_IN_DATE && + MODE_INVALID_DATES == date_mode_t::INVALID_DATES, + "sql_mode_t and date_mode_t values must be equal"); + return date_mode_t(thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES)); } /* @@ -5022,7 +5033,8 @@ protected: SELECT_LEX_UNIT *unit; /* Something used only by the parser: */ public: - select_result(THD *thd_arg): select_result_sink(thd_arg) {} + ha_rows est_records; /* estimated number of records in the result */ + select_result(THD *thd_arg): select_result_sink(thd_arg), est_records(0) {} void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; } virtual ~select_result() {}; /** @@ -5594,7 +5606,6 @@ public: TMP_TABLE_PARAM tmp_table_param; int write_err; /* Error code from the last send_data->ha_write_row call. */ TABLE *table; - ha_rows records; select_unit(THD *thd_arg): select_result_interceptor(thd_arg), @@ -5632,7 +5643,6 @@ public: curr_sel= UINT_MAX; step= UNION_TYPE; write_err= 0; - records= 0; } void change_select(); }; @@ -5646,10 +5656,16 @@ class select_union_recursive :public select_unit TABLE *first_rec_table_to_update; /* The temporary tables used for recursive table references */ List<TABLE> rec_tables; + /* + The count of how many times cleanup() was called with cleaned==false + for the unit specifying the recursive CTE for which this object was created + or for the unit specifying a CTE that mutually recursive with this CTE. + */ + uint cleanup_count; select_union_recursive(THD *thd_arg): select_unit(thd_arg), - incr_table(0), first_rec_table_to_update(0) {}; + incr_table(0), first_rec_table_to_update(0), cleanup_count(0) {}; int send_data(List<Item> &items); bool create_result_table(THD *thd, List<Item> *column_types, diff --git a/sql/sql_const.h b/sql/sql_const.h index e28a0649f04..be26de872df 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -84,7 +84,7 @@ #define MAX_FIELDS 4096 /* Limit in the .frm file */ #define MAX_PARTITIONS 8192 -#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1) +#define MAX_SELECT_NESTING (SELECT_NESTING_MAP_SIZE - 1) #define MAX_SORT_MEMORY 2048*1024 #define MIN_SORT_MEMORY 1024 diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index a518f991892..c89f49aaefc 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -751,9 +751,10 @@ bool With_clause::prepare_unreferenced_elements(THD *thd) @brief Save the specification of the given with table as a string - @param thd The context of the statement containing this with element - @param spec_start The beginning of the specification in the input string - @param spec_end The end of the specification in the input string + @param thd The context of the statement containing this with element + @param spec_start The beginning of the specification in the input string + @param spec_end The end of the specification in the input string + @param spec_offset The offset of the specification in the input string @details The method creates for a string copy of the specification used in this @@ -765,10 +766,17 @@ bool With_clause::prepare_unreferenced_elements(THD *thd) true on failure */ -bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end) +bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end, + my_ptrdiff_t spec_offset) { + stmt_prepare_mode= thd->m_parser_state->m_lip.stmt_prepare_mode; unparsed_spec.length= spec_end - spec_start; - unparsed_spec.str= thd->strmake(spec_start, unparsed_spec.length); + + if (stmt_prepare_mode || !thd->lex->sphead) + unparsed_spec.str= spec_start; + else + unparsed_spec.str= thd->strmake(spec_start, unparsed_spec.length); + unparsed_spec_offset= spec_offset; if (!unparsed_spec.str) { @@ -838,12 +846,27 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, TABLE_LIST *spec_tables_tail; st_select_lex *with_select; + char save_end= unparsed_spec.str[unparsed_spec.length]; + ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= '\0'; if (parser_state.init(thd, (char*) unparsed_spec.str, (unsigned int)unparsed_spec.length)) goto err; + parser_state.m_lip.stmt_prepare_mode= stmt_prepare_mode; + parser_state.m_lip.multi_statements= false; + parser_state.m_lip.m_digest= NULL; + lex_start(thd); + lex->clone_spec_offset= unparsed_spec_offset; + lex->param_list= old_lex->param_list; + lex->sphead= old_lex->sphead; + lex->spname= old_lex->spname; + lex->spcont= old_lex->spcont; + lex->sp_chistics= old_lex->sp_chistics; + lex->stmt_lex= old_lex; parse_status= parse_sql(thd, &parser_state, 0); + ((char*) &unparsed_spec.str[unparsed_spec.length])[0]= save_end; with_select= lex->first_select_lex(); + if (parse_status) goto err; @@ -888,6 +911,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, with_select)); if (check_dependencies_in_with_clauses(lex->with_clauses_list)) res= NULL; + lex->sphead= NULL; // in order not to delete lex->sphead lex_end(lex); err: if (arena) @@ -1262,7 +1286,7 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel, With_element *with_elem= unit->with_element; if (encountered & with_elem->get_elem_map()) unrestricted|= with_elem->mutually_recursive; - else + else if (with_elem ==this) encountered|= with_elem->get_elem_map(); } } diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 4a194b2a38f..03c697bf746 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -90,6 +90,11 @@ private: It used to build clones of the specification if they are needed. */ LEX_CSTRING unparsed_spec; + /* Offset of the specification in the input string */ + my_ptrdiff_t unparsed_spec_offset; + + /* True if the with element is used a prepared statement */ + bool stmt_prepare_mode; /* Return the map where 1 is set only in the position for this element */ table_map get_elem_map() { return (table_map) 1 << number; } @@ -114,7 +119,14 @@ public: for the definition of this element */ bool is_recursive; - + /* + For a simple recursive CTE: the number of references to the CTE from + outside of the CTE specification. + For a CTE mutually recursive with other CTEs : the total number of + references to all these CTEs outside of their specification. + Each of these mutually recursive CTEs has the same value in this field. + */ + uint rec_outer_references; /* Any non-recursive select in the specification of a recursive with element is a called anchor. In the case mutually recursive @@ -158,7 +170,7 @@ public: top_level_dep_map(0), sq_rec_ref(NULL), next_mutually_recursive(NULL), references(0), query_name(name), column_list(list), spec(unit), - is_recursive(false), with_anchor(false), + is_recursive(false), rec_outer_references(0), with_anchor(false), level(0), rec_result(NULL) { unit->with_element= this; } @@ -185,7 +197,8 @@ public: TABLE_LIST *find_first_sq_rec_ref_in_select(st_select_lex *sel); - bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end); + bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end, + my_ptrdiff_t spec_offset); st_select_lex_unit *clone_parsed_spec(THD *thd, TABLE_LIST *with_table); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 80ee4a22419..148c2371a6b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -323,12 +323,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_ASSERT(table); DBUG_ASSERT(!conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - DBUG_ASSERT(!conds); - conds= table_list->on_expr; - table_list->on_expr= NULL; + // conds could be cached from previous SP call + if (!conds) + { + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + conds= table_list->on_expr; + table_list->on_expr= NULL; + } } if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT)) @@ -932,7 +936,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, List<Item> all_fields; *delete_while_scanning= true; - thd->lex->allow_sum_func= 0; + thd->lex->allow_sum_func.clear_all(); if (setup_tables_and_check_access(thd, &thd->lex->first_select_lex()->context, &thd->lex->first_select_lex()-> @@ -1212,6 +1216,7 @@ multi_delete::~multi_delete() { TABLE *table= table_being_deleted->table; table->no_keyread=0; + table->no_cache= 0; } for (uint counter= 0; counter < num_of_tables; counter++) diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index d65969d2160..878aa715b84 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -1107,6 +1107,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_ASSERT(derived->table && derived->table->is_created()); select_unit *derived_result= derived->derived_result; SELECT_LEX *save_current_select= lex->current_select; + bool derived_recursive_is_filled= false; if (unit->executed && !derived_is_recursive && (unit->uncacheable & UNCACHEABLE_DEPENDENT)) @@ -1135,6 +1136,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) { /* In this case all iteration are performed */ res= derived->fill_recursive(thd); + derived_recursive_is_filled= true; } } else if (unit->is_unit_op()) @@ -1190,7 +1192,8 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) } } err: - if (res || (!lex->describe && !derived_is_recursive && !unit->uncacheable)) + if (res || (!lex->describe && !unit->uncacheable && + (!derived_is_recursive || derived_recursive_is_filled))) unit->cleanup(); lex->current_select= save_current_select; diff --git a/sql/sql_error.h b/sql/sql_error.h index 88355e529b5..6586c49a125 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -18,11 +18,11 @@ #define SQL_ERROR_H #include "sql_list.h" /* Sql_alloc, MEM_ROOT, list */ -#include "m_string.h" /* LEX_STRING */ -#include "sql_string.h" /* String */ -#include "sql_plist.h" /* I_P_List */ -#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */ -#include "my_time.h" /* MYSQL_TIME */ +#include "sql_type_int.h" // Longlong_hybrid +#include "sql_string.h" /* String */ +#include "sql_plist.h" /* I_P_List */ +#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */ +#include "my_time.h" /* MYSQL_TIME */ #include "decimal.h" class THD; @@ -814,11 +814,48 @@ private: extern char *err_conv(char *buff, uint to_length, const char *from, uint from_length, CHARSET_INFO *from_cs); -class ErrConv +class ErrBuff { protected: mutable char err_buffer[MYSQL_ERRMSG_SIZE]; public: + ErrBuff() + { + err_buffer[0]= '\0'; + } + const char *ptr() const { return err_buffer; } + const char *set_longlong(const Longlong_hybrid &nr) const + { + return nr.is_unsigned() ? ullstr(nr.value(), err_buffer) : + llstr(nr.value(), err_buffer); + } + const char *set_double(double nr) const + { + my_gcvt(nr, MY_GCVT_ARG_DOUBLE, sizeof(err_buffer), err_buffer, 0); + return err_buffer; + } + const char *set_decimal(const decimal_t *d) const + { + int len= sizeof(err_buffer); + decimal2string(d, err_buffer, &len, 0, 0, ' '); + return err_buffer; + } + const char *set_str(const char *str, size_t len, CHARSET_INFO *cs) const + { + DBUG_ASSERT(len < UINT_MAX32); + return err_conv(err_buffer, (uint) sizeof(err_buffer), str, (uint) len, cs); + } + const char *set_mysql_time(const MYSQL_TIME *ltime) const + { + my_TIME_to_str(ltime, err_buffer, AUTO_SEC_PART_DIGITS); + return err_buffer; + } +}; + + +class ErrConv: public ErrBuff +{ +public: ErrConv() {} virtual ~ErrConv() {} virtual const char *ptr() const = 0; @@ -838,22 +875,18 @@ public: : ErrConv(), str(s->ptr()), len(s->length()), cs(s->charset()) {} const char *ptr() const { - DBUG_ASSERT(len < UINT_MAX32); - return err_conv(err_buffer, (uint) sizeof(err_buffer), str, (uint) len, cs); + return set_str(str, len, cs); } }; -class ErrConvInteger : public ErrConv +class ErrConvInteger : public ErrConv, public Longlong_hybrid { - longlong m_value; - bool m_unsigned; public: - ErrConvInteger(longlong num_arg, bool unsigned_flag= false) : - ErrConv(), m_value(num_arg), m_unsigned(unsigned_flag) {} + ErrConvInteger(const Longlong_hybrid &nr) + : ErrConv(), Longlong_hybrid(nr) { } const char *ptr() const { - return m_unsigned ? ullstr(m_value, err_buffer) : - llstr(m_value, err_buffer); + return set_longlong(static_cast<Longlong_hybrid>(*this)); } }; @@ -864,8 +897,7 @@ public: ErrConvDouble(double num_arg) : ErrConv(), num(num_arg) {} const char *ptr() const { - my_gcvt(num, MY_GCVT_ARG_DOUBLE, sizeof(err_buffer), err_buffer, 0); - return err_buffer; + return set_double(num); } }; @@ -876,8 +908,7 @@ public: ErrConvTime(const MYSQL_TIME *ltime_arg) : ErrConv(), ltime(ltime_arg) {} const char *ptr() const { - my_TIME_to_str(ltime, err_buffer, AUTO_SEC_PART_DIGITS); - return err_buffer; + return set_mysql_time(ltime); } }; @@ -888,9 +919,7 @@ public: ErrConvDecimal(const decimal_t *d_arg) : ErrConv(), d(d_arg) {} const char *ptr() const { - int len= sizeof(err_buffer); - decimal2string(d, err_buffer, &len, 0, 0, ' '); - return err_buffer; + return set_decimal(d); } }; diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc index 1b64819732c..b7da889340f 100644 --- a/sql/sql_get_diagnostics.cc +++ b/sql/sql_get_diagnostics.cc @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA */ #include "mariadb.h" #include "sql_list.h" // Sql_alloc, List, List_iterator diff --git a/sql/sql_get_diagnostics.h b/sql/sql_get_diagnostics.h index f34820757f5..6f1652bb146 100644 --- a/sql/sql_get_diagnostics.h +++ b/sql/sql_get_diagnostics.h @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA */ #ifndef SQL_GET_DIAGNOSTICS_H #define SQL_GET_DIAGNOSTICS_H diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 51acf10a98a..127b4b10eb4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2518,7 +2518,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) The thread could be killed with an error message if di->handle_inserts() or di->open_and_lock_table() fails. The thread could be killed without an error message if - killed using kill_delayed_threads_for_table(). + killed using THD::notify_shared_lock() or + kill_delayed_threads_for_table(). */ if (!thd.is_error()) my_message(ER_QUERY_INTERRUPTED, ER_THD(&thd, ER_QUERY_INTERRUPTED), diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 20b1eb413cf..2f5a845d18c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -693,6 +693,7 @@ void LEX::start(THD *thd_arg) curr_with_clause= 0; with_clauses_list= 0; with_clauses_list_last_next= &with_clauses_list; + clone_spec_offset= 0; create_view= NULL; field_list.empty(); value_list.empty(); @@ -744,7 +745,7 @@ void LEX::start(THD *thd_arg) profile_options= PROFILE_NONE; nest_level= 0; builtin_select.nest_level_base= &unit; - allow_sum_func= 0; + allow_sum_func.clear_all(); in_sum_func= NULL; used_tables= 0; @@ -2388,7 +2389,7 @@ void st_select_lex::init_select() m_non_agg_field_used= false; m_agg_func_used= false; m_custom_agg_func_used= false; - name_visibility_map= 0; + name_visibility_map.clear_all(); with_dep= 0; join= 0; lock_type= TL_READ_DEFAULT; @@ -3169,7 +3170,7 @@ LEX::LEX() INITIAL_LEX_PLUGIN_LIST_SIZE, 0); reset_query_tables_list(TRUE); mi.init(); - init_dynamic_array2(&delete_gtid_domain, sizeof(ulong*), + init_dynamic_array2(&delete_gtid_domain, sizeof(uint32), gtid_domain_static_buffer, initial_gtid_domain_buffer_size, initial_gtid_domain_buffer_size, 0); @@ -4765,18 +4766,18 @@ void SELECT_LEX::increase_derived_records(ha_rows records) return; } - select_unit *result= (select_unit*)unit->result; + select_result *result= unit->result; switch (linkage) { case INTERSECT_TYPE: // result of intersect can't be more then one of components - set_if_smaller(result->records, records); + set_if_smaller(result->est_records, records); case EXCEPT_TYPE: // in worse case none of record will be removed break; default: // usual UNION - result->records+= records; + result->est_records+= records; break; } } @@ -5249,6 +5250,9 @@ LEX::create_unit(SELECT_LEX *first_sel) SELECT_LEX_UNIT *unit; DBUG_ENTER("LEX::create_unit"); + if (first_sel->master_unit()) + DBUG_RETURN(first_sel->master_unit()); + if (!(unit= alloc_unit())) DBUG_RETURN(NULL); @@ -6937,6 +6941,14 @@ Item *LEX::make_item_sysvar(THD *thd, } +static bool param_push_or_clone(THD *thd, LEX *lex, Item_param *item) +{ + return !lex->clone_spec_offset ? + lex->param_list.push_back(item, thd->mem_root) : + item->add_as_clone(thd); +} + + Item_param *LEX::add_placeholder(THD *thd, const LEX_CSTRING *name, const char *start, const char *end) { @@ -6953,7 +6965,7 @@ Item_param *LEX::add_placeholder(THD *thd, const LEX_CSTRING *name, Query_fragment pos(thd, sphead, start, end); Item_param *item= new (thd->mem_root) Item_param(thd, name, pos.pos(), pos.length()); - if (unlikely(!item) || unlikely(param_list.push_back(item, thd->mem_root))) + if (unlikely(!item) || unlikely(param_push_or_clone(thd, this, item))) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); return NULL; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 24788158d26..8dab2a06973 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -33,6 +33,10 @@ #include "sql_tvc.h" #include "item.h" +/* Used for flags of nesting constructs */ +#define SELECT_NESTING_MAP_SIZE 64 +typedef Bitmap<SELECT_NESTING_MAP_SIZE> nesting_map; + /* YACC and LEX Definitions */ @@ -2935,6 +2939,11 @@ public: with clause in the current statement */ With_clause **with_clauses_list_last_next; + /* + When a copy of a with element is parsed this is set to the offset of + the with element in the input string, otherwise it's set to 0 + */ + my_ptrdiff_t clone_spec_offset; Create_view_info *create_view; @@ -3280,7 +3289,7 @@ public: */ DYNAMIC_ARRAY delete_gtid_domain; static const ulong initial_gtid_domain_buffer_size= 16; - ulong gtid_domain_static_buffer[initial_gtid_domain_buffer_size]; + uint32 gtid_domain_static_buffer[initial_gtid_domain_buffer_size]; inline void set_limit_rows_examined() { diff --git a/sql/sql_list.h b/sql/sql_list.h index 9f4eaae43f1..27827b42be5 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -278,10 +278,13 @@ public: */ inline void swap(base_list &rhs) { + list_node **rhs_last=rhs.last; swap_variables(list_node *, first, rhs.first); - swap_variables(list_node **, last, rhs.last); swap_variables(uint, elements, rhs.elements); + rhs.last= last == &first ? &rhs.first : last; + last = rhs_last == &rhs.first ? &first : rhs_last; } + inline list_node* last_node() { return *last; } inline list_node* first_node() { return first;} inline void *head() { return first->info; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1baa26ab465..1f9cd305847 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -109,6 +109,7 @@ #include "../storage/maria/ha_maria.h" #endif +#include "wsrep.h" #include "wsrep_mysqld.h" #include "wsrep_thd.h" @@ -736,6 +737,9 @@ void init_update_queries(void) /* We don't want to replicate DROP for temp tables in row format */ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; + /* We don't want to replicate CREATE/DROP INDEX for temp tables in row format */ + sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; + sql_command_flags[SQLCOM_DROP_INDEX]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* One can change replication mode with SET */ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; @@ -1597,10 +1601,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (thd->wsrep_conflict_state == ABORTED && command != COM_STMT_CLOSE && command != COM_QUIT) { + mysql_mutex_unlock(&thd->LOCK_thd_data); my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction", MYF(0)); WSREP_DEBUG("Deadlock error for: %s", thd->query()); - mysql_mutex_unlock(&thd->LOCK_thd_data); thd->reset_killed(); thd->mysys_var->abort = 0; thd->wsrep_conflict_state = NO_CONFLICT; @@ -2562,6 +2566,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, case SCH_TABLE_NAMES: case SCH_TABLES: + case SCH_CHECK_CONSTRAINTS: case SCH_VIEWS: case SCH_TRIGGERS: case SCH_EVENTS: @@ -3006,7 +3011,7 @@ static int mysql_create_routine(THD *thd, LEX *lex) if (sp_process_definer(thd)) return true; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -3076,7 +3081,7 @@ static int mysql_create_routine(THD *thd, LEX *lex) return false; } #ifdef WITH_WSREP -error: /* Used by WSREP_TO_ISOLATION_BEGIN */ +wsrep_error_label: #endif return true; } @@ -6297,7 +6302,10 @@ end_with_restore_list: goto finish; error: - res= TRUE; +#ifdef WITH_WSREP +wsrep_error_label: +#endif + res= true; finish: @@ -7590,8 +7598,14 @@ void THD::reset_for_next_command(bool do_clear_error) DBUG_ASSERT(!in_sub_stmt); if (likely(do_clear_error)) + { clear_error(1); - + /* + The following variable can't be reset in clear_error() as + clear_error() is called during auto_repair of table + */ + error_printed_to_log= 0; + } free_list= 0; /* We also assign stmt_lex in lex_start(), but during bootstrap this @@ -7863,7 +7877,9 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, "WAIT_FOR wsrep_retry_autocommit_continue"; DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act))); }); + WSREP_DEBUG("Retry autocommit query: %s", thd->query()); } + mysql_parse(thd, rawbuf, length, parser_state, is_com_multi, is_next_command); @@ -7895,36 +7911,46 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, thd->lex->sql_command != SQLCOM_SELECT && (thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit)) { - WSREP_DEBUG("wsrep retrying AC query: %s", + mysql_mutex_unlock(&thd->LOCK_thd_data); + WSREP_DEBUG("wsrep retrying AC query: %s", (thd->query()) ? thd->query() : "void"); /* Performance Schema Interface instrumentation, end */ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da()); thd->m_statement_psi= NULL; thd->m_digest= NULL; + // Released thd->LOCK_thd_data above as below could end up + // close_thread_tables()/close_open_tables()/close_thread_table()/mysql_mutex_lock(&thd->LOCK_thd_data) close_thread_tables(thd); + mysql_mutex_lock(&thd->LOCK_thd_data); thd->wsrep_conflict_state= RETRY_AUTOCOMMIT; thd->wsrep_retry_counter++; // grow wsrep_copy_query(thd); thd->set_time(); parser_state->reset(rawbuf, length); + mysql_mutex_unlock(&thd->LOCK_thd_data); } else { - WSREP_DEBUG("%s, thd: %lld is_AC: %d, retry: %lu - %lu SQL: %s", - (thd->wsrep_conflict_state == ABORTED) ? + mysql_mutex_unlock(&thd->LOCK_thd_data); + // This does dirty read to wsrep variables but it is only a debug code + WSREP_DEBUG("%s, thd: %lld is_AC: %d, retry: %lu - %lu SQL: %s", + (thd->wsrep_conflict_state == ABORTED) ? "BF Aborted" : "cert failure", (longlong) thd->thread_id, is_autocommit, - thd->wsrep_retry_counter, + thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, thd->query()); my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction", MYF(0)); + + mysql_mutex_lock(&thd->LOCK_thd_data); thd->wsrep_conflict_state= NO_CONFLICT; if (thd->wsrep_conflict_state != REPLAYING) thd->wsrep_retry_counter= 0; // reset + mysql_mutex_unlock(&thd->LOCK_thd_data); } - mysql_mutex_unlock(&thd->LOCK_thd_data); + thd->reset_killed(); } else @@ -7960,6 +7986,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, #endif /* WITH_WSREP */ } + /* When you modify mysql_parse(), you may need to modify mysql_test_parse_for_slave() in this same file. diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 3133b94fa5b..96e07b19dd8 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -853,7 +853,7 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, const bool save_agg_field= thd->lex->current_select->non_agg_field_used(); const bool save_agg_func= thd->lex->current_select->agg_func_used(); const nesting_map saved_allow_sum_func= thd->lex->allow_sum_func; - thd->lex->allow_sum_func= 0; + thd->lex->allow_sum_func.clear_all(); if (likely(!(error= func_expr->fix_fields_if_needed(thd, (Item**)&func_expr)))) func_expr->walk(&Item::post_fix_fields_part_expr_processor, 0, NULL); @@ -1559,7 +1559,7 @@ static bool check_vers_constants(THD *thd, partition_info *part_info) my_tz_OFFSET0->gmt_sec_to_TIME(<ime, vers_info->interval.start); while ((el= it++)->id < hist_parts) { - if (date_add_interval(<ime, vers_info->interval.type, + if (date_add_interval(thd, <ime, vers_info->interval.type, vers_info->interval.step)) goto err; uint error= 0; @@ -2571,6 +2571,18 @@ static int add_key_with_algorithm(String *str, partition_info *part_info) return err; } +char *generate_partition_syntax_for_frm(THD *thd, partition_info *part_info, + uint *buf_length, + HA_CREATE_INFO *create_info, + Alter_info *alter_info) +{ + sql_mode_t old_mode= thd->variables.sql_mode; + thd->variables.sql_mode &= ~MODE_ANSI_QUOTES; + char *res= generate_partition_syntax(thd, part_info, buf_length, + true, create_info, alter_info); + thd->variables.sql_mode= old_mode; + return res; +} /* Generate the partition syntax from the partition data structure. @@ -8205,7 +8217,7 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, field->type() == MYSQL_TYPE_DATETIME)) { /* Monotonic, but return NULL for dates with zeros in month/day. */ - zero_in_start_date= field->get_date(&start_date, 0); + zero_in_start_date= field->get_date(&start_date, date_mode_t(0)); DBUG_PRINT("info", ("zero start %u %04d-%02d-%02d", zero_in_start_date, start_date.year, start_date.month, start_date.day)); @@ -8229,7 +8241,7 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, !part_info->part_expr->null_value) { MYSQL_TIME end_date; - bool zero_in_end_date= field->get_date(&end_date, 0); + bool zero_in_end_date= field->get_date(&end_date, date_mode_t(0)); /* This is an optimization for TO_DAYS()/TO_SECONDS() to avoid scanning the NULL partition for ranges that cannot include a date with 0 as diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 170ae8ccee1..83cac8f24ba 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -276,6 +276,10 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info, bool show_partition_options, HA_CREATE_INFO *create_info, Alter_info *alter_info); +char *generate_partition_syntax_for_frm(THD *thd, partition_info *part_info, + uint *buf_length, + HA_CREATE_INFO *create_info, + Alter_info *alter_info); bool verify_data_with_partition(TABLE *table, TABLE *part_table, uint32 part_id); bool compare_partition_options(HA_CREATE_INFO *table_create_info, diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 2cc3f247e95..71ab7477391 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2014, SkySQL Ab. - Copyright (c) 2016, MariaDB Corporation + Copyright (c) 2016, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -99,7 +99,7 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd) thd->prepare_logs_for_admin_command(); DBUG_RETURN(exchange_partition(thd, first_table, &alter_info)); #ifdef WITH_WSREP - error: + wsrep_error_label: /* handle errors in TO_ISOLATION here */ DBUG_RETURN(true); #endif /* WITH_WSREP */ diff --git a/sql/sql_plist.h b/sql/sql_plist.h index bb9889cc534..4d279af7a0d 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -184,7 +184,12 @@ public: list= &a; current= a.m_first; } - /* Operator for it++ */ + /** + Operator for it++ + + @note since we save next element pointer, caller may remove current element. + Such modification doesn't invalidate iterator. + */ inline T* operator++(int) { T *result= current; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 8c231d9b8f7..d448b7b9e02 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -280,6 +280,7 @@ struct st_mysql_sys_var MYSQL_PLUGIN_VAR_HEADER; }; +enum install_status { INSTALL_GOOD, INSTALL_FAIL_WARN_OK, INSTALL_FAIL_NOT_OK }; /* sys_var class for access to all plugin variables visible to the user */ @@ -1077,7 +1078,7 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) NOTE Requires that a write-lock is held on LOCK_system_variables_hash */ -static bool plugin_add(MEM_ROOT *tmp_root, +static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists, const LEX_CSTRING *name, LEX_CSTRING *dl, myf MyFlags) { struct st_plugin_int tmp, *maybe_dupe; @@ -1088,14 +1089,16 @@ static bool plugin_add(MEM_ROOT *tmp_root, if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN)) { + if (if_not_exists) + MyFlags|= ME_NOTE; my_error(ER_PLUGIN_INSTALLED, MyFlags, name->str); - DBUG_RETURN(TRUE); + DBUG_RETURN(if_not_exists ? INSTALL_FAIL_WARN_OK : INSTALL_FAIL_NOT_OK); } /* Clear the whole struct to catch future extensions. */ bzero((char*) &tmp, sizeof(tmp)); fix_dl_name(tmp_root, dl); if (! (tmp.plugin_dl= plugin_dl_add(dl, MyFlags))) - DBUG_RETURN(TRUE); + DBUG_RETURN(INSTALL_FAIL_NOT_OK); /* Find plugin by name */ for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++) { @@ -1121,7 +1124,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, if (plugin->name != maybe_dupe->plugin->name) { my_error(ER_UDF_EXISTS, MyFlags, plugin->name); - DBUG_RETURN(TRUE); + DBUG_RETURN(INSTALL_FAIL_NOT_OK); } dupes++; continue; // already installed @@ -1173,7 +1176,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, init_alloc_root(&tmp_plugin_ptr->mem_root, "plugin", 4096, 4096, MYF(0)); if (name->str) - DBUG_RETURN(FALSE); // all done + DBUG_RETURN(INSTALL_GOOD); // all done oks++; tmp.plugin_dl->ref_count++; @@ -1191,7 +1194,9 @@ err: my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, name->str); plugin_dl_del(tmp.plugin_dl); - DBUG_RETURN(errs > 0 || oks + dupes == 0); + if (errs > 0 || oks + dupes == 0) + DBUG_RETURN(INSTALL_FAIL_NOT_OK); + DBUG_RETURN(INSTALL_GOOD); } static void plugin_variables_deinit(struct st_plugin_int *plugin) @@ -1847,7 +1852,7 @@ static void plugin_load(MEM_ROOT *tmp_root) the mutex here to satisfy the assert */ mysql_mutex_lock(&LOCK_plugin); - plugin_add(tmp_root, &name, &dl, MYF(ME_ERROR_LOG)); + plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG)); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_unlock(&LOCK_plugin); } @@ -1870,7 +1875,7 @@ end: static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) { char buffer[FN_REFLEN]; - LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; + LEX_CSTRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; char *p= buffer; DBUG_ENTER("plugin_load_list"); while (list) @@ -1889,7 +1894,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) #ifndef __WIN__ case ':': /* can't use this as delimiter as it may be drive letter */ #endif - str->str[str->length]= '\0'; + p[-1]= 0; if (str == &name) // load all plugins in named module { if (!name.length) @@ -1902,16 +1907,16 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) mysql_mutex_lock(&LOCK_plugin); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); name.str= 0; // load everything - if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl, - MYF(ME_ERROR_LOG))) + if (plugin_add(tmp_root, false, &name, &dl, + MYF(ME_ERROR_LOG)) != INSTALL_GOOD) goto error; } else { free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_lock(&LOCK_plugin); - if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl, - MYF(ME_ERROR_LOG))) + if (plugin_add(tmp_root, false, &name, &dl, + MYF(ME_ERROR_LOG)) != INSTALL_GOOD) goto error; } mysql_mutex_unlock(&LOCK_plugin); @@ -1923,7 +1928,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) case '#': if (str == &name) { - name.str[name.length]= '\0'; + p[-1]= 0; str= &dl; str->str= p; continue; @@ -2146,7 +2151,7 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, TABLE_LIST tables; TABLE *table; LEX_CSTRING dl= *dl_arg; - bool error; + enum install_status error; int argc=orig_argc; char **argv=orig_argv; unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] = @@ -2194,12 +2199,14 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, mysql_audit_acquire_plugins(thd, event_class_mask); mysql_mutex_lock(&LOCK_plugin); - error= plugin_add(thd->mem_root, name, &dl, MYF(0)); - if (unlikely(error)) + error= plugin_add(thd->mem_root, thd->lex->create_info.if_not_exists(), + name, &dl, MYF(0)); + if (unlikely(error != INSTALL_GOOD)) goto err; if (name->str) - error= finalize_install(thd, table, name, &argc, argv); + error= finalize_install(thd, table, name, &argc, argv) + ? INSTALL_FAIL_NOT_OK : INSTALL_GOOD; else { st_plugin_dl *plugin_dl= plugin_dl_find(&dl); @@ -2207,11 +2214,12 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, for (plugin= plugin_dl->plugins; plugin->info; plugin++) { LEX_CSTRING str= { plugin->name, strlen(plugin->name) }; - error|= finalize_install(thd, table, &str, &argc, argv); + if (finalize_install(thd, table, &str, &argc, argv)) + error= INSTALL_FAIL_NOT_OK; } } - if (unlikely(error)) + if (unlikely(error != INSTALL_GOOD)) { reap_needed= true; reap_plugins(); @@ -2220,11 +2228,11 @@ err: mysql_mutex_unlock(&LOCK_plugin); if (argv) free_defaults(argv); - DBUG_RETURN(error); + DBUG_RETURN(error == INSTALL_FAIL_NOT_OK); #ifdef WITH_WSREP -error: - DBUG_RETURN(TRUE); -#endif /* WITH_WSREP */ +wsrep_error_label: + DBUG_RETURN(true); +#endif } @@ -2236,8 +2244,9 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name) if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) || plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING)) { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str); - return 1; + myf MyFlags= thd->lex->if_exists() ? ME_NOTE : 0; + my_error(ER_SP_DOES_NOT_EXIST, MyFlags, "PLUGIN", name->str); + return !MyFlags; } if (!plugin->plugin_dl) { @@ -2300,7 +2309,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) @@ -2359,8 +2368,9 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, } else { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SONAME", dl.str); - error= true; + myf MyFlags= thd->lex->if_exists() ? ME_NOTE : 0; + my_error(ER_SP_DOES_NOT_EXIST, MyFlags, "SONAME", dl.str); + error|= !MyFlags; } } reap_plugins(); @@ -2368,9 +2378,9 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, mysql_mutex_unlock(&LOCK_plugin); DBUG_RETURN(error); #ifdef WITH_WSREP -error: - DBUG_RETURN(TRUE); -#endif /* WITH_WSREP */ +wsrep_error_label: + DBUG_RETURN(true); +#endif } diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index 47decf38973..8de53aa2161 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -181,7 +181,8 @@ static struct wsrep_service_st wsrep_handler = { wsrep_thd_ws_handle, wsrep_trx_is_aborting, wsrep_trx_order_before, - wsrep_unlock_rollback + wsrep_unlock_rollback, + wsrep_set_data_home_dir }; static struct thd_specifics_service_st thd_specifics_handler= diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c615356b354..6def798b1c7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -893,6 +893,7 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array, DBUG_RETURN(1); if (param->convert_str_value(stmt->thd)) DBUG_RETURN(1); /* out of memory */ + param->sync_clones(); } DBUG_RETURN(0); } @@ -941,6 +942,7 @@ static bool insert_bulk_params(Prepared_statement *stmt, } else DBUG_RETURN(1); // long is not supported here + param->sync_clones(); } DBUG_RETURN(0); } @@ -969,6 +971,7 @@ static bool set_conversion_functions(Prepared_statement *stmt, read_pos+= 2; (**it).unsigned_flag= MY_TEST(typecode & signed_bit); (*it)->setup_conversion(thd, (uchar) (typecode & 0xff)); + (*it)->sync_clones(); } *data= read_pos; DBUG_RETURN(0); @@ -1039,6 +1042,7 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query) if (param->has_no_value()) DBUG_RETURN(1); } + param->sync_clones(); } if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ @@ -1081,6 +1085,7 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt, String *query) if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ + param->sync_clones(); } if (acc.finalize()) DBUG_RETURN(1); @@ -1136,7 +1141,11 @@ swap_parameter_array(Item_param **param_array_dst, Item_param **end= param_array_dst + param_count; for (; dst < end; ++src, ++dst) + { (*dst)->set_param_type_and_swap_value(*src); + (*dst)->sync_clones(); + (*src)->sync_clones(); + } } @@ -1167,6 +1176,7 @@ insert_params_from_actual_params(Prepared_statement *stmt, if (ps_param->save_in_param(stmt->thd, param) || param->convert_str_value(stmt->thd)) DBUG_RETURN(1); + param->sync_clones(); } DBUG_RETURN(0); } @@ -1209,6 +1219,8 @@ insert_params_from_actual_params_with_log(Prepared_statement *stmt, if (param->convert_str_value(thd)) DBUG_RETURN(1); + + param->sync_clones(); } if (acc.finalize()) DBUG_RETURN(1); @@ -1582,7 +1594,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, DBUG_RETURN(TRUE); if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, - DT_PREPARE | DT_CREATE)) + DT_INIT | DT_PREPARE | DT_CREATE)) DBUG_RETURN(TRUE); DBUG_RETURN(setup_fields(thd, Ref_ptr_array(), *values, COLUMNS_READ, 0, NULL, 0)); @@ -1614,7 +1626,7 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) || open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, - DT_PREPARE | DT_CREATE)) + DT_INIT | DT_PREPARE | DT_CREATE)) goto error; while ((var= it++)) @@ -1651,7 +1663,8 @@ static bool mysql_test_call_fields(Prepared_statement *stmt, if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) || - open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, DT_PREPARE)) + open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, + DT_INIT | DT_PREPARE)) goto err; while ((item= it++)) @@ -1777,7 +1790,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, MYSQL_OPEN_FORCE_SHARED_MDL, - DT_PREPARE | DT_CREATE)) + DT_INIT | DT_PREPARE | DT_CREATE)) DBUG_RETURN(TRUE); select_lex->context.resolve_in_select_list= TRUE; @@ -1798,7 +1811,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) */ if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, MYSQL_OPEN_FORCE_SHARED_MDL, - DT_PREPARE)) + DT_INIT | DT_PREPARE)) DBUG_RETURN(TRUE); } @@ -2025,7 +2038,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt) lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, - DT_PREPARE)) + DT_INIT | DT_PREPARE)) goto err; res= select_like_stmt_test(stmt, 0, 0); @@ -3030,7 +3043,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) lex->result->cleanup(); lex->result->set_thd(thd); } - lex->allow_sum_func= 0; + lex->allow_sum_func.clear_all(); lex->in_sum_func= NULL; DBUG_VOID_RETURN; } @@ -3201,7 +3214,7 @@ static void mysql_stmt_execute_common(THD *thd, sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size); /* Close connection socket; for use with client testing (Bug#43560). */ - DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio);); + DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_shutdown(thd->net.vio,SHUT_RD);); DBUG_VOID_RETURN; } diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 9bcb9a30a4c..abdf9d76d15 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -289,9 +289,18 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, */ if (tables) { + int err; for (TABLE_LIST *t= tables; t; t= t->next_local) - if (!find_table_for_mdl_upgrade(thd, t->db.str, t->table_name.str, false)) - return 1; + if (!find_table_for_mdl_upgrade(thd, t->db.str, t->table_name.str, &err)) + { + if (is_locked_view(thd, t)) + t->next_local= t->next_global; + else + { + my_error(err, MYF(0), t->table_name.str); + return 1; + } + } } else { @@ -616,4 +625,3 @@ static void disable_checkpoints(THD *thd) ha_checkpoint_state(1); // Disable checkpoints } } - diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 616fea0f401..8f55497d8d0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -293,6 +293,9 @@ static bool find_order_in_list(THD *, Ref_ptr_array, TABLE_LIST *, ORDER *, static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, table_map rem_tables); void set_postjoin_aggr_write_func(JOIN_TAB *tab); + +static Item **get_sargable_cond(JOIN *join, TABLE *table); + #ifndef DBUG_OFF /* @@ -643,7 +646,7 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, const bool saved_non_agg_field_used= select->non_agg_field_used(); DBUG_ENTER("setup_without_group"); - thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level); + thd->lex->allow_sum_func.clear_bit(select->nest_level); res= setup_conds(thd, tables, leaves, conds); if (thd->lex->current_select->first_cond_optimization) { @@ -656,18 +659,18 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, /* it's not wrong to have non-aggregated columns in a WHERE */ select->set_non_agg_field_used(saved_non_agg_field_used); - thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level; + thd->lex->allow_sum_func.set_bit(select->nest_level); save_place= thd->lex->current_select->context_analysis_place; thd->lex->current_select->context_analysis_place= IN_ORDER_BY; res= res || setup_order(thd, ref_pointer_array, tables, fields, all_fields, order); - thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level); + thd->lex->allow_sum_func.clear_bit(select->nest_level); thd->lex->current_select->context_analysis_place= IN_GROUP_BY; res= res || setup_group(thd, ref_pointer_array, tables, fields, all_fields, group, hidden_group_fields); thd->lex->current_select->context_analysis_place= save_place; - thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level; + thd->lex->allow_sum_func.set_bit(select->nest_level); res= res || setup_windows(thd, ref_pointer_array, tables, fields, all_fields, win_specs, win_funcs); thd->lex->allow_sum_func= save_allow_sum_func; @@ -1115,7 +1118,7 @@ JOIN::prepare(TABLE_LIST *tables_init, select_lex->master_unit()->global_parameters()) { nesting_map save_allow_sum_func= thd->lex->allow_sum_func; - thd->lex->allow_sum_func|= (nesting_map)1 << select_lex->nest_level; + thd->lex->allow_sum_func.set_bit(select_lex->nest_level); thd->where= "order clause"; for (ORDER *order= select_lex->order_list.first; order; order= order->next) { @@ -1133,7 +1136,7 @@ JOIN::prepare(TABLE_LIST *tables_init, { nesting_map save_allow_sum_func= thd->lex->allow_sum_func; thd->where="having clause"; - thd->lex->allow_sum_func|= (nesting_map)1 << select_lex_arg->nest_level; + thd->lex->allow_sum_func.set_bit(select_lex_arg->nest_level); select_lex->having_fix_field= 1; /* Wrap alone field in HAVING clause in case it will be outer field @@ -1643,6 +1646,13 @@ JOIN::optimize_inner() if (optimize_constant_subqueries()) DBUG_RETURN(1); + if (conds && conds->with_subquery()) + (void) conds->walk(&Item::cleanup_is_expensive_cache_processor, + 0, (void *) 0); + if (having && having->with_subquery()) + (void) having->walk(&Item::cleanup_is_expensive_cache_processor, + 0, (void *) 0); + List<Item> eq_list; if (setup_degenerate_jtbm_semi_joins(this, join_list, eq_list)) @@ -1688,8 +1698,14 @@ JOIN::optimize_inner() conds= optimize_cond(this, conds, join_list, FALSE, &cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS); - if (thd->lex->sql_command == SQLCOM_SELECT && - optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY)) + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from optimize_cond")); + DBUG_RETURN(1); + } + + if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY)) { TABLE_LIST *tbl; List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); @@ -1714,8 +1730,7 @@ JOIN::optimize_inner() DBUG_RETURN(TRUE); } - if (thd->lex->sql_command == SQLCOM_SELECT && - optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)) + if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)) { TABLE_LIST *tbl; List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); @@ -1753,13 +1768,6 @@ JOIN::optimize_inner() DBUG_RETURN(1); } - if (unlikely(thd->is_error())) - { - error= 1; - DBUG_PRINT("error",("Error from optimize_cond")); - DBUG_RETURN(1); - } - { having= optimize_cond(this, having, join_list, TRUE, &having_value, &having_equal); @@ -1810,19 +1818,9 @@ JOIN::optimize_inner() List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); while ((tbl= li++)) { - /* - If tbl->embedding!=NULL that means that this table is in the inner - part of the nested outer join, and we can't do partition pruning - (TODO: check if this limitation can be lifted) - */ - if (!tbl->embedding || - (tbl->embedding && tbl->embedding->sj_on_expr)) - { - Item *prune_cond= tbl->on_expr? tbl->on_expr : conds; - tbl->table->all_partitions_pruned_away= prune_partitions(thd, - tbl->table, - prune_cond); - } + Item **prune_cond= get_sargable_cond(this, tbl->table); + tbl->table->all_partitions_pruned_away= + prune_partitions(thd, tbl->table, *prune_cond); } } #endif @@ -1919,6 +1917,14 @@ JOIN::optimize_inner() error= 1; DBUG_RETURN(1); } + if (!group_list) + { + /* The output has only one row */ + order=0; + simple_order=1; + group_optimized_away= 1; + select_distinct=0; + } } /* Calculate how to do the join */ @@ -3159,7 +3165,7 @@ bool JOIN::make_aggr_tables_info() remove_duplicates() assumes there is a preceding computation step (and in the degenerate join, there's none) */ - if (top_join_tab_count) + if (top_join_tab_count && tables_list) curr_tab->distinct= true; having= NULL; @@ -3805,7 +3811,7 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite, If there is SELECT in this statement with the same number it must be the same SELECT */ - DBUG_ASSERT(select_lex->select_number == UINT_MAX || + DBUG_SLOW_ASSERT(select_lex->select_number == UINT_MAX || select_lex->select_number == INT_MAX || !output || !output->get_select(select_lex->select_number) || @@ -4342,6 +4348,84 @@ struct SARGABLE_PARAM }; +/* + Mark all tables inside a join nest as constant. + + @detail This is called when there is a local "Impossible WHERE" inside + a multi-table LEFT JOIN. +*/ + +void mark_join_nest_as_const(JOIN *join, + TABLE_LIST *join_nest, + table_map *found_const_table_map, + uint *const_count) +{ + List_iterator<TABLE_LIST> it(join_nest->nested_join->join_list); + TABLE_LIST *tbl; + while ((tbl= it++)) + { + if (tbl->nested_join) + { + mark_join_nest_as_const(join, tbl, found_const_table_map, const_count); + continue; + } + JOIN_TAB *tab= tbl->table->reginfo.join_tab; + + if (!(join->const_table_map & tab->table->map)) + { + tab->type= JT_CONST; + tab->info= ET_IMPOSSIBLE_ON_CONDITION; + tab->table->const_table= 1; + + join->const_table_map|= tab->table->map; + *found_const_table_map|= tab->table->map; + set_position(join,(*const_count)++,tab,(KEYUSE*) 0); + mark_as_null_row(tab->table); // All fields are NULL + } + } +} + + +/* + @brief Get the condition that can be used to do range analysis/partition + pruning/etc + + @detail + Figure out which condition we can use: + - For INNER JOIN, we use the WHERE, + - "t1 LEFT JOIN t2 ON ..." uses t2's ON expression + - "t1 LEFT JOIN (...) ON ..." uses the join nest's ON expression. +*/ + +static Item **get_sargable_cond(JOIN *join, TABLE *table) +{ + Item **retval; + if (table->pos_in_table_list->on_expr) + { + /* + This is an inner table from a single-table LEFT JOIN, "t1 LEFT JOIN + t2 ON cond". Use the condition cond. + */ + retval= &table->pos_in_table_list->on_expr; + } + else if (table->pos_in_table_list->embedding && + !table->pos_in_table_list->embedding->sj_on_expr) + { + /* + This is the inner side of a multi-table outer join. Use the + appropriate ON expression. + */ + retval= &(table->pos_in_table_list->embedding->on_expr); + } + else + { + /* The table is not inner wrt some LEFT JOIN. Use the WHERE clause */ + retval= &join->conds; + } + return retval; +} + + /** Calculate the best possible join and initialize the join structure. @@ -4619,8 +4703,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, int ref_changed; do { - more_const_tables_found: ref_changed = 0; + more_const_tables_found: found_ref=0; /* @@ -4789,7 +4873,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, } } } - } while (join->const_table_map & found_ref && ref_changed); + } while (ref_changed); join->sort_by_table= get_sort_by_table(join->order, join->group_list, join->select_lex->leaf_tables, @@ -4913,39 +4997,38 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, /* Perform range analysis if there are keys it could use (1). - Don't do range analysis if we're on the inner side of an outer join (2). - Do range analysis if we're on the inner side of a semi-join (3). - Don't do range analysis for materialized subqueries (4). - Don't do range analysis for materialized derived tables (5) + Don't do range analysis for materialized subqueries (2). + Don't do range analysis for materialized derived tables (3) */ if ((!s->const_keys.is_clear_all() || !bitmap_is_clear_all(&s->table->cond_set)) && // (1) - (!s->table->pos_in_table_list->embedding || // (2) - (s->table->pos_in_table_list->embedding && // (3) - s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3) - !s->table->is_filled_at_execution() && // (4) - !(s->table->pos_in_table_list->derived && // (5) - s->table->pos_in_table_list->is_materialized_derived())) // (5) + !s->table->is_filled_at_execution() && // (2) + !(s->table->pos_in_table_list->derived && // (3) + s->table->pos_in_table_list->is_materialized_derived())) // (3) { bool impossible_range= FALSE; ha_rows records= HA_POS_ERROR; SQL_SELECT *select= 0; + Item **sargable_cond= NULL; if (!s->const_keys.is_clear_all()) { + sargable_cond= get_sargable_cond(join, s->table); + select= make_select(s->table, found_const_table_map, found_const_table_map, - *s->on_expr_ref ? *s->on_expr_ref : join->conds, + *sargable_cond, (SORT_INFO*) 0, 1, &error); if (!select) goto error; records= get_quick_record_count(join->thd, select, s->table, &s->const_keys, join->row_limit); - /* Range analyzer could modify the condition. */ - if (*s->on_expr_ref) - *s->on_expr_ref= select->cond; - else - join->conds= select->cond; + + /* + Range analyzer might have modified the condition. Put it the new + condition to where we got it from. + */ + *sargable_cond= select->cond; s->quick=select->quick; s->needed_reg=select->needed_reg; @@ -4958,10 +5041,11 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, } if (!impossible_range) { + if (!sargable_cond) + sargable_cond= get_sargable_cond(join, s->table); if (join->thd->variables.optimizer_use_condition_selectivity > 1) calculate_cond_selectivity_for_table(join->thd, s->table, - *s->on_expr_ref ? - s->on_expr_ref : &join->conds); + sargable_cond); if (s->table->reginfo.impossible_range) { impossible_range= TRUE; @@ -4970,23 +5054,33 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, } if (impossible_range) { - /* - Impossible WHERE or ON expression - In case of ON, we mark that the we match one empty NULL row. - In case of WHERE, don't set found_const_table_map to get the - caller to abort with a zero row result. - */ - join->const_table_map|= s->table->map; - set_position(join,const_count++,s,(KEYUSE*) 0); - s->type= JT_CONST; - s->table->const_table= 1; - if (*s->on_expr_ref) - { - /* Generate empty row */ - s->info= ET_IMPOSSIBLE_ON_CONDITION; - found_const_table_map|= s->table->map; - mark_as_null_row(s->table); // All fields are NULL - } + /* + Impossible WHERE or ON expression + In case of ON, we mark that the we match one empty NULL row. + In case of WHERE, don't set found_const_table_map to get the + caller to abort with a zero row result. + */ + TABLE_LIST *emb= s->table->pos_in_table_list->embedding; + if (emb && !emb->sj_on_expr) + { + /* Mark all tables in a multi-table join nest as const */ + mark_join_nest_as_const(join, emb, &found_const_table_map, + &const_count); + } + else + { + join->const_table_map|= s->table->map; + set_position(join,const_count++,s,(KEYUSE*) 0); + s->type= JT_CONST; + s->table->const_table= 1; + if (*s->on_expr_ref) + { + /* Generate empty row */ + s->info= ET_IMPOSSIBLE_ON_CONDITION; + found_const_table_map|= s->table->map; + mark_as_null_row(s->table); // All fields are NULL + } + } } if (records != HA_POS_ERROR) { @@ -6575,7 +6669,7 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab) Item_field *cur_item; key_map possible_keys(0); - if (join->group_list || join->simple_group) + if (join->group_list) { /* Collect all query fields referenced in the GROUP clause. */ for (cur_group= join->group_list; cur_group; cur_group= cur_group->next) (*cur_group->item)->walk(&Item::collect_item_field_processor, 0, @@ -7310,8 +7404,13 @@ best_access_path(JOIN *join, tmp+= (s->records - rnd_records)/(double) TIME_FOR_COMPARE; } } + double best_records= rnd_records; - tmp += s->startup_cost; + /* Splitting technique cannot be used with join cache */ + if (s->table->is_splittable()) + tmp+= s->table->get_materialization_cost(); + else + tmp+= s->startup_cost; filter= s->table->best_filter_for_current_join_order(MAX_KEY, rnd_records, @@ -7341,6 +7440,7 @@ best_access_path(JOIN *join, best_ref_depends_map= 0; best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map & join->outer_join))); + spl_plan= 0; } } @@ -9050,8 +9150,13 @@ bool JOIN_TAB::keyuse_is_valid_for_access_in_chosen_plan(JOIN *join, st_select_lex *sjm_sel= emb_sj_nest->sj_subq_pred->unit->first_select(); for (uint i= 0; i < sjm_sel->item_list.elements; i++) { - if (sjm_sel->ref_pointer_array[i] == keyuse->val) - return true; + DBUG_ASSERT(sjm_sel->ref_pointer_array[i]->real_item()->type() == Item::FIELD_ITEM); + if (keyuse->val->real_item()->type() == Item::FIELD_ITEM) + { + Field *field = ((Item_field*)sjm_sel->ref_pointer_array[i]->real_item())->field; + if (field->eq(((Item_field*)keyuse->val->real_item())->field)) + return true; + } } return false; } @@ -9702,7 +9807,6 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, if (first_keyuse) { key_parts++; - first_keyuse= FALSE; } else { @@ -9712,7 +9816,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, if (curr->keypart == keyuse->keypart && !(~used_tables & curr->used_tables) && join_tab->keyuse_is_valid_for_access_in_chosen_plan(join, - keyuse) && + curr) && are_tables_local(join_tab, curr->used_tables)) break; } @@ -9720,6 +9824,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, key_parts++; } } + first_keyuse= FALSE; keyuse++; } while (keyuse->table == table && keyuse->is_for_hash_join()); if (!key_parts) @@ -11677,7 +11782,15 @@ uint check_join_cache_usage(JOIN_TAB *tab, effort now. */ if (tab->table->pos_in_table_list->is_materialized_derived()) + { no_bka_cache= true; + /* + Don't use hash join algorithm if the temporary table for the rows + of the derived table will be created with an equi-join key. + */ + if (tab->table->s->keys) + no_hashed_cache= true; + } /* Don't use join buffering if we're dictated not to by no_jbuf_after @@ -22111,11 +22224,30 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, tmp_map.clear_all(); // Force the creation of quick select tmp_map.set_bit(best_key); // only best_key. select->quick= 0; + + bool cond_saved= false; + Item *saved_cond; + + /* + Index Condition Pushdown may have removed parts of the condition for + this table. Temporarily put them back because we want the whole + condition for the range analysis. + */ + if (select->pre_idx_push_select_cond) + { + saved_cond= select->cond; + select->cond= select->pre_idx_push_select_cond; + cond_saved= true; + } + select->test_quick_select(join->thd, tmp_map, 0, join->select_options & OPTION_FOUND_ROWS ? HA_POS_ERROR : join->unit->select_limit_cnt, TRUE, FALSE, FALSE); + + if (cond_saved) + select->cond= saved_cond; } order_direction= best_key_direction; /* @@ -23119,7 +23251,8 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, return 1; } } - if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + context_analysis_place == IN_GROUP_BY) { /* Don't allow one to use fields that is not used in GROUP BY @@ -26087,7 +26220,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, LEX_CSTRING t_alias= alias; str->append(' '); - if (lower_case_table_names== 1) + if (lower_case_table_names == 1) { if (alias.str && alias.str[0]) { @@ -27292,9 +27425,10 @@ AGGR_OP::end_send() // Update ref array join_tab->join->set_items_ref_array(*join_tab->ref_array); + bool keep_last_filesort_result = join_tab->filesort ? false : true; if (join_tab->window_funcs_step) { - if (join_tab->window_funcs_step->exec(join)) + if (join_tab->window_funcs_step->exec(join, keep_last_filesort_result)) return NESTED_LOOP_ERROR; } @@ -27348,6 +27482,12 @@ AGGR_OP::end_send() } } + if (keep_last_filesort_result) + { + delete join_tab->filesort_result; + join_tab->filesort_result= NULL; + } + // Finish rnd scn after sending records if (join_tab->table->file->inited) join_tab->table->file->ha_rnd_end(); diff --git a/sql/sql_select.h b/sql/sql_select.h index de3dff46189..1d08b746279 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -263,8 +263,12 @@ typedef struct st_join_table { /* Pointer to the associated ON expression. on_expr_ref=!NULL except for degenerate joins. - *on_expr_ref!=NULL for tables that are first inner tables within an outer - join. + + Optimization phase: *on_expr_ref!=NULL for tables that are the single + tables on the inner side of the outer join (t1 LEFT JOIN t2 ON...) + + Execution phase: *on_expr_ref!=NULL for tables that are first inner tables + within an outer join (which may have multiple tables) */ Item **on_expr_ref; COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */ diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index baaa4571c9e..1ed0bb38e64 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -542,7 +542,8 @@ void sequence_definition::adjust_values(longlong next_value) if ((real_increment= global_system_variables.auto_increment_increment) != 1) - offset= global_system_variables.auto_increment_offset; + offset= (global_system_variables.auto_increment_offset % + global_system_variables.auto_increment_increment); /* Ensure that next_free_value has the right offset, so that we @@ -564,7 +565,7 @@ void sequence_definition::adjust_values(longlong next_value) else { next_free_value+= to_add; - DBUG_ASSERT(next_free_value % real_increment == offset); + DBUG_ASSERT(llabs(next_free_value % real_increment) == offset); } } } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e8f42003691..b98f8aabdc1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2616,7 +2616,7 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) We can't just use table->query, because our SQL_MODE may trigger a different syntax, like when ANSI_QUOTES is defined. */ - table->view->unit.print(buff, enum_query_type(QT_ORDINARY | + table->view->unit.print(buff, enum_query_type(QT_VIEW_INTERNAL | QT_ITEM_ORIGINAL_FUNC_NULLIF)); if (table->with_check != VIEW_CHECK_NONE) @@ -5009,9 +5009,7 @@ public: const char* msg, Sql_condition ** cond_hdl) { - if (sql_errno == ER_PARSE_ERROR || - sql_errno == ER_TRG_NO_DEFINER || - sql_errno == ER_TRG_NO_CREATION_CTX) + if (sql_errno == ER_TRG_NO_DEFINER || sql_errno == ER_TRG_NO_CREATION_CTX) return true; if (*level != Sql_condition::WARN_LEVEL_ERROR) @@ -6795,6 +6793,42 @@ store_constraints(THD *thd, TABLE *table, const LEX_CSTRING *db_name, return schema_table_store_record(thd, table); } +static int get_check_constraints_record(THD *thd, TABLE_LIST *tables, + TABLE *table, bool res, + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) +{ + DBUG_ENTER("get_check_constraints_record"); + if (res) + { + if (thd->is_error()) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); + thd->clear_error(); + DBUG_RETURN(0); + } + else if (!tables->view) + { + if (tables->table->s->table_check_constraints) + { + for (uint i= 0; i < tables->table->s->table_check_constraints; i++) + { + StringBuffer<MAX_FIELD_WIDTH> str(system_charset_info); + Virtual_column_info *check= tables->table->check_constraints[i]; + restore_record(table, s->default_values); + table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info); + table->field[1]->store(db_name->str, db_name->length, system_charset_info); + table->field[2]->store(check->name.str, check->name.length, system_charset_info); + table->field[3]->store(table_name->str, table_name->length, system_charset_info); + check->print(&str); + table->field[4]->store(str.ptr(), str.length(), system_charset_info); + schema_table_store_record(thd, table); + } + } + } + DBUG_RETURN(res); +} static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, @@ -9715,6 +9749,18 @@ ST_FIELD_INFO spatial_ref_sys_fields_info[]= #endif /*HAVE_SPATIAL*/ +ST_FIELD_INFO check_constraints_fields_info[]= +{ + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, + {"CHECK_CLAUSE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + OPEN_FULL_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} +}; /* Description of ST_FIELD_INFO in table.h @@ -9730,6 +9776,8 @@ ST_SCHEMA_TABLE schema_tables[]= fill_schema_applicable_roles, 0, 0, -1, -1, 0, 0}, {"CHARACTER_SETS", charsets_fields_info, 0, fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0}, + {"CHECK_CONSTRAINTS", check_constraints_fields_info, 0, + get_all_tables, 0, get_check_constraints_record, 1, 2, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, {"COLLATIONS", collation_fields_info, 0, fill_schema_collation, make_old_format, 0, -1, -1, 0, 0}, {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info, diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 00ed7fb0202..04806f07b3b 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -3060,6 +3060,39 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) /** + @breif + Cleanup of min/max statistical values for table share +*/ + +void delete_stat_values_for_table_share(TABLE_SHARE *table_share) +{ + TABLE_STATISTICS_CB *stats_cb= &table_share->stats_cb; + Table_statistics *table_stats= stats_cb->table_stats; + if (!table_stats) + return; + Column_statistics *column_stats= table_stats->column_stats; + if (!column_stats) + return; + + for (Field **field_ptr= table_share->field; + *field_ptr; + field_ptr++, column_stats++) + { + if (column_stats->min_value) + { + delete column_stats->min_value; + column_stats->min_value= NULL; + } + if (column_stats->max_value) + { + delete column_stats->max_value; + column_stats->max_value= NULL; + } + } +} + + +/** @brief Check whether any statistics is to be read for tables from a table list @@ -3239,6 +3272,9 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables) if (!tl->is_view_or_derived() && !is_temporary_table(tl) && tl->table) { TABLE_SHARE *table_share= tl->table->s; + if (table_share && !(table_share->table_category == TABLE_CATEGORY_USER)) + continue; + if (table_share && table_share->stats_cb.stats_can_be_read && !table_share->stats_cb.stats_is_read) @@ -3289,7 +3325,7 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables) The function is called when executing the statement DROP TABLE 'tab'. */ -int delete_statistics_for_table(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab) +int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab) { int err; enum_binlog_format save_binlog_format; diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 7ec742b70db..39cddf95188 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -21,7 +21,7 @@ enum enum_use_stat_tables_mode { NEVER, COMPLEMENTARY, - PEFERABLY, + PREFERABLY, } Use_stat_tables_mode; typedef @@ -92,9 +92,10 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables); int collect_statistics_for_table(THD *thd, TABLE *table); int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *share, bool is_safe); +void delete_stat_values_for_table_share(TABLE_SHARE *table_share); int alloc_statistics_for_table(THD *thd, TABLE *table); int update_statistics_for_table(THD *thd, TABLE *table); -int delete_statistics_for_table(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab); +int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab); int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col); int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info, bool ext_prefixes_only); @@ -332,12 +333,17 @@ private: public: Histogram histogram; + + uint32 no_values_provided_bitmap() + { + return + ((1 << (COLUMN_STAT_HISTOGRAM-COLUMN_STAT_COLUMN_NAME))-1) << + (COLUMN_STAT_COLUMN_NAME+1); + } void set_all_nulls() { - column_stat_nulls= - ((1 << (COLUMN_STAT_HISTOGRAM-COLUMN_STAT_COLUMN_NAME))-1) << - (COLUMN_STAT_COLUMN_NAME+1); + column_stat_nulls= no_values_provided_bitmap(); } void set_not_null(uint stat_field_no) @@ -383,8 +389,22 @@ public: bool min_max_values_are_provided() { return !is_null(COLUMN_STAT_MIN_VALUE) && - !is_null(COLUMN_STAT_MIN_VALUE); - } + !is_null(COLUMN_STAT_MAX_VALUE); + } + /* + This function checks whether the values for the fields of the statistical + tables that were NULL by DEFAULT for a column have changed or not. + + @retval + TRUE: Statistics are not present for a column + FALSE: Statisitics are present for a column + */ + bool no_stat_values_provided() + { + if (column_stat_nulls == no_values_provided_bitmap()) + return true; + return false; + } }; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index e7acabe8bee..39d9438d5bf 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -237,9 +237,9 @@ bool String::copy(const String &str) bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs) { + DBUG_ASSERT(arg_length < UINT_MAX32); if (alloc(arg_length)) return TRUE; - DBUG_ASSERT(arg_length < UINT_MAX32); if (Ptr == str && arg_length == uint32(str_length)) { /* @@ -256,6 +256,24 @@ bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs) return FALSE; } +/* + Copy string, where strings may overlap. + Same as String::copy, but use memmove instead of memcpy to avoid warnings + from valgrind +*/ + +bool String::copy_or_move(const char *str,size_t arg_length, CHARSET_INFO *cs) +{ + DBUG_ASSERT(arg_length < UINT_MAX32); + if (alloc(arg_length)) + return TRUE; + if ((str_length=uint32(arg_length))) + memmove(Ptr,str,arg_length); + Ptr[arg_length]=0; + str_charset=cs; + return FALSE; +} + /* Checks that the source string can be just copied to the destination string @@ -389,8 +407,9 @@ bool String::set_or_copy_aligned(const char *str, size_t arg_length, /* How many bytes are in incomplete character */ size_t offset= (arg_length % cs->mbminlen); - if (!offset) /* All characters are complete, just copy */ + if (!offset) { + /* All characters are complete, just use given string */ set(str, arg_length, cs); return FALSE; } diff --git a/sql/sql_string.h b/sql/sql_string.h index d9d3f10777c..0ae68cb3796 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -437,6 +437,7 @@ public: bool copy(); // Alloc string if not alloced bool copy(const String &s); // Allocate new string bool copy(const char *s,size_t arg_length, CHARSET_INFO *cs); // Allocate new string + bool copy_or_move(const char *s,size_t arg_length, CHARSET_INFO *cs); static bool needs_conversion(size_t arg_length, CHARSET_INFO *cs_from, CHARSET_INFO *cs_to, uint32 *offset); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2b7333b4c24..3e204c4945b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -29,7 +29,7 @@ #include "lock.h" // mysql_unlock_tables #include "strfunc.h" // find_type2, find_set #include "sql_truncate.h" // regenerate_locked_table -#include "sql_partition.h" // generate_partition_syntax, +#include "sql_partition.h" // mem_alloc_error, // partition_info // NOT_A_PARTITION_ID #include "sql_db.h" // load_db_opt_by_name @@ -1838,13 +1838,10 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) partition_info *part_info= lpt->table->part_info; if (part_info) { - if (!(part_syntax_buf= generate_partition_syntax(lpt->thd, part_info, - &syntax_len, TRUE, - lpt->create_info, - lpt->alter_info))) - { + part_syntax_buf= generate_partition_syntax_for_frm(lpt->thd, part_info, + &syntax_len, lpt->create_info, lpt->alter_info); + if (!part_syntax_buf) DBUG_RETURN(TRUE); - } part_info->part_info_string= part_syntax_buf; part_info->part_info_len= syntax_len; } @@ -1921,10 +1918,9 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) { TABLE_SHARE *share= lpt->table->s; char *tmp_part_syntax_str; - if (!(part_syntax_buf= generate_partition_syntax(lpt->thd, part_info, - &syntax_len, TRUE, - lpt->create_info, - lpt->alter_info))) + part_syntax_buf= generate_partition_syntax_for_frm(lpt->thd, + part_info, &syntax_len, lpt->create_info, lpt->alter_info); + if (!part_syntax_buf) { error= 1; goto err; @@ -2113,7 +2109,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, in its elements. */ table->table= find_table_for_mdl_upgrade(thd, table->db.str, - table->table_name.str, false); + table->table_name.str, NULL); if (!table->table) DBUG_RETURN(true); table->mdl_request.ticket= table->table->mdl_ticket; @@ -2939,20 +2935,6 @@ uint Column_definition::pack_flag_numeric(uint dec) const bool Column_definition::prepare_stage2_varchar(ulonglong table_flags) { -#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR - if (table_flags & HA_NO_VARCHAR) - { - /* convert VARCHAR to CHAR because handler is not yet up to date */ - set_handler(&type_handler_var_string); - pack_length= type_handler()->calc_pack_length((uint) length); - if ((length / charset->mbmaxlen) > MAX_FIELD_CHARLENGTH) - { - my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name.str, - static_cast<ulong>(MAX_FIELD_CHARLENGTH)); - return true; - } - } -#endif pack_flag= (charset->state & MY_CS_BINSORT) ? FIELDFLAG_BINARY : 0; return false; } @@ -3456,6 +3438,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, !(sql_field->charset= find_bin_collation(sql_field->charset))) DBUG_RETURN(true); + /* Virtual fields are always NULL */ + if (sql_field->vcol_info) + sql_field->flags&= ~NOT_NULL_FLAG; + if (sql_field->prepare_stage1(thd, thd->mem_root, file, file->ha_table_flags())) DBUG_RETURN(true); @@ -4148,7 +4134,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } if (!unique_key && !primary_key && - (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY)) + ((file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY) || + ((file->ha_table_flags() & HA_WANTS_PRIMARY_KEY) && + !create_info->sequence))) { my_message(ER_REQUIRES_PRIMARY_KEY, ER_THD(thd, ER_REQUIRES_PRIMARY_KEY), MYF(0)); @@ -4260,11 +4248,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } /* Give warnings for not supported table options */ -#if defined(WITH_ARIA_STORAGE_ENGINE) extern handlerton *maria_hton; - if (file->partition_ht() != maria_hton) -#endif - if (create_info->transactional) + if (file->partition_ht() != maria_hton && create_info->transactional && + !file->has_transaction_manager()) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER_THD(thd, ER_ILLEGAL_HA_CREATE_OPTION), @@ -4615,11 +4601,8 @@ handler *mysql_create_frm_image(THD *thd, We reverse the partitioning parser and generate a standard format for syntax stored in frm file. */ - sql_mode_t old_mode= thd->variables.sql_mode; - thd->variables.sql_mode &= ~MODE_ANSI_QUOTES; - part_syntax_buf= generate_partition_syntax(thd, part_info, &syntax_len, - true, create_info, alter_info); - thd->variables.sql_mode= old_mode; + part_syntax_buf= generate_partition_syntax_for_frm(thd, part_info, + &syntax_len, create_info, alter_info); if (!part_syntax_buf) goto err; part_info->part_info_string= part_syntax_buf; @@ -4862,6 +4845,8 @@ int create_table_impl(THD *thd, { if (options.or_replace()) { + (void) delete_statistics_for_table(thd, db, table_name); + TABLE_LIST table_list; table_list.init_one_table(db, table_name, 0, TL_WRITE_ALLOW_WRITE); table_list.table= create_info->table; @@ -5485,13 +5470,13 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db, } delete file; - if (unlikely(error)) - { - if (error == HA_ERR_WRONG_COMMAND) - my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE"); - else - my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error); - } + if (error == HA_ERR_WRONG_COMMAND) + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE"); + else if (error == ENOTDIR) + my_error(ER_BAD_DB_ERROR, MYF(0), new_db->str); + else if (error) + my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error); + else if (!(flags & FN_IS_TMP)) mysql_audit_rename_table(thd, old_db, old_name, new_db, new_name); @@ -9563,8 +9548,6 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, } DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); - /* We can abort alter table for any table type */ - thd->abort_on_warning= !ignore && thd->is_strict_mode(); /* Create .FRM for new version of table with a temporary name. @@ -9594,7 +9577,6 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, C_ALTER_TABLE_FRM_ONLY, NULL, &key_info, &key_count, &frm); reenable_binlog(thd); - thd->abort_on_warning= false; if (unlikely(error)) { my_free(const_cast<uchar*>(frm.str)); @@ -9797,8 +9779,10 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, Table can be found in the list of open tables in THD::all_temp_tables list. */ - tbl.table= thd->find_temporary_table(&tbl); + if ((tbl.table= thd->find_temporary_table(&tbl)) == NULL) + goto err_new_table_cleanup; new_table= tbl.table; + DBUG_ASSERT(new_table); } else { @@ -9812,10 +9796,60 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, alter_ctx.new_db.str, alter_ctx.tmp_name.str, true, true); + if (!new_table) + goto err_new_table_cleanup; + + /* + Normally, an attempt to modify an FK parent table will cause + FK children to be prelocked, so the table-being-altered cannot + be modified by a cascade FK action, because ALTER holds a lock + and prelocking will wait. + + But if a new FK is being added by this very ALTER, then the target + table is not locked yet (it's a temporary table). So, we have to + lock FK parents explicitly. + */ + if (alter_info->flags & ALTER_ADD_FOREIGN_KEY) + { + List <FOREIGN_KEY_INFO> fk_list; + List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); + FOREIGN_KEY_INFO *fk; + + /* tables_opened can be > 1 only for MERGE tables */ + DBUG_ASSERT(tables_opened == 1); + DBUG_ASSERT(&table_list->next_global == thd->lex->query_tables_last); + + new_table->file->get_foreign_key_list(thd, &fk_list); + while ((fk= fk_list_it++)) + { + if (lower_case_table_names) + { + char buf[NAME_LEN]; + size_t len; + strmake_buf(buf, fk->referenced_db->str); + len = my_casedn_str(files_charset_info, buf); + thd->make_lex_string(fk->referenced_db, buf, len); + strmake_buf(buf, fk->referenced_table->str); + len = my_casedn_str(files_charset_info, buf); + thd->make_lex_string(fk->referenced_table, buf, len); + } + if (table_already_fk_prelocked(table_list, fk->referenced_db, + fk->referenced_table, TL_READ_NO_INSERT)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + tl->init_one_table_for_prelocking(fk->referenced_db, fk->referenced_table, + NULL, TL_READ_NO_INSERT, TABLE_LIST::PRELOCK_FK, + NULL, 0, &thd->lex->query_tables_last); + } + + if (open_tables(thd, &table_list->next_global, &tables_opened, 0, + &alter_prelocking_strategy)) + goto err_new_table_cleanup; + } } - if (!new_table) - goto err_new_table_cleanup; new_table->s->orig_table_name= table->s->table_name.str; + /* Note: In case of MERGE table, we do not attach children. We do not copy data for MERGE tables. Only the children have data. diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 430eb192052..9562394f11e 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -175,7 +175,7 @@ int calc_weekday(long daynr,bool sunday_first_day_of_week) next week is week 1. */ -uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) +uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year) { uint days; ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day); @@ -264,7 +264,7 @@ bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month, ulong convert_period_to_month(ulong period) { ulong a,b; - if (period == 0) + if (period == 0 || period > 999912) return 0L; if ((a=period/100) < YY_PART_YEAR) a+=2000; @@ -289,14 +289,14 @@ ulong convert_month_to_period(ulong month) bool -check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, +check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, date_mode_t fuzzydate, timestamp_type ts_type) { int unused; - if (check_date(ltime, fuzzy_date, &unused)) + if (check_date(ltime, fuzzydate, &unused)) { ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, &str, ts_type, 0); return true; } @@ -305,7 +305,7 @@ check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, bool -adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) +adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec) { MYSQL_TIME copy= *ltime; ErrConvTime str(©); @@ -313,7 +313,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) if (check_time_range(ltime, dec, &warnings)) return true; if (warnings) - current_thd->push_warning_truncated_wrong_value("time", str.ptr()); + thd->push_warning_truncated_wrong_value("time", str.ptr()); return false; } @@ -351,33 +351,63 @@ to_ascii(CHARSET_INFO *cs, } -/* Character set-aware version of str_to_time() */ -bool -str_to_time(CHARSET_INFO *cs, const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) +class TemporalAsciiBuffer: public LEX_CSTRING { char cnv[32]; - if ((cs->state & MY_CS_NONASCII) != 0) +public: + TemporalAsciiBuffer(const char *str, size_t length, CHARSET_INFO *cs) { - length= to_ascii(cs, str, length, cnv, sizeof(cnv)); - str= cnv; + if ((cs->state & MY_CS_NONASCII) != 0) + { + LEX_CSTRING::str= cnv; + LEX_CSTRING::length= to_ascii(cs, str, length, cnv, sizeof(cnv)); + } + else + { + LEX_CSTRING::str= str; + LEX_CSTRING::length= length; + } } - return str_to_time(str, length, l_time, fuzzydate, status); +}; + + +/* Character set-aware version of str_to_time() */ +bool Temporal::str_to_time(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t fuzzydate) +{ + TemporalAsciiBuffer tmp(str, length, cs); + bool rc= ::str_to_time(tmp.str, tmp.length, this, + ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE), + status); + DBUG_ASSERT(status->warnings || !rc); + return rc; } /* Character set-aware version of str_to_datetime() */ -bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong flags, - MYSQL_TIME_STATUS *status) +bool Temporal::str_to_datetime(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t flags) { - char cnv[32]; - if ((cs->state & MY_CS_NONASCII) != 0) - { - length= to_ascii(cs, str, length, cnv, sizeof(cnv)); - str= cnv; - } - return str_to_datetime(str, length, l_time, flags, status); + TemporalAsciiBuffer tmp(str, length, cs); + bool rc= ::str_to_datetime(tmp.str, tmp.length, this, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + status); + DBUG_ASSERT(status->warnings || !rc); + return rc; +} + + +/* Character set-aware version of str_to_DDhhmmssff() */ +bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status, + const char *str, size_t length, + CHARSET_INFO *cs, ulong max_hour) +{ + TemporalAsciiBuffer tmp(str, length, cs); + bool rc= ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status); + DBUG_ASSERT(status->warnings || !rc); + return rc; } @@ -390,51 +420,46 @@ bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, */ bool -str_to_datetime_with_warn(CHARSET_INFO *cs, - const char *str, size_t length, MYSQL_TIME *l_time, - ulonglong flags) +str_to_datetime_with_warn(THD *thd, CHARSET_INFO *cs, + const char *str, size_t length, MYSQL_TIME *to, + date_mode_t mode) { - MYSQL_TIME_STATUS status; - THD *thd= current_thd; - bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status); - if (ret_val || status.warnings) - { - const ErrConvString err(str, length, &my_charset_bin); - make_truncated_value_warning(thd, - ret_val ? Sql_condition::WARN_LEVEL_WARN : - Sql_condition::time_warn_level(status.warnings), - &err, flags & TIME_TIME_ONLY ? - MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS); - } - DBUG_EXECUTE_IF("str_to_datetime_warn", - push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_YES, str);); - return ret_val; + Temporal::Warn_push warn(thd, NullS, to, mode); + Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, str, length, cs, mode); + return !t->is_valid_temporal(); } -bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, - ulonglong fuzzydate, const char *field_name) +bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *field_name) { - const ErrConvDouble str(value); - return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name); + Temporal::Warn_push warn(thd, field_name, ltime, fuzzydate); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate); + return !t->is_valid_temporal(); } -bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, - ulonglong fuzzydate, const char *field_name) +bool decimal_to_datetime_with_warn(THD *thd, const my_decimal *value, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *field_name) { - const ErrConvDecimal str(value); - return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name); + Temporal::Warn_push warn(thd, field_name, ltime, fuzzydate); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate); + return !t->is_valid_temporal(); } -bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime, - ulonglong fuzzydate, const char *field_name) +bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *field_name) { - const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg); - Sec6 sec(neg, value, 0); - return sec.convert_to_mysql_time(ltime, fuzzydate, &str, field_name); + /* + Note: conversion from an integer to TIME can overflow to '838:59:59.999999', + so the conversion result can have fractional digits. + */ + Temporal::Warn_push warn(thd, field_name, ltime, fuzzydate); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, nr, fuzzydate); + return !t->is_valid_temporal(); } @@ -864,20 +889,7 @@ void make_truncated_value_warning(THD *thd, timestamp_type time_type, const char *field_name) { - const char *type_str; - - switch (time_type) { - case MYSQL_TIMESTAMP_DATE: - type_str= "date"; - break; - case MYSQL_TIMESTAMP_TIME: - type_str= "time"; - break; - case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH - default: - type_str= "datetime"; - break; - } + const char *type_str= Temporal::type_name_by_timestamp_type(time_type); return thd->push_warning_wrong_or_truncated_value(level, time_type <= MYSQL_TIMESTAMP_ERROR, type_str, sval->ptr(), field_name); @@ -891,7 +903,7 @@ void make_truncated_value_warning(THD *thd, (X)->second_part) #define GET_PART(X, N) X % N ## LL; X/= N ## LL -bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, +bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, const INTERVAL &interval) { long period, sign; @@ -1006,7 +1018,6 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, invalid_date: { - THD *thd= current_thd; push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW), @@ -1073,10 +1084,10 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, } microseconds= ((longlong)days * SECONDS_IN_24H + - (longlong)(l_time1->hour*3600L + + (longlong)(l_time1->hour*3600LL + l_time1->minute*60L + l_time1->second) - - l_sign*(longlong)(l_time2->hour*3600L + + l_sign*(longlong)(l_time2->hour*3600LL + l_time2->minute*60L + l_time2->second)) * 1000000LL + (longlong)l_time1->second_part - @@ -1095,7 +1106,7 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int l_sign, MYSQL_TIME *l_time3, ulonglong fuzzydate) + int l_sign, MYSQL_TIME *l_time3, date_mode_t fuzzydate) { ulonglong seconds; ulong microseconds; @@ -1174,56 +1185,6 @@ bool time_to_datetime(MYSQL_TIME *ltime) } -/** - Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME. - If ltime is TIME, it's first converted to DATETIME. - If ts_type is DATE, hhmmss is set to zero. - The date part of the result is checked against fuzzy_date. - - @param ltime The value to convert. - @param fuzzy_date Flags to check date. - @param ts_type The type to convert to. - @return false on success, true of error (negative time).*/ -bool -make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date, - timestamp_type ts_type) -{ - DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE || - ts_type == MYSQL_TIMESTAMP_DATETIME); - if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime)) - { - /* e.g. negative time */ - ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, ts_type, 0); - return true; - } - if ((ltime->time_type= ts_type) == MYSQL_TIMESTAMP_DATE) - ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; - return check_date_with_warn(ltime, fuzzy_date, ts_type); -} - - -/* - Convert a TIME value to DAY-TIME interval, e.g. for extraction: - EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. - Moves full days from ltime->hour to ltime->day. - Note, time_type is set to MYSQL_TIMESTAMP_NONE, to make sure that - the structure is not used for anything else other than extraction: - non-extraction TIME functions expect zero day value! -*/ -void time_to_daytime_interval(MYSQL_TIME *ltime) -{ - DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME); - DBUG_ASSERT(ltime->year == 0); - DBUG_ASSERT(ltime->month == 0); - DBUG_ASSERT(ltime->day == 0); - ltime->day= ltime->hour / 24; - ltime->hour%= 24; - ltime->time_type= MYSQL_TIMESTAMP_NONE; -} - - /*** Conversion from TIME to DATETIME ***/ /* @@ -1342,7 +1303,7 @@ time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) bool time_to_datetime_with_warn(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to, - ulonglong fuzzydate) + date_mode_t fuzzydate) { int warn= 0; DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); @@ -1406,10 +1367,3 @@ void unpack_time(longlong packed, MYSQL_TIME *my_time, break; } } - - -bool my_decimal::to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const char *field_name) -{ - return decimal_to_datetime_with_warn(this, to, fuzzydate, field_name); -} diff --git a/sql/sql_time.h b/sql/sql_time.h index 8c6e58856e6..433374e2e9a 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -17,6 +17,7 @@ #ifndef SQL_TIME_INCLUDED #define SQL_TIME_INCLUDED +#include "sql_basic_types.h" #include "my_time.h" #include "mysql_time.h" /* timestamp_type */ #include "sql_error.h" /* Sql_condition */ @@ -35,25 +36,28 @@ ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); void set_current_date(THD *thd, MYSQL_TIME *to); bool time_to_datetime(MYSQL_TIME *ltime); -void time_to_daytime_interval(MYSQL_TIME *l_time); bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code); -bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time, - ulonglong flags); -bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool str_to_datetime_with_warn(THD *thd, + CHARSET_INFO *cs, const char *str, size_t length, + MYSQL_TIME *l_time, + date_mode_t flags); +bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *name); -bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool decimal_to_datetime_with_warn(THD *thd, + const my_decimal *value, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *name); -bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const char *name); bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt); bool time_to_datetime_with_warn(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt, - ulonglong fuzzydate); + date_mode_t fuzzydate); inline void datetime_to_date(MYSQL_TIME *ltime) { @@ -86,7 +90,7 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec); /* MYSQL_TIME operations */ -bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, +bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, const INTERVAL &interval); bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, int l_sign, ulonglong *seconds_out, ulong *microseconds_out); @@ -115,26 +119,17 @@ int append_interval(String *str, interval_type int_type, @return false - otherwise */ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate); + int lsign, MYSQL_TIME *l_time3, date_mode_t fuzzydate); int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds); -uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year); +uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year); int calc_weekday(long daynr,bool sunday_first_day_of_week); bool parse_date_time_format(timestamp_type format_type, const char *format, uint format_length, DATE_TIME_FORMAT *date_time_format); -/* Character set-aware version of str_to_time() */ -bool str_to_time(CHARSET_INFO *cs, const char *str,size_t length, - MYSQL_TIME *l_time, ulonglong fuzzydate, - MYSQL_TIME_STATUS *status); -/* Character set-aware version of str_to_datetime() */ -bool str_to_datetime(CHARSET_INFO *cs, - const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong flags, - MYSQL_TIME_STATUS *status); /* convenience wrapper */ inline bool parse_date_time_format(timestamp_type format_type, @@ -171,15 +166,14 @@ non_zero_date(const MYSQL_TIME *ltime) non_zero_hhmmssuu(ltime)); } static inline bool -check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut) +check_date(const MYSQL_TIME *ltime, date_mode_t flags, int *was_cut) { - return check_date(ltime, non_zero_date(ltime), flags, was_cut); + return check_date(ltime, non_zero_date(ltime), + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), was_cut); } -bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, +bool check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, date_mode_t fuzzy_date, timestamp_type ts_type); -bool make_date_with_warn(MYSQL_TIME *ltime, - ulonglong fuzzy_date, timestamp_type ts_type); -bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec); +bool adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec); longlong pack_time(const MYSQL_TIME *my_time); void unpack_time(longlong packed, MYSQL_TIME *my_time, diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 9f26bff3321..b79c1a1adb1 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -35,16 +35,6 @@ #include "sp_cache.h" // sp_invalidate_cache #include <mysys_err.h> -LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, - const char* str, size_t length, - MEM_ROOT *mem_root) -{ - if (!(lex_str->str= strmake_root(mem_root, str, length))) - return 0; - lex_str->length= length; - return lex_str; -} - /*************************************************************************/ /** @@ -515,7 +505,11 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (err_status) goto end; } - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + +#ifdef WITH_WSREP + if (thd->wsrep_exec_mode == LOCAL_STATE) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); +#endif /* We should have only one table in table list. */ DBUG_ASSERT(tables->next_global == 0); @@ -543,7 +537,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* Under LOCK TABLES we must only accept write locked tables. */ if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db.str, tables->table_name.str, - FALSE))) + NULL))) goto end; } else @@ -619,9 +613,9 @@ end: DBUG_RETURN(result); #ifdef WITH_WSREP - error: +wsrep_error_label: DBUG_RETURN(true); -#endif /* WITH_WSREP */ +#endif } @@ -1499,8 +1493,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, if (likely((name= error_handler.get_trigger_name()))) { - if (unlikely(!(make_lex_string(&trigger->name, name->str, - name->length, &table->mem_root)))) + trigger->name= safe_lexcstrdup_root(&table->mem_root, *name); + if (unlikely(!trigger->name.str)) goto err_with_lex_cleanup; } trigger->definer= ((!trg_definer || !trg_definer->length) ? diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index d515aacd1d0..798e929170c 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -299,7 +299,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, if (thd->locked_tables_mode) { if (!(table= find_table_for_mdl_upgrade(thd, table_ref->db.str, - table_ref->table_name.str, FALSE))) + table_ref->table_name.str, NULL))) DBUG_RETURN(TRUE); *hton_can_recreate= ha_check_storage_engine_flag(table->file->ht, @@ -350,7 +350,8 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, { DEBUG_SYNC(thd, "upgrade_lock_for_truncate"); /* To remove the table from the cache we need an exclusive lock. */ - if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DROP)) + if (wait_while_table_is_used(thd, table, + *hton_can_recreate ? HA_EXTRA_PREPARE_FOR_DROP : HA_EXTRA_NOT_USED)) DBUG_RETURN(TRUE); m_ticket_downgrade= table->mdl_ticket; /* Close if table is going to be recreated. */ @@ -400,6 +401,8 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) /* In RBR, the statement is not binlogged if the table is temporary. */ binlog_stmt= !thd->is_current_stmt_binlog_format_row(); + thd->close_unused_temporary_table_instances(table_ref); + error= handler_truncate(thd, table_ref, TRUE); /* diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 43edd2d506a..0e4caae7a2f 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -469,6 +469,7 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd, for (uint i=1; i < arg_count; i++) { + char col_name[8]; List<Item> *tvc_value; if (!(tvc_value= new (thd->mem_root) List<Item>())) return true; @@ -479,13 +480,27 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd, for (uint j=0; j < row_list->cols(); j++) { + if (i == 1) + { + sprintf(col_name, "_col_%i", j+1); + row_list->element_index(j)->set_name(thd, col_name, strlen(col_name), + thd->charset()); + } if (tvc_value->push_back(row_list->element_index(j), thd->mem_root)) return true; } } - else if (tvc_value->push_back(args[i]->real_item())) - return true; + else + { + if (i == 1) + { + sprintf(col_name, "_col_%i", 1); + args[i]->set_name(thd, col_name, strlen(col_name), thd->charset()); + } + if (tvc_value->push_back(args[i]->real_item())) + return true; + } if (values->push_back(tvc_value, thd->mem_root)) return true; diff --git a/sql/sql_tvc.h b/sql/sql_tvc.h index 420311cccb2..128cc883dd8 100644 --- a/sql/sql_tvc.h +++ b/sql/sql_tvc.h @@ -50,6 +50,8 @@ public: have_query_plan(QEP_NOT_PRESENT_YET), explain(0), select_options(select_options_arg) { }; + + ha_rows get_records() { return lists_of_values.elements; } bool prepare(THD *thd_arg, SELECT_LEX *sl, select_result *tmp_result, diff --git a/sql/sql_type.cc b/sql/sql_type.cc index e8dbf663df6..a432f37b580 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -154,12 +154,9 @@ VDec_op::VDec_op(Item_func_hybrid_field_type *item) } -bool Dec_ptr::to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - Item *item) +date_mode_t Temporal::sql_mode_for_dates(THD *thd) { - if (to_datetime_with_warn(to, fuzzydate, item->field_name_or_null())) - return item->null_value|= item->make_zero_date(to, fuzzydate); - return item->null_value= false; + return ::sql_mode_for_dates(thd); } @@ -176,9 +173,23 @@ my_decimal *Temporal::bad_to_decimal(my_decimal *to) const } -Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item) +void Temporal::make_from_str(THD *thd, Warn *warn, + const char *str, size_t length, + CHARSET_INFO *cs, date_mode_t fuzzydate) { - if (item->get_date(this, sql_mode_for_dates(thd))) + DBUG_EXECUTE_IF("str_to_datetime_warn", + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_YES, ErrConvString(str, length,cs).ptr());); + if (str_to_datetime(warn, str, length, cs, fuzzydate)) + make_fuzzy_date(&warn->warnings, fuzzydate); + if (warn->warnings) + warn->set_str(str, length, &my_charset_bin); +} + + +Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate) +{ + if (item->get_date(thd, this, fuzzydate)) time_type= MYSQL_TIMESTAMP_NONE; } @@ -211,80 +222,50 @@ void Sec6::make_truncated_warning(THD *thd, const char *type_str) const { char buff[1 + MAX_BIGINT_WIDTH + 1 + 6 + 1]; // '-' int '.' frac '\0' to_string(buff, sizeof(buff)); - current_thd->push_warning_truncated_wrong_value(type_str, buff); + thd->push_warning_truncated_wrong_value(type_str, buff); } -bool Sec6::to_time_with_warn(MYSQL_TIME *to, const ErrConv *str, - const char *field_name) const +bool Sec6::convert_to_mysql_time(THD *thd, int *warn, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - int was_cut; - bool res= to_time(to, &was_cut); - if (res || MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut)) - current_thd-> - push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN, - res, "time", str->ptr(), - field_name); - return res; -} - - -bool Sec6::to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const ErrConv *str, - const char *field_name) const -{ - bool res, have_warnings= false; - int was_cut; - res= to_datetime(to, fuzzydate, &was_cut); - have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE); - if (res || have_warnings) - current_thd-> - push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN, - res, "datetime", str->ptr(), - field_name); - return res; + bool is_time= bool(fuzzydate & TIME_TIME_ONLY); + bool rc= is_time ? to_time(ltime, warn) : to_datetime(ltime, fuzzydate, warn); + DBUG_ASSERT(*warn || !rc); + if (truncated()) + *warn|= MYSQL_TIME_WARN_TRUNCATED; + return rc; } -bool Sec6::convert_to_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate, - const ErrConv *str, const char *field_name) - const +void Temporal::push_conversion_warnings(THD *thd, bool totally_useless_value, int warn, + const char *typestr, + const char *field_name, + const char *value) { - bool is_time= fuzzydate & TIME_TIME_ONLY; - if (truncated()) - { - /* - The value was already truncated at the constructor call time, - and a truncation warning was issued. Here we convert silently - to avoid double warnings. - */ - current_thd-> - push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN, - !is_time, - is_time ? "time" : "datetime", - str->ptr(), field_name); - int warn; - return is_time ? to_time(ltime, &warn) : - to_datetime(ltime, fuzzydate, &warn); - } - return is_time ? to_time_with_warn(ltime, str, field_name) : - to_datetime_with_warn(ltime, fuzzydate, str, field_name); + if (MYSQL_TIME_WARN_HAVE_WARNINGS(warn)) + thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN, + totally_useless_value, + typestr, value, field_name); + else if (MYSQL_TIME_WARN_HAVE_NOTES(warn)) + thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_NOTE, + false, typestr, value, field_name); } -VSec6::VSec6(Item *item, const char *type_str, ulonglong limit) +VSec6::VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit) { if (item->decimals == 0) { // optimize for an important special case - longlong nr= item->val_int(); - make_from_int(nr, item->unsigned_flag); + Longlong_hybrid nr(item->val_int(), item->unsigned_flag); + make_from_int(nr); m_is_null= item->null_value; if (!m_is_null && m_sec > limit) { m_sec= limit; m_truncated= true; - ErrConvInteger err(nr, item->unsigned_flag); - current_thd->push_warning_truncated_wrong_value(type_str, err.ptr()); + ErrConvInteger err(nr); + thd->push_warning_truncated_wrong_value(type_str, err.ptr()); } } else if (item->cmp_type() == REAL_RESULT) @@ -300,7 +281,7 @@ VSec6::VSec6(Item *item, const char *type_str, ulonglong limit) if (m_truncated) { ErrConvDouble err(nr); - current_thd->push_warning_truncated_wrong_value(type_str, err.ptr()); + thd->push_warning_truncated_wrong_value(type_str, err.ptr()); } } else @@ -315,7 +296,7 @@ VSec6::VSec6(Item *item, const char *type_str, ulonglong limit) if (m_truncated) { ErrConvDecimal err(tmp.ptr()); - current_thd->push_warning_truncated_wrong_value(type_str, err.ptr()); + thd->push_warning_truncated_wrong_value(type_str, err.ptr()); } } } @@ -323,8 +304,8 @@ VSec6::VSec6(Item *item, const char *type_str, ulonglong limit) Year::Year(longlong value, bool unsigned_flag, uint length) { - if ((m_truncated= (value < 0 && !unsigned_flag))) - m_year= 0; + if ((m_truncated= (value < 0))) // Negative or huge unsigned + m_year= unsigned_flag ? 9999 : 0; else if (value > 9999) { m_truncated= true; @@ -349,24 +330,133 @@ uint Year::year_precision(const Item *item) const VYear::VYear(Item *item) - :Year_null(Year(item->val_int(), item->unsigned_flag, - year_precision(item)), item->null_value) + :Year_null(item->to_longlong_null(), item->unsigned_flag, year_precision(item)) { } VYear_op::VYear_op(Item_func_hybrid_field_type *item) - :Year_null(Year(item->int_op(), item->unsigned_flag, - year_precision(item)), item->null_value) + :Year_null(item->to_longlong_null_op(), item->unsigned_flag, + year_precision(item)) { } -void Time::make_from_item(int *warn, Item *item, const Options opt) +const LEX_CSTRING Interval_DDhhmmssff::m_type_name= + {STRING_WITH_LEN("INTERVAL DAY TO SECOND")}; + + +Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, Status *st, + bool push_warnings, + Item *item, ulong max_hour) +{ + switch (item->cmp_type()) { + case ROW_RESULT: + DBUG_ASSERT(0); + time_type= MYSQL_TIMESTAMP_NONE; + break; + case TIME_RESULT: + { + if (item->get_date(thd, this, TIME_TIME_ONLY)) + time_type= MYSQL_TIMESTAMP_NONE; + else if (time_type != MYSQL_TIMESTAMP_TIME) + { + st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + push_warning_wrong_or_truncated_value(thd, ErrConvTime(this), + st->warnings); + time_type= MYSQL_TIMESTAMP_NONE; + } + break; + } + case INT_RESULT: + case REAL_RESULT: + case DECIMAL_RESULT: + case STRING_RESULT: + { + StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp; + String *str= item->val_str(&tmp); + if (!str) + time_type= MYSQL_TIMESTAMP_NONE; + else if (str_to_DDhhmmssff(st, str->ptr(), str->length(), str->charset(), + UINT_MAX32)) + { + if (push_warnings) + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, + ErrConvString(str).ptr()); + time_type= MYSQL_TIMESTAMP_NONE; + } + else + { + if (hour > max_hour) + { + st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + time_type= MYSQL_TIMESTAMP_NONE; + } + // Warn if hour or nanosecond truncation happened + if (push_warnings) + push_warning_wrong_or_truncated_value(thd, ErrConvString(str), + st->warnings); + } + } + break; + } + DBUG_ASSERT(is_valid_value_slow()); +} + + +void +Interval_DDhhmmssff::push_warning_wrong_or_truncated_value(THD *thd, + const ErrConv &str, + int warnings) +{ + if (warnings & MYSQL_TIME_WARN_OUT_OF_RANGE) + { + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, str.ptr()); + } + else if (MYSQL_TIME_WARN_HAVE_WARNINGS(warnings)) + { + thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, str.ptr()); + } + else if (MYSQL_TIME_WARN_HAVE_NOTES(warnings)) + { + thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_NOTE, + m_type_name.str, str.ptr()); + } +} + + +uint Interval_DDhhmmssff::fsp(THD *thd, Item *item) +{ + switch (item->cmp_type()) { + case INT_RESULT: + case TIME_RESULT: + return item->decimals; + case REAL_RESULT: + case DECIMAL_RESULT: + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); + case ROW_RESULT: + DBUG_ASSERT(0); + return 0; + case STRING_RESULT: + break; + } + if (!item->const_item() || item->is_expensive()) + return TIME_SECOND_PART_DIGITS; + Status st; + Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32); + return it.is_valid_interval_DDhhmmssff() ? st.precision : + TIME_SECOND_PART_DIGITS; +} + + +void Time::make_from_item(THD *thd, int *warn, Item *item, const Options opt) { *warn= 0; - if (item->get_date(this, opt.get_date_flags())) + if (item->get_date(thd, this, opt.get_date_flags())) time_type= MYSQL_TIMESTAMP_NONE; else - valid_MYSQL_TIME_to_valid_value(warn, opt); + valid_MYSQL_TIME_to_valid_value(thd, warn, opt); } @@ -404,6 +494,12 @@ void Time::make_from_datetime_with_days_diff(int *warn, const MYSQL_TIME *from, from->second) * 1000000LL + from->second_part); unpack_time(timediff, this, MYSQL_TIMESTAMP_TIME); + if (year || month) + { + *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE; + year= month= day= 0; + hour= TIME_MAX_HOUR + 1; + } } // The above code can generate TIME values outside of the valid TIME range. adjust_time_range_or_invalidate(warn); @@ -469,7 +565,7 @@ Time::Time(int *warn, const MYSQL_TIME *from, long curdays) } -void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags) +void Temporal_with_date::make_from_item(THD *thd, Item *item, date_mode_t flags) { flags&= ~TIME_TIME_ONLY; /* @@ -479,10 +575,10 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags) In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY and leave it to get_date() to check date. */ - ulonglong time_flag= (item->field_type() == MYSQL_TYPE_TIME && - !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? - TIME_TIME_ONLY : 0; - if (item->get_date(this, flags | time_flag)) + date_mode_t time_flag= (item->field_type() == MYSQL_TYPE_TIME && + !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? + TIME_TIME_ONLY : date_mode_t(0); + if (item->get_date(thd, this, flags | time_flag)) time_type= MYSQL_TIMESTAMP_NONE; else if (time_type == MYSQL_TIMESTAMP_TIME) { @@ -501,15 +597,16 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item) } -void Temporal_with_date::check_date_or_invalidate(int *warn, sql_mode_t flags) +void Temporal_with_date::check_date_or_invalidate(int *warn, date_mode_t flags) { - if (check_date(this, pack_time(this) != 0, flags, warn)) + if (check_date(this, pack_time(this) != 0, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), warn)) time_type= MYSQL_TIMESTAMP_NONE; } void Datetime::make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, - sql_mode_t flags) + date_mode_t flags) { DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); if (time_to_datetime(thd, from, this)) @@ -523,7 +620,7 @@ void Datetime::make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, void Datetime::make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, - sql_mode_t flags) + date_mode_t flags) { DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATE || from->time_type == MYSQL_TIMESTAMP_DATETIME); @@ -540,9 +637,9 @@ void Datetime::make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, Datetime::Datetime(THD *thd, int *warn, const MYSQL_TIME *from, - sql_mode_t flags) + date_mode_t flags) { - DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0); + DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); switch (from->time_type) { case MYSQL_TIMESTAMP_ERROR: case MYSQL_TIMESTAMP_NONE: @@ -3024,14 +3121,14 @@ void Type_handler_row::Item_update_null_value(Item *item) const void Type_handler_time_common::Item_update_null_value(Item *item) const { MYSQL_TIME ltime; - (void) item->get_date(<ime, TIME_TIME_ONLY); + (void) item->get_date(current_thd, <ime, TIME_TIME_ONLY); } void Type_handler_temporal_with_date::Item_update_null_value(Item *item) const { MYSQL_TIME ltime; - (void) item->get_date(<ime, sql_mode_for_dates(current_thd)); + (void) item->get_date(current_thd, <ime, sql_mode_for_dates(current_thd)); } @@ -3503,6 +3600,106 @@ bool Type_handler:: } +bool Type_handler_temporal_result:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func, + items, nitems); + bool is_time= func->field_type() == MYSQL_TYPE_TIME; + func->decimals= 0; + for (uint i= 0; i < nitems; i++) + { + uint deci= is_time ? items[i]->time_precision(thd) : + items[i]->datetime_precision(thd); + set_if_bigger(func->decimals, deci); + } + + if (rc || func->maybe_null) + return rc; + /* + LEAST/GREATES(non-temporal, temporal) can return NULL. + CAST functions Item_{time|datetime|date}_typecast always set maybe_full + to true. Here we try to detect nullability more thoroughly. + Perhaps CAST functions should also reuse this idea eventually. + */ + const Type_handler *hf= func->type_handler(); + for (uint i= 0; i < nitems; i++) + { + /* + If items[i] does not need conversion to the current temporal data + type, then we trust items[i]->maybe_null, which was already ORred + to func->maybe_null in the argument loop in fix_fields(). + If items[i] requires conversion to the current temporal data type, + then conversion can fail and return NULL even for NOT NULL items. + */ + const Type_handler *ha= items[i]->type_handler(); + if (hf == ha) + continue; // No conversion. + if (ha->cmp_type() != TIME_RESULT) + { + func->maybe_null= true; // Conversion from non-temporal is not safe + break; + } + timestamp_type tf= hf->mysql_timestamp_type(); + timestamp_type ta= ha->mysql_timestamp_type(); + if (tf == ta || + (tf == MYSQL_TIMESTAMP_DATETIME && ta == MYSQL_TIMESTAMP_DATE)) + { + /* + If handlers have the same mysql_timestamp_type(), + then conversion is NULL safe. Conversion from DATE to DATETIME + is also safe. This branch includes data type pairs: + Function return type Argument type Comment + -------------------- ------------- ------------- + TIMESTAMP TIMESTAMP no conversion + TIMESTAMP DATETIME not possible + TIMESTAMP DATE not possible + DATETIME DATETIME no conversion + DATETIME TIMESTAMP safe conversion + DATETIME DATE safe conversion + DATE DATE no conversion + TIME TIME no conversion + + Note, a function cannot return TIMESTAMP if it has non-TIMESTAMP + arguments (it would return DATETIME in such case). + */ + DBUG_ASSERT(hf->field_type() != MYSQL_TYPE_TIMESTAMP || tf == ta); + continue; + } + /* + Here we have the following data type pairs that did not match + the condition above: + + Function return type Argument type Comment + -------------------- ------------- ------- + TIMESTAMP TIME Not possible + DATETIME TIME depends on OLD_MODE_ZERO_DATE_TIME_CAST + DATE TIMESTAMP Not possible + DATE DATETIME Not possible + DATE TIME Not possible + TIME TIMESTAMP Not possible + TIME DATETIME Not possible + TIME DATE Not possible + + Most pairs are not possible, because the function data type + would be DATETIME (according to LEAST/GREATEST aggregation rules). + Conversion to DATETIME from TIME is not safe when + OLD_MODE_ZERO_DATE_TIME_CAST is set: + - negative TIME values cannot be converted to not-NULL DATETIME values + - TIME values can produce DATETIME values that do not pass + NO_ZERO_DATE and NO_ZERO_IN_DATE tests. + */ + DBUG_ASSERT(hf->field_type() == MYSQL_TYPE_DATETIME); + if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) + continue; + func->maybe_null= true; + break; + } + return rc; +} + + bool Type_handler_real_result:: Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, Item **items, uint nitems) const @@ -3801,43 +3998,87 @@ bool Type_handler_string_result::Item_val_bool(Item *item) const /*************************************************************************/ -bool Type_handler_int_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + +bool Type_handler::Item_get_date_with_warn(THD *thd, Item *item, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->get_date_from_int(ltime, fuzzydate); + Temporal::Warn_push warn(thd, item->field_name_or_null(), ltime, fuzzydate); + Item_get_date(thd, item, &warn, ltime, fuzzydate); + return ltime->time_type < 0; } -bool Type_handler_year::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +bool Type_handler::Item_func_hybrid_field_type_get_date_with_warn(THD *thd, + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + date_mode_t mode) const +{ + Temporal::Warn_push warn(thd, item->field_name_or_null(), ltime, mode); + Item_func_hybrid_field_type_get_date(thd, item, &warn, ltime, mode); + return ltime->time_type < 0; +} + + +/************************************************************************/ +void Type_handler_decimal_result::Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const +{ + new(ltime) Temporal_hybrid(thd, warn, VDec(item).ptr(), fuzzydate); +} + + +void Type_handler_int_result::Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, + MYSQL_TIME *to, + date_mode_t mode) const +{ + new(to) Temporal_hybrid(thd, warn, item->to_longlong_hybrid_null(), mode); +} + + +void Type_handler_year::Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->null_value= - VYear(item).to_mysql_time_with_warn(ltime, fuzzydate, - item->field_name_or_null()); + VYear year(item); + DBUG_ASSERT(!year.truncated()); + Longlong_hybrid_null nr(Longlong_null(year.to_YYYYMMDD(), year.is_null()), + item->unsigned_flag); + new(ltime) Temporal_hybrid(thd, warn, nr, fuzzydate); } -bool Type_handler_real_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +void Type_handler_real_result::Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { - return item->get_date_from_real(ltime, fuzzydate); + new(ltime) Temporal_hybrid(thd, warn, item->to_double_null(), fuzzydate); } -bool Type_handler_string_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +void Type_handler_string_result::Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, + MYSQL_TIME *ltime, + date_mode_t mode) const { - return item->get_date_from_string(ltime, fuzzydate); + StringBuffer<40> tmp; + new(ltime) Temporal_hybrid(thd, warn, item->val_str(&tmp), mode); } -bool Type_handler_temporal_result::Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const +void Type_handler_temporal_result::Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { DBUG_ASSERT(0); // Temporal type items must implement native get_date() item->null_value= true; - set_zero_time(ltime, mysql_timestamp_type()); - return true; + set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); } @@ -3978,24 +4219,31 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_decimal( } -bool +void Type_handler_decimal_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, + Temporal::Warn *warn, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return VDec_op(item).to_datetime_with_warn(ltime, fuzzydate, item); + new (ltime) Temporal_hybrid(thd, warn, VDec_op(item).ptr(), fuzzydate); } -bool +void Type_handler_year::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, + Temporal::Warn *warn, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->null_value= - VYear_op(item).to_mysql_time_with_warn(ltime, fuzzydate, NULL); + VYear_op year(item); + DBUG_ASSERT(!year.truncated()); + Longlong_hybrid_null nr(Longlong_null(year.to_YYYYMMDD(), year.is_null()), + item->unsigned_flag); + new(ltime) Temporal_hybrid(thd, warn, nr, fuzzydate); } @@ -4038,17 +4286,18 @@ Type_handler_int_result::Item_func_hybrid_field_type_val_decimal( } -bool +void Type_handler_int_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, - MYSQL_TIME *ltime, - ulonglong fuzzydate) const + Temporal::Warn *warn, + MYSQL_TIME *to, + date_mode_t mode) const { - return item->get_date_from_int_op(ltime, fuzzydate); + new(to) Temporal_hybrid(thd, warn, item->to_longlong_hybrid_null_op(), mode); } - /***************************************************************************/ String * @@ -4087,13 +4336,15 @@ Type_handler_real_result::Item_func_hybrid_field_type_val_decimal( } -bool +void Type_handler_real_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, - MYSQL_TIME *ltime, - ulonglong fuzzydate) const + Temporal::Warn *warn, + MYSQL_TIME *to, + date_mode_t mode) const { - return item->get_date_from_real_op(ltime, fuzzydate); + new(to) Temporal_hybrid(thd, warn, item->to_double_null_op(), mode); } @@ -4135,13 +4386,16 @@ Type_handler_temporal_result::Item_func_hybrid_field_type_val_decimal( } -bool +void Type_handler_temporal_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, + Temporal::Warn *warn, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->date_op(ltime, fuzzydate); + if (item->date_op(thd, ltime, fuzzydate)) + set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); } @@ -4183,13 +4437,16 @@ Type_handler_time_common::Item_func_hybrid_field_type_val_decimal( } -bool +void Type_handler_time_common::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, + Temporal::Warn *warn, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t fuzzydate) const { - return item->time_op(ltime); + if (item->time_op(thd, ltime)) + set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); } @@ -4231,13 +4488,18 @@ Type_handler_string_result::Item_func_hybrid_field_type_val_decimal( } -bool +void Type_handler_string_result::Item_func_hybrid_field_type_get_date( + THD *thd, Item_func_hybrid_field_type *item, + Temporal::Warn *warn, MYSQL_TIME *ltime, - ulonglong fuzzydate) const + date_mode_t mode) const { - return item->get_date_from_str_op(ltime, fuzzydate); + StringBuffer<40> tmp; + String *res= item->str_op(&tmp); + DBUG_ASSERT((res == NULL) == item->null_value); + new(ltime) Temporal_hybrid(thd, warn, res, mode); } /***************************************************************************/ @@ -4560,13 +4822,30 @@ double Type_handler_string_result:: } -double Type_handler_temporal_result:: +double Type_handler_time_common:: Item_func_min_max_val_real(Item_func_min_max *func) const { - MYSQL_TIME ltime; - if (func->get_date(<ime, 0)) - return 0; - return TIME_to_double(<ime); + return Time(current_thd, func).to_double(); +} + + +double Type_handler_date_common:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return Date(current_thd, func).to_double(); +} + + +double Type_handler_datetime_common:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_double(); +} + +double Type_handler_timestamp_common:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_double(); } @@ -4584,13 +4863,31 @@ longlong Type_handler_string_result:: } -longlong Type_handler_temporal_result:: +longlong Type_handler_time_common:: Item_func_min_max_val_int(Item_func_min_max *func) const { - MYSQL_TIME ltime; - if (func->get_date(<ime, 0)) - return 0; - return TIME_to_ulonglong(<ime); + return Time(current_thd, func).to_longlong(); +} + + +longlong Type_handler_date_common:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return Date(current_thd, func).to_longlong(); +} + + +longlong Type_handler_datetime_common:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_longlong(); +} + + +longlong Type_handler_timestamp_common:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return Datetime(current_thd, func).to_longlong(); } @@ -4617,20 +4914,41 @@ my_decimal *Type_handler_numeric:: } -my_decimal *Type_handler_temporal_result:: +my_decimal *Type_handler_time_common:: Item_func_min_max_val_decimal(Item_func_min_max *func, my_decimal *dec) const { - MYSQL_TIME ltime; - if (func->get_date(<ime, 0)) - return 0; - return date2my_decimal(<ime, dec); + return Time(current_thd, func).to_decimal(dec); +} + + +my_decimal *Type_handler_date_common:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return Date(current_thd, func).to_decimal(dec); +} + + +my_decimal *Type_handler_datetime_common:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return Datetime(current_thd, func).to_decimal(dec); +} + + +my_decimal *Type_handler_timestamp_common:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return Datetime(current_thd, func).to_decimal(dec); } bool Type_handler_string_result:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { /* just like ::val_int() method of a string item can be called, @@ -4638,30 +4956,42 @@ bool Type_handler_string_result:: ::get_date() can be called for non-temporal values, for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09")) */ - return func->get_date_from_string(ltime, fuzzydate); + return func->get_date_from_string(thd, ltime, fuzzydate); } bool Type_handler_numeric:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { - return Item_get_date(func, ltime, fuzzydate); + return Item_get_date_with_warn(thd, func, ltime, fuzzydate); } bool Type_handler_temporal_result:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { - return func->get_date_native(ltime, fuzzydate); + /* + - If the caller specified TIME_TIME_ONLY, then it's going to convert + a DATETIME or DATE to TIME. So we pass the default flags for date. This is + exactly the same with what Item_func_min_max_val_{int|real|decimal|str} or + Item_send_datetime() do. We return the value in accordance with the + current session date flags and let the caller further convert it to TIME. + - If the caller did not specify TIME_TIME_ONLY, then return the value + according to the flags supplied by the caller. + */ + return func->get_date_native(thd, ltime, + fuzzydate & TIME_TIME_ONLY ? + sql_mode_for_dates(thd) : + fuzzydate); } bool Type_handler_time_common:: - Item_func_min_max_get_date(Item_func_min_max *func, - MYSQL_TIME *ltime, ulonglong fuzzydate) const + Item_func_min_max_get_date(THD *thd, Item_func_min_max *func, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const { - return func->get_time_native(ltime); + return func->get_time_native(thd, ltime); } /***************************************************************************/ @@ -5126,7 +5456,7 @@ bool Type_handler:: Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const { uint dec= item->decimals == NOT_FIXED_DEC ? - item->arguments()[0]->time_precision() : + item->arguments()[0]->time_precision(current_thd) : item->decimals; item->fix_attributes_temporal(MIN_TIME_WIDTH, dec); item->maybe_null= true; @@ -5148,7 +5478,7 @@ bool Type_handler:: const { uint dec= item->decimals == NOT_FIXED_DEC ? - item->arguments()[0]->datetime_precision() : + item->arguments()[0]->datetime_precision(current_thd) : item->decimals; item->fix_attributes_temporal(MAX_DATETIME_WIDTH, dec); item->maybe_null= true; @@ -5468,32 +5798,34 @@ bool Type_handler_string_result:: /***************************************************************************/ -uint Type_handler::Item_time_precision(Item *item) const +uint Type_handler::Item_time_precision(THD *thd, Item *item) const { return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); } -uint Type_handler::Item_datetime_precision(Item *item) const +uint Type_handler::Item_datetime_precision(THD *thd, Item *item) const { return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); } -uint Type_handler_string_result::Item_temporal_precision(Item *item, +uint Type_handler_string_result::Item_temporal_precision(THD *thd, Item *item, bool is_time) const { - MYSQL_TIME ltime; StringBuffer<64> buf; String *tmp; MYSQL_TIME_STATUS status; DBUG_ASSERT(item->is_fixed()); if ((tmp= item->val_str(&buf)) && - !(is_time ? - str_to_time(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_TIME_ONLY, &status) : - str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_FUZZY_DATES, &status))) + (is_time ? + Time(thd, &status, tmp->ptr(), tmp->length(), tmp->charset(), + Time::Options(TIME_TIME_ONLY, + Time::DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)). + is_valid_time() : + Datetime(&status, tmp->ptr(), tmp->length(), tmp->charset(), + TIME_FUZZY_DATES). + is_valid_datetime())) return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS); return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); } @@ -5682,7 +6014,7 @@ bool Type_handler::check_null(const Item *item, st_value *value) const bool Type_handler_null:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_NULL; return true; @@ -5690,7 +6022,7 @@ bool Type_handler_null:: bool Type_handler_row:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { DBUG_ASSERT(0); value->m_type= DYN_COL_NULL; @@ -5699,7 +6031,7 @@ bool Type_handler_row:: bool Type_handler_int_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= item->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT; value->value.m_longlong= item->val_int(); @@ -5708,7 +6040,7 @@ bool Type_handler_int_result:: bool Type_handler_real_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DOUBLE; value->value.m_double= item->val_real(); @@ -5717,7 +6049,7 @@ bool Type_handler_real_result:: bool Type_handler_decimal_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DECIMAL; my_decimal *dec= item->val_decimal(&value->m_decimal); @@ -5728,7 +6060,7 @@ bool Type_handler_decimal_result:: bool Type_handler_string_result:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_STRING; String *str= item->val_str(&value->m_string); @@ -5739,19 +6071,19 @@ bool Type_handler_string_result:: bool Type_handler_temporal_with_date:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DATETIME; - item->get_date(&value->value.m_time, sql_mode_for_dates(current_thd)); + item->get_date(thd, &value->value.m_time, sql_mode_for_dates(thd)); return check_null(item, value); } bool Type_handler_time_common:: - Item_save_in_value(Item *item, st_value *value) const + Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DATETIME; - item->get_time(&value->value.m_time); + item->get_time(thd, &value->value.m_time); return check_null(item, value); } @@ -5935,7 +6267,8 @@ bool Type_handler:: bool Type_handler:: Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const { - item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd)); + item->get_date(protocol->thd, &buf->value.m_time, + sql_mode_for_dates(protocol->thd)); if (!item->null_value) return protocol->store(&buf->value.m_time, item->decimals); return protocol->store_null(); @@ -5945,7 +6278,8 @@ bool Type_handler:: bool Type_handler:: Item_send_date(Item *item, Protocol *protocol, st_value *buf) const { - item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd)); + item->get_date(protocol->thd, &buf->value.m_time, + sql_mode_for_dates(protocol->thd)); if (!item->null_value) return protocol->store_date(&buf->value.m_time); return protocol->store_null(); @@ -5955,7 +6289,7 @@ bool Type_handler:: bool Type_handler:: Item_send_time(Item *item, Protocol *protocol, st_value *buf) const { - item->get_time(&buf->value.m_time); + item->get_time(protocol->thd, &buf->value.m_time); if (!item->null_value) return protocol->store_time(&buf->value.m_time, item->decimals); return protocol->store_null(); @@ -6014,7 +6348,7 @@ Item *Type_handler_time_common:: make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const { Item_cache_temporal *cache; - longlong value= item->val_time_packed(); + longlong value= item->val_time_packed(thd); if (item->null_value) return new (thd->mem_root) Item_null(thd, item->name.str); cache= new (thd->mem_root) Item_cache_time(thd); @@ -6028,7 +6362,7 @@ Item *Type_handler_temporal_with_date:: make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const { Item_cache_temporal *cache; - longlong value= item->val_datetime_packed(); + longlong value= item->val_datetime_packed(thd); if (item->null_value) return new (thd->mem_root) Item_null(thd, item->name.str); cache= new (thd->mem_root) Item_cache_datetime(thd); @@ -7047,8 +7381,8 @@ bool Type_handler_time_common::Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const { - longlong value0= a->val_time_packed(); - longlong value1= b->val_time_packed(); + longlong value0= a->val_time_packed(thd); + longlong value1= b->val_time_packed(thd); return !a->null_value && !b->null_value && value0 == value1; } @@ -7057,8 +7391,8 @@ bool Type_handler_temporal_with_date::Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const { - longlong value0= a->val_datetime_packed(); - longlong value1= b->val_datetime_packed(); + longlong value0= a->val_datetime_packed(thd); + longlong value1= b->val_datetime_packed(thd); return !a->null_value && !b->null_value && value0 == value1; } @@ -7124,7 +7458,7 @@ int Type_handler_temporal_with_date::stored_field_cmp_to_item(THD *thd, { MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time; field->get_date(&field_time, TIME_INVALID_DATES); - item->get_date(&item_time, TIME_INVALID_DATES); + item->get_date(thd, &item_time, TIME_INVALID_DATES); if (item_time.time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(thd, &item_time, item_time_cmp= &item_time2)) return 1; @@ -7138,7 +7472,7 @@ int Type_handler_time_common::stored_field_cmp_to_item(THD *thd, { MYSQL_TIME field_time, item_time; field->get_time(&field_time); - item->get_time(&item_time); + item->get_time(thd, &item_time); return my_time_compare(&field_time, &item_time); } @@ -7202,7 +7536,7 @@ static bool have_important_literal_warnings(const MYSQL_TIME_STATUS *status) static void literal_warn(THD *thd, const Item *item, const char *str, size_t length, CHARSET_INFO *cs, - const MYSQL_TIME *ltime, + timestamp_type time_type, const MYSQL_TIME_STATUS *st, const char *typestr, bool send_error) { @@ -7213,7 +7547,7 @@ static void literal_warn(THD *thd, const Item *item, ErrConvString err(str, length, cs); make_truncated_value_warning(thd, Sql_condition::time_warn_level(st->warnings), - &err, ltime->time_type, 0); + &err, time_type, 0); } } else if (send_error) @@ -7231,14 +7565,15 @@ Type_handler_date_common::create_literal_item(THD *thd, CHARSET_INFO *cs, bool send_error) const { - MYSQL_TIME_STATUS st; - MYSQL_TIME ltime; + Temporal::Warn st; Item_literal *item= NULL; - sql_mode_t flags= sql_mode_for_dates(thd); - if (!str_to_datetime(cs, str, length, <ime, flags, &st) && - ltime.time_type == MYSQL_TIMESTAMP_DATE && !st.warnings) - item= new (thd->mem_root) Item_date_literal(thd, <ime); - literal_warn(thd, item, str, length, cs, <ime, &st, "DATE", send_error); + Temporal_hybrid tmp(thd, &st, str, length, cs, sql_mode_for_dates(thd)); + if (tmp.is_valid_temporal() && + tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATE && + !have_important_literal_warnings(&st)) + item= new (thd->mem_root) Item_date_literal(thd, tmp.get_mysql_time()); + literal_warn(thd, item, str, length, cs, MYSQL_TIMESTAMP_DATE, + &st, "DATE", send_error); return item; } @@ -7250,15 +7585,16 @@ Type_handler_temporal_with_date::create_literal_item(THD *thd, CHARSET_INFO *cs, bool send_error) const { - MYSQL_TIME_STATUS st; - MYSQL_TIME ltime; + Temporal::Warn st; Item_literal *item= NULL; - sql_mode_t flags= sql_mode_for_dates(thd); - if (!str_to_datetime(cs, str, length, <ime, flags, &st) && - ltime.time_type == MYSQL_TIMESTAMP_DATETIME && + Temporal_hybrid tmp(thd, &st, str, length, cs, sql_mode_for_dates(thd)); + if (tmp.is_valid_temporal() && + tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATETIME && !have_important_literal_warnings(&st)) - item= new (thd->mem_root) Item_datetime_literal(thd, <ime, st.precision); - literal_warn(thd, item, str, length, cs, <ime, &st, "DATETIME", send_error); + item= new (thd->mem_root) Item_datetime_literal(thd, tmp.get_mysql_time(), + st.precision); + literal_warn(thd, item, str, length, cs, MYSQL_TIMESTAMP_DATETIME, + &st, "DATETIME", send_error); return item; } @@ -7271,12 +7607,14 @@ Type_handler_time_common::create_literal_item(THD *thd, bool send_error) const { MYSQL_TIME_STATUS st; - MYSQL_TIME ltime; Item_literal *item= NULL; - if (!str_to_time(cs, str, length, <ime, 0, &st) && - ltime.time_type == MYSQL_TIMESTAMP_TIME && + Time::Options opt(TIME_TIME_ONLY, Time::DATETIME_TO_TIME_DISALLOW); + Time tmp(thd, &st, str, length, cs, opt); + if (tmp.is_valid_time() && !have_important_literal_warnings(&st)) - item= new (thd->mem_root) Item_time_literal(thd, <ime, st.precision); - literal_warn(thd, item, str, length, cs, <ime, &st, "TIME", send_error); + item= new (thd->mem_root) Item_time_literal(thd, tmp.get_mysql_time(), + st.precision); + literal_warn(thd, item, str, length, cs, MYSQL_TIMESTAMP_TIME, + &st, "TIME", send_error); return item; } diff --git a/sql/sql_type.h b/sql/sql_type.h index 40433fdaabd..5de4c8e431a 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -96,6 +96,7 @@ protected: my_decimal *m_ptr; Dec_ptr() { } public: + Dec_ptr(my_decimal *ptr) :m_ptr(ptr) { } bool is_null() const { return m_ptr == NULL; } const my_decimal *ptr() const { return m_ptr; } const my_decimal *ptr_or(const my_decimal *def) const @@ -113,13 +114,6 @@ public: longlong to_longlong(bool unsigned_flag) { return m_ptr ? m_ptr->to_longlong(unsigned_flag) : 0; } bool to_bool() const { return m_ptr ? m_ptr->to_bool() : false; } - bool to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const char *field_name) - { - return m_ptr ? m_ptr->to_datetime_with_warn(to, fuzzydate, field_name) : - true; - } - bool to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, Item *item); String *to_string(String *to) const { return m_ptr ? m_ptr->to_string(to) : NULL; @@ -224,10 +218,10 @@ protected: bool m_truncated; // Indicates if the constructor truncated the value void make_from_decimal(const my_decimal *d); void make_from_double(double d); - void make_from_int(longlong nr, bool unsigned_val) + void make_from_int(const Longlong_hybrid &nr) { - m_neg= nr < 0 && !unsigned_val; - m_sec= m_neg ? (ulonglong) -nr : (ulonglong) nr; + m_neg= nr.neg(); + m_sec= nr.abs(); m_usec= 0; m_truncated= false; } @@ -237,20 +231,21 @@ protected: } Sec6() { } public: - Sec6(bool neg, ulonglong nr, ulong frac) - :m_sec(nr), m_usec(frac), m_neg(neg), m_truncated(false) - { } - Sec6(double nr) + explicit Sec6(double nr) { make_from_double(nr); } - Sec6(const my_decimal *d) + explicit Sec6(const my_decimal *d) { make_from_decimal(d); } - Sec6(longlong nr, bool unsigned_val) + explicit Sec6(const Longlong_hybrid &nr) + { + make_from_int(nr); + } + explicit Sec6(longlong nr, bool unsigned_val) { - make_from_int(nr, unsigned_val); + make_from_int(Longlong_hybrid(nr, unsigned_val)); } bool neg() const { return m_neg; } bool truncated() const { return m_truncated; } @@ -258,38 +253,41 @@ public: long usec() const { return m_usec; } /** Converts Sec6 to MYSQL_TIME - - @param ltime converted value will be written here + @param thd current thd + @param [out] warn conversion warnings will be written here + @param [out] ltime converted value will be written here @param fuzzydate conversion flags (TIME_INVALID_DATE, etc) - @param str original number, as an ErrConv. For the warning - @param field_name field name or NULL if not a field. For the warning @returns false for success, true for a failure */ - bool convert_to_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate, - const ErrConv *str, const char *field_name) const; + bool convert_to_mysql_time(THD *thd, + int *warn, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; // Convert a number in format hhhmmss.ff to TIME'hhh:mm:ss.ff' bool to_time(MYSQL_TIME *to, int *warn) const { - return number_to_time(m_neg, m_sec, m_usec, to, warn); + bool rc= number_to_time(m_neg, m_sec, m_usec, to, warn); + DBUG_ASSERT(*warn || !rc); + return rc; } - bool to_time_with_warn(MYSQL_TIME *to, const ErrConv *str, - const char *field_name) const; /* Convert a number in format YYYYMMDDhhmmss.ff to TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff' */ - bool to_datetime(MYSQL_TIME *to, ulonglong flags, int *warn) const + bool to_datetime(MYSQL_TIME *to, date_mode_t flags, int *warn) const { if (m_neg) { *warn= MYSQL_TIME_WARN_OUT_OF_RANGE; return true; } - return number_to_datetime(m_sec, m_usec, to, flags, warn) == -1; + bool rc= number_to_datetime(m_sec, m_usec, to, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + warn) == -1; + DBUG_ASSERT(*warn || !rc); + return rc; } - bool to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const ErrConv *str, const char *field_name) const; // Convert elapsed seconds to TIME bool sec_to_time(MYSQL_TIME *ltime, uint dec) const { @@ -325,7 +323,7 @@ class VSec6: public Sec6 { bool m_is_null; public: - VSec6(Item *item, const char *type_str, ulonglong limit); + VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit); bool is_null() const { return m_is_null; } }; @@ -368,13 +366,13 @@ public: m_neg= !m_neg; // Swap sign } } - bool to_time(MYSQL_TIME *ltime, uint decimals) const + bool to_time(THD *thd, MYSQL_TIME *ltime, uint decimals) const { if (m_error) return true; to_hh24mmssff(ltime, MYSQL_TIMESTAMP_TIME); ltime->hour+= to_days_abs() * 24; - return adjust_time_range_with_warn(ltime, decimals); + return adjust_time_range_with_warn(thd, ltime, decimals); } bool to_datetime(MYSQL_TIME *ltime) const { @@ -394,39 +392,23 @@ class Year protected: uint m_year; bool m_truncated; - bool to_mysql_time_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const char *field_name) const - { - longlong value= m_year * 10000; // Make it YYYYMMDD - const ErrConvInteger str(value, true); - Sec6 sec(false, value, 0); - return sec.convert_to_mysql_time(to, fuzzydate, &str, field_name); - } uint year_precision(const Item *item) const; public: Year(): m_year(0), m_truncated(false) { } Year(longlong value, bool unsigned_flag, uint length); uint year() const { return m_year; } + uint to_YYYYMMDD() const { return m_year * 10000; } bool truncated() const { return m_truncated; } }; -class Year_null: public Year +class Year_null: public Year, public Null_flag { -protected: - bool m_is_null; public: - Year_null(const Year &other, bool is_null) - :Year(is_null ? Year() : other), - m_is_null(is_null) + Year_null(const Longlong_null &nr, bool unsigned_flag, uint length) + :Year(nr.is_null() ? 0 : nr.value(), unsigned_flag, length), + Null_flag(nr.is_null()) { } - bool is_null() const { return m_is_null; } - bool to_mysql_time_with_warn(MYSQL_TIME *to, ulonglong fuzzydate, - const char *field_name) const - { - return m_is_null ? true : - Year::to_mysql_time_with_warn(to, fuzzydate, field_name); - } }; @@ -444,14 +426,110 @@ public: }; -class Temporal: protected MYSQL_TIME +class Double_null: public Null_flag { protected: + double m_value; +public: + Double_null(double value, bool is_null) + :Null_flag(is_null), m_value(value) + { } + double value() const { return m_value; } +}; + + +class Temporal: protected MYSQL_TIME +{ +public: + class Status: public MYSQL_TIME_STATUS + { + public: + Status() { my_time_status_init(this); } + }; + + class Warn: public ErrBuff, + public Status + { + public: + void push_conversion_warnings(THD *thd, bool totally_useless_value, date_mode_t mode, + timestamp_type tstype, const char *name) + { + const char *typestr= tstype >= 0 ? type_name_by_timestamp_type(tstype) : + mode & TIME_TIME_ONLY ? "time" : "datetime"; + Temporal::push_conversion_warnings(thd, totally_useless_value, warnings, typestr, + name, ptr()); + } + }; + + class Warn_push: public Warn + { + THD *m_thd; + const char *m_name; + const MYSQL_TIME *m_ltime; + date_mode_t m_mode; + public: + Warn_push(THD *thd, const char *name, + const MYSQL_TIME *ltime, date_mode_t mode) + :m_thd(thd), m_name(name), m_ltime(ltime), m_mode(mode) + { } + ~Warn_push() + { + if (warnings) + push_conversion_warnings(m_thd, m_ltime->time_type < 0, + m_mode, m_ltime->time_type, m_name); + } + }; + +public: + static date_mode_t sql_mode_for_dates(THD *thd); bool is_valid_temporal() const { DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); return time_type != MYSQL_TIMESTAMP_NONE; } + static const char *type_name_by_timestamp_type(timestamp_type time_type) + { + switch (time_type) { + case MYSQL_TIMESTAMP_DATE: return "date"; + case MYSQL_TIMESTAMP_TIME: return "time"; + case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH + default: + break; + } + return "datetime"; + } + static void push_conversion_warnings(THD *thd, bool totally_useless_value, int warn, + const char *type_name, + const char *field_name, + const char *value); + /* + This method is used if the item was not null but convertion to + TIME/DATE/DATETIME failed. We return a zero date if allowed, + otherwise - null. + */ + void make_fuzzy_date(int *warn, date_mode_t fuzzydate) + { + /* + In the following scenario: + - The caller expected to get a TIME value + - Item returned a not NULL string or numeric value + - But then conversion from string or number to TIME failed + we need to change the default time_type from MYSQL_TIMESTAMP_DATE + (which was set in bzero) to MYSQL_TIMESTAMP_TIME and therefore + return TIME'00:00:00' rather than DATE'0000-00-00'. + If we don't do this, methods like Item::get_time_with_conversion() + will erroneously subtract CURRENT_DATE from '0000-00-00 00:00:00' + and return TIME'-838:59:59' instead of TIME'00:00:00' as a result. + */ + timestamp_type tstype= !(fuzzydate & TIME_FUZZY_DATES) ? + MYSQL_TIMESTAMP_NONE : + fuzzydate & TIME_TIME_ONLY ? + MYSQL_TIMESTAMP_TIME : + MYSQL_TIMESTAMP_DATETIME; + set_zero_time(this, tstype); + } + +protected: my_decimal *bad_to_decimal(my_decimal *to) const; my_decimal *to_decimal(my_decimal *to) const; static double to_double(bool negate, ulonglong num, ulong frac) @@ -465,7 +543,65 @@ protected: *warn= MYSQL_TIME_WARN_OUT_OF_RANGE; time_type= MYSQL_TIMESTAMP_NONE; } + void make_from_sec6(THD *thd, MYSQL_TIME_STATUS *st, + const Sec6 &nr, date_mode_t mode) + { + if (nr.convert_to_mysql_time(thd, &st->warnings, this, mode)) + make_fuzzy_date(&st->warnings, mode); + } + void make_from_str(THD *thd, Warn *warn, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t fuzzydate); + void make_from_double(THD *thd, Warn *warn, double nr, date_mode_t mode) + { + make_from_sec6(thd, warn, Sec6(nr), mode); + if (warn->warnings) + warn->set_double(nr); + } + void make_from_longlong_hybrid(THD *thd, Warn *warn, + const Longlong_hybrid &nr, date_mode_t mode) + { + /* + Note: conversion from an integer to TIME can overflow to + '838:59:59.999999', so the conversion result can have fractional digits. + */ + make_from_sec6(thd, warn, Sec6(nr), mode); + if (warn->warnings) + warn->set_longlong(nr); + } + void make_from_decimal(THD *thd, Warn *warn, + const my_decimal *nr, date_mode_t mode) + { + make_from_sec6(thd, warn, Sec6(nr), mode); + if (warn->warnings) + warn->set_decimal(nr); + } + bool str_to_time(MYSQL_TIME_STATUS *st, const char *str, size_t length, + CHARSET_INFO *cs, date_mode_t fuzzydate); + bool str_to_datetime(MYSQL_TIME_STATUS *st, const char *str, size_t length, + CHARSET_INFO *cs, date_mode_t fuzzydate); + bool has_valid_mmssff() const + { + return minute <= TIME_MAX_MINUTE && + second <= TIME_MAX_SECOND && + second_part <= TIME_MAX_SECOND_PART; + } + bool has_zero_YYYYMM() const + { + return year == 0 && month == 0; + } + bool has_zero_YYYYMMDD() const + { + return year == 0 && month == 0 && day == 0; + } public: + static void *operator new(size_t size, MYSQL_TIME *ltime) throw() + { + DBUG_ASSERT(size == sizeof(MYSQL_TIME)); + return ltime; + } + static void operator delete(void *ptr, MYSQL_TIME *ltime) { } + long fraction_remainder(uint dec) const { return my_time_fraction_remainder(second_part, dec); @@ -481,8 +617,64 @@ public: class Temporal_hybrid: public Temporal { public: - Temporal_hybrid(THD *thd, Item *item); - Temporal_hybrid(Item *item) { *this= Temporal_hybrid(current_thd, item); } + // Contructors for Item + Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate); + Temporal_hybrid(THD *thd, Item *item) + :Temporal_hybrid(thd, item, sql_mode_for_dates(thd)) + { } + Temporal_hybrid(Item *item) + :Temporal_hybrid(current_thd, item) + { } + + // Constructors for non-NULL values + Temporal_hybrid(THD *thd, Warn *warn, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t fuzzydate) + { + make_from_str(thd, warn, str, length, cs, fuzzydate); + } + Temporal_hybrid(THD *thd, Warn *warn, + const Longlong_hybrid &nr, date_mode_t fuzzydate) + { + make_from_longlong_hybrid(thd, warn, nr, fuzzydate); + } + Temporal_hybrid(THD *thd, Warn *warn, double nr, date_mode_t fuzzydate) + { + make_from_double(thd, warn, nr, fuzzydate); + } + + // Constructors for nullable values + Temporal_hybrid(THD *thd, Warn *warn, const String *str, date_mode_t mode) + { + if (!str) + time_type= MYSQL_TIMESTAMP_NONE; + else + make_from_str(thd, warn, str->ptr(), str->length(), str->charset(), mode); + } + Temporal_hybrid(THD *thd, Warn *warn, + const Longlong_hybrid_null &nr, date_mode_t fuzzydate) + { + if (nr.is_null()) + time_type= MYSQL_TIMESTAMP_NONE; + else + make_from_longlong_hybrid(thd, warn, nr, fuzzydate); + } + Temporal_hybrid(THD *thd, Warn *warn, const Double_null &nr, date_mode_t mode) + { + if (nr.is_null()) + time_type= MYSQL_TIMESTAMP_NONE; + else + make_from_double(thd, warn, nr.value(), mode); + } + Temporal_hybrid(THD *thd, Warn *warn, const my_decimal *nr, date_mode_t mode) + { + if (!nr) + time_type= MYSQL_TIMESTAMP_NONE; + else + make_from_decimal(thd, warn, nr, mode); + } + // End of constuctors + longlong to_longlong() const { if (!is_valid_temporal()) @@ -507,6 +699,182 @@ public: str->length(my_TIME_to_str(this, const_cast<char*>(str->ptr()), dec)); return str; } + const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_temporal()); + return this; + } +}; + + +/* + This class resembles the SQL standard <extract source>, + used in extract expressions, e.g: EXTRACT(DAY FROM dt) + <extract expression> ::= + EXTRACT <left paren> <extract field> FROM <extract source> <right paren> + <extract source> ::= <datetime value expression> | <interval value expression> +*/ +class Extract_source: public Temporal_hybrid +{ + /* + Convert a TIME value to DAY-TIME interval, e.g. for extraction: + EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. + Moves full days from ltime->hour to ltime->day. + */ + void time_to_daytime_interval() + { + DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_TIME); + DBUG_ASSERT(has_zero_YYYYMMDD()); + MYSQL_TIME::day= MYSQL_TIME::hour / 24; + MYSQL_TIME::hour%= 24; + } + bool is_valid_extract_source_slow() const + { + return is_valid_temporal() && MYSQL_TIME::hour < 24 && + (has_zero_YYYYMM() || time_type != MYSQL_TIMESTAMP_TIME); + } + bool is_valid_value_slow() const + { + return time_type == MYSQL_TIMESTAMP_NONE || is_valid_extract_source_slow(); + } +public: + Extract_source(THD *thd, Item *item, date_mode_t mode) + :Temporal_hybrid(thd, item, mode) + { + if (MYSQL_TIME::time_type == MYSQL_TIMESTAMP_TIME) + time_to_daytime_interval(); + DBUG_ASSERT(is_valid_value_slow()); + } + inline const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_extract_source_slow()); + return this; + } + bool is_valid_extract_source() const { return is_valid_temporal(); } + int sign() const { return get_mysql_time()->neg ? -1 : 1; } + uint year() const { return get_mysql_time()->year; } + uint month() const { return get_mysql_time()->month; } + int day() const { return (int) get_mysql_time()->day * sign(); } + int hour() const { return (int) get_mysql_time()->hour * sign(); } + int minute() const { return (int) get_mysql_time()->minute * sign(); } + int second() const { return (int) get_mysql_time()->second * sign(); } + int microsecond() const { return (int) get_mysql_time()->second_part * sign(); } + + uint year_month() const { return year() * 100 + month(); } + uint quarter() const { return (month() + 2)/3; } + uint week(THD *thd) const; + + longlong second_microsecond() const + { + return (second() * 1000000LL + microsecond()); + } + + // DAY TO XXX + longlong day_hour() const + { + return (longlong) day() * 100LL + hour(); + } + longlong day_minute() const + { + return day_hour() * 100LL + minute(); + } + longlong day_second() const + { + return day_minute() * 100LL + second(); + } + longlong day_microsecond() const + { + return day_second() * 1000000LL + microsecond(); + } + + // HOUR TO XXX + int hour_minute() const + { + return hour() * 100 + minute(); + } + int hour_second() const + { + return hour_minute() * 100 + second(); + } + longlong hour_microsecond() const + { + return hour_second() * 1000000LL + microsecond(); + } + + // MINUTE TO XXX + int minute_second() const + { + return minute() * 100 + second(); + } + longlong minute_microsecond() const + { + return minute_second() * 1000000LL + microsecond(); + } +}; + + +/* + This class is used for the "time_interval" argument of these SQL functions: + TIMESTAMP(tm,time_interval) + ADDTIME(tm,time_interval) + Features: + - DATE and DATETIME formats are treated as errors + - Preserves hours for TIME format as is, without limiting to TIME_MAX_HOUR +*/ +class Interval_DDhhmmssff: public Temporal +{ + static const LEX_CSTRING m_type_name; + bool str_to_DDhhmmssff(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + ulong max_hour); + void push_warning_wrong_or_truncated_value(THD *thd, + const ErrConv &str, + int warnings); + bool is_valid_interval_DDhhmmssff_slow() const + { + return time_type == MYSQL_TIMESTAMP_TIME && + has_zero_YYYYMMDD() && has_valid_mmssff(); + } + bool is_valid_value_slow() const + { + return time_type == MYSQL_TIMESTAMP_NONE || + is_valid_interval_DDhhmmssff_slow(); + } +public: + // Get fractional second precision from an Item + static uint fsp(THD *thd, Item *item); + /* + Maximum useful HOUR value: + TIMESTAMP'0001-01-01 00:00:00' + '87649415:59:59' = '9999-12-31 23:59:59' + This gives maximum possible interval values: + - '87649415:59:59.999999' (in 'hh:mm:ss.ff' format) + - '3652058 23:59:59.999999' (in 'DD hh:mm:ss.ff' format) + */ + static uint max_useful_hour() + { + return 87649415; + } +public: + Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings, + Item *item, ulong max_hour); + Interval_DDhhmmssff(THD *thd, Item *item) + { + Status st; + new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour()); + } + const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_interval_DDhhmmssff_slow()); + return this; + } + bool is_valid_interval_DDhhmmssff() const + { + return time_type == MYSQL_TIMESTAMP_TIME; + } + bool is_valid_value() const + { + return time_type == MYSQL_TIMESTAMP_NONE || is_valid_interval_DDhhmmssff(); + } }; @@ -533,6 +901,7 @@ class Time: public Temporal public: enum datetime_to_time_mode_t { + DATETIME_TO_TIME_DISALLOW, DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS, DATETIME_TO_TIME_YYYYMMDD_TRUNCATE, DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY, @@ -540,22 +909,22 @@ public: }; class Options { - sql_mode_t m_get_date_flags; + date_mode_t m_get_date_flags; datetime_to_time_mode_t m_datetime_to_time_mode; public: Options() :m_get_date_flags(flags_for_get_date()), m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) { } - Options(sql_mode_t flags) + Options(date_mode_t flags) :m_get_date_flags(flags), m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) { } - Options(sql_mode_t flags, datetime_to_time_mode_t dtmode) + Options(date_mode_t flags, datetime_to_time_mode_t dtmode) :m_get_date_flags(flags), m_datetime_to_time_mode(dtmode) { } - sql_mode_t get_date_flags() const + date_mode_t get_date_flags() const { return m_get_date_flags; } datetime_to_time_mode_t datetime_to_time_mode() const { return m_datetime_to_time_mode; } @@ -571,6 +940,10 @@ public: Options_for_cast() :Options(flags_for_get_date(), DATETIME_TO_TIME_YYYYMMDD_TRUNCATE) { } + Options_for_cast(date_mode_t mode) + :Options(flags_for_get_date() | (mode & TIME_FUZZY_DATES), + DATETIME_TO_TIME_YYYYMMDD_TRUNCATE) + { } }; private: bool is_valid_value_slow() const @@ -580,10 +953,7 @@ private: bool is_valid_time_slow() const { return time_type == MYSQL_TIMESTAMP_TIME && - year == 0 && month == 0 && day == 0 && - minute <= TIME_MAX_MINUTE && - second <= TIME_MAX_SECOND && - second_part <= TIME_MAX_SECOND_PART; + has_zero_YYYYMMDD() && has_valid_mmssff(); } void hhmmssff_copy(const MYSQL_TIME *from) { @@ -611,7 +981,7 @@ private: { MYSQL_TIME current_date, tmp; set_current_date(thd, ¤t_date); - calc_time_diff(this, ¤t_date, 1, &tmp, 0); + calc_time_diff(this, ¤t_date, 1, &tmp, date_mode_t(0)); static_cast<MYSQL_TIME*>(this)[0]= tmp; int warnings= 0; (void) check_time_range(this, TIME_SECOND_PART_DIGITS, &warnings); @@ -623,7 +993,7 @@ private: e.g. returned from Item::get_date(), str_to_time(), number_to_time(). After this call, "this" is a valid TIME value. */ - void valid_datetime_to_valid_time(int *warn, const Options opt) + void valid_datetime_to_valid_time(THD *thd, int *warn, const Options opt) { DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE || time_type == MYSQL_TIMESTAMP_DATETIME); @@ -640,7 +1010,7 @@ private: DBUG_ASSERT(hour < 24); if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_MINUS_CURRENT_DATE) { - datetime_to_time_minus_current_date(current_thd); + datetime_to_time_minus_current_date(thd); } else { @@ -665,7 +1035,7 @@ private: - either a valid TIME (within the supported TIME range), - or MYSQL_TIMESTAMP_NONE */ - void valid_MYSQL_TIME_to_valid_value(int *warn, const Options opt) + void valid_MYSQL_TIME_to_valid_value(THD *thd, int *warn, const Options opt) { switch (time_type) { case MYSQL_TIMESTAMP_DATE: @@ -674,8 +1044,10 @@ private: DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY && (year || month || day)) make_from_out_of_range(warn); + else if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_DISALLOW) + make_from_out_of_range(warn); else - valid_datetime_to_valid_time(warn, opt); + valid_datetime_to_valid_time(thd, warn, opt); break; case MYSQL_TIMESTAMP_NONE: break; @@ -694,11 +1066,11 @@ private: We trust that xxx_to_time() returns a valid TIME/DATE/DATETIME value, so here we need to do only simple validation. */ - void xxx_to_time_result_to_valid_value(int *warn, const Options opt) + void xxx_to_time_result_to_valid_value(THD *thd, int *warn, const Options opt) { // str_to_time(), number_to_time() never return MYSQL_TIMESTAMP_ERROR DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); - valid_MYSQL_TIME_to_valid_value(warn, opt); + valid_MYSQL_TIME_to_valid_value(thd, warn, opt); } void adjust_time_range_or_invalidate(int *warn) { @@ -711,7 +1083,7 @@ private: long curdays); void make_from_time(int *warn, const MYSQL_TIME *from); void make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays); - void make_from_item(int *warn, Item *item, const Options opt); + void make_from_item(THD *thd, int *warn, Item *item, const Options opt); public: /* All constructors that accept an "int *warn" parameter initialize *warn. @@ -719,44 +1091,62 @@ public: */ Time() { time_type= MYSQL_TIMESTAMP_NONE; } Time(Item *item) + :Time(current_thd, item, Options()) + { } + Time(THD *thd, Item *item, const Options opt) { int warn; - make_from_item(&warn, item, Options()); - } - Time(Item *item, const Options opt) - { - int warn; - make_from_item(&warn, item, opt); + make_from_item(thd, &warn, item, opt); } + Time(THD *thd, Item *item) + :Time(thd, item, Options()) + { } Time(int *warn, const MYSQL_TIME *from, long curdays); - Time(int *warn, const char *str, size_t len, CHARSET_INFO *cs, + Time(THD *thd, MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, const Options opt) { - MYSQL_TIME_STATUS status; - if (str_to_time(cs, str, len, this, opt.get_date_flags(), &status)) + if (str_to_time(status, str, len, cs, opt.get_date_flags())) time_type= MYSQL_TIMESTAMP_NONE; // The below call will optionally add notes to already collected warnings: - xxx_to_time_result_to_valid_value(&status.warnings, opt); - *warn= status.warnings; + xxx_to_time_result_to_valid_value(thd, &status->warnings, opt); } - Time(int *warn, const Sec6 &nr, const Options opt) + Time(THD *thd, int *warn, const Sec6 &nr, const Options opt) { if (nr.to_time(this, warn)) time_type= MYSQL_TIMESTAMP_NONE; - xxx_to_time_result_to_valid_value(warn, opt); + xxx_to_time_result_to_valid_value(thd, warn, opt); } - Time(int *warn, double nr) - :Temporal(Time(warn, Sec6(nr), Options())) - { } - Time(int *warn, longlong nr, bool unsigned_val) - :Temporal(Time(warn, Sec6(nr, unsigned_val), Options())) + Time(THD *thd, int *warn, const Sec6 &nr) + :Time(thd, warn, nr, Options()) { } - Time(int *warn, const my_decimal *d) - :Temporal(Time(warn, Sec6(d), Options())) - { } - static sql_mode_t flags_for_get_date() + + Time(THD *thd, Item *item, const Options opt, uint dec) + :Time(thd, item, opt) + { + trunc(dec); + } + Time(int *warn, const MYSQL_TIME *from, long curdays, uint dec) + :Time(warn, from, curdays) + { + trunc(dec); + } + Time(THD *thd, MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + const Options &opt, uint dec) + :Time(thd, status, str, len, cs, opt) + { + trunc(dec); + } + Time(THD *thd, int *warn, const Sec6 &nr, uint dec) + :Time(thd, warn, nr) + { + trunc(dec); + } + + static date_mode_t flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES; } - static sql_mode_t comparison_flags_for_get_date() + static date_mode_t comparison_flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES | TIME_FUZZY_DATES; } bool is_valid_time() const { @@ -829,11 +1219,12 @@ public: { return is_valid_time() ? Temporal::to_packed() : 0; } - Time trunc(uint dec) const + Time &trunc(uint dec) { - Time tm(*this); - my_time_trunc(&tm, dec); - return tm; + if (is_valid_time()) + my_time_trunc(this, dec); + DBUG_ASSERT(is_valid_value_slow()); + return *this; } }; @@ -864,14 +1255,39 @@ public: class Temporal_with_date: public Temporal { protected: - void check_date_or_invalidate(int *warn, sql_mode_t flags); - void make_from_item(THD *thd, Item *item, sql_mode_t flags); + void check_date_or_invalidate(int *warn, date_mode_t flags); + void make_from_item(THD *thd, Item *item, date_mode_t flags); void make_from_item(THD *thd, Item *item); + + ulong daynr() const + { + return (ulong) ::calc_daynr((uint) year, (uint) month, (uint) day); + } + ulong dayofyear() const + { + return (ulong) (daynr() - ::calc_daynr(year, 1, 1) + 1); + } + uint quarter() const + { + return (month + 2) / 3; + } + uint week(uint week_behaviour) const + { + uint year; + return calc_week(this, week_behaviour, &year); + } + uint yearweek(uint week_behaviour) const + { + uint year; + uint week= calc_week(this, week_behaviour, &year); + return week + year * 100; + } + Temporal_with_date() { time_type= MYSQL_TIMESTAMP_NONE; } - Temporal_with_date(THD *thd, Item *item, sql_mode_t flags) + Temporal_with_date(THD *thd, Item *item, date_mode_t flags) { make_from_item(thd, item, flags); } @@ -879,27 +1295,26 @@ protected: { make_from_item(thd, item); } - Temporal_with_date(int *warn, const Sec6 &nr, sql_mode_t flags) + Temporal_with_date(int *warn, const Sec6 &nr, date_mode_t flags) { - DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0); + DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); if (nr.to_datetime(this, flags, warn)) time_type= MYSQL_TIMESTAMP_NONE; } - Temporal_with_date(int *warn, const char *str, size_t len, CHARSET_INFO *cs, - sql_mode_t flags) + Temporal_with_date(MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + date_mode_t flags) { - DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0); - MYSQL_TIME_STATUS status; - if (str_to_datetime(cs, str, len, this, flags, &status)) + DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); + if (str_to_datetime(status, str, len, cs, flags)) time_type= MYSQL_TIMESTAMP_NONE; - *warn= status.warnings; } public: - bool check_date_with_warn(ulonglong flags) + bool check_date_with_warn(THD *thd, date_mode_t flags) { - return ::check_date_with_warn(this, flags, MYSQL_TIMESTAMP_ERROR); + return ::check_date_with_warn(thd, this, flags, MYSQL_TIMESTAMP_ERROR); } - static sql_mode_t comparison_flags_for_get_date() + static date_mode_t comparison_flags_for_get_date() { return TIME_INVALID_DATES | TIME_FUZZY_DATES; } }; @@ -925,7 +1340,7 @@ class Date: public Temporal_with_date return !check_datetime_range(this); } public: - Date(THD *thd, Item *item, sql_mode_t flags) + Date(THD *thd, Item *item, date_mode_t flags) :Temporal_with_date(thd, item, flags) { if (time_type == MYSQL_TIMESTAMP_DATETIME) @@ -940,12 +1355,8 @@ public: DBUG_ASSERT(is_valid_value_slow()); } Date(Item *item) - :Temporal_with_date(current_thd, item) - { - if (time_type == MYSQL_TIMESTAMP_DATETIME) - datetime_to_date(this); - DBUG_ASSERT(is_valid_value_slow()); - } + :Date(current_thd, item) + { } Date(const Temporal_with_date *d) :Temporal_with_date(*d) { @@ -973,6 +1384,32 @@ public: *ltime= *this; return false; } + ulong daynr() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::daynr(); + } + ulong dayofyear() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::dayofyear(); + } + uint quarter() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::quarter(); + } + uint week(uint week_behaviour) const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::week(week_behaviour); + } + uint yearweek(uint week_behaviour) const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal_with_date::yearweek(week_behaviour); + } + longlong to_longlong() const { return is_valid_date() ? (longlong) TIME_to_ulonglong_date(this) : 0LL; @@ -1024,11 +1461,11 @@ class Datetime: public Temporal_with_date date_to_datetime(this); } void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, - sql_mode_t flags); + date_mode_t flags); void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, - sql_mode_t flags); + date_mode_t flags); public: - Datetime(THD *thd, Item *item, sql_mode_t flags) + Datetime(THD *thd, Item *item, date_mode_t flags) :Temporal_with_date(thd, item, flags) { date_to_datetime_if_needed(); @@ -1041,47 +1478,50 @@ public: DBUG_ASSERT(is_valid_value_slow()); } Datetime(Item *item) - :Temporal_with_date(current_thd, item) - { - date_to_datetime_if_needed(); - DBUG_ASSERT(is_valid_value_slow()); - } - Datetime(THD *thd, int *warn, const MYSQL_TIME *from, sql_mode_t flags); + :Datetime(current_thd, item) + { } + Datetime(THD *thd, int *warn, const MYSQL_TIME *from, date_mode_t flags); Datetime() { set_zero_time(this, MYSQL_TIMESTAMP_DATETIME); } - Datetime(int *warn, const char *str, size_t len, CHARSET_INFO *cs, - sql_mode_t flags) - :Temporal_with_date(warn, str, len, cs, flags) + Datetime(MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + date_mode_t flags) + :Temporal_with_date(status, str, len, cs, flags) { date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } - Datetime(int *warn, double nr, sql_mode_t flags) - :Temporal_with_date(warn, Sec6(nr), flags) + Datetime(int *warn, const Sec6 &nr, date_mode_t flags) + :Temporal_with_date(warn, nr, flags) { date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } - Datetime(int *warn, const my_decimal *d, sql_mode_t flags) - :Temporal_with_date(warn, Sec6(d), flags) + + Datetime(THD *thd, Item *item, date_mode_t flags, uint dec) + :Datetime(thd, item, flags) { - date_to_datetime_if_needed(); - DBUG_ASSERT(is_valid_value_slow()); + trunc(dec); } - /* - Create a Datime object from a longlong number. - Note, unlike in Time(), we don't need an "unsigned_val" here, - as it's not important if overflow happened because - of a negative number, or because of a huge positive number. - */ - Datetime(int *warn, longlong sec, ulong usec, sql_mode_t flags) - :Temporal_with_date(warn, Sec6(false, (ulonglong) sec, usec), flags) + Datetime(MYSQL_TIME_STATUS *status, + const char *str, size_t len, CHARSET_INFO *cs, + date_mode_t fuzzydate, uint dec) + :Datetime(status, str, len, cs, fuzzydate) { - Sec6 nr(false, (ulonglong) sec, usec); - date_to_datetime_if_needed(); - DBUG_ASSERT(is_valid_value_slow()); + trunc(dec); + } + Datetime(int *warn, const Sec6 &nr, date_mode_t fuzzydate, uint dec) + :Datetime(warn, nr, fuzzydate) + { + trunc(dec); + } + Datetime(THD *thd, int *warn, const MYSQL_TIME *from, + date_mode_t fuzzydate, uint dec) + :Datetime(thd, warn, from, fuzzydate) + { + trunc(dec); } bool is_valid_datetime() const @@ -1093,11 +1533,42 @@ public: DBUG_ASSERT(is_valid_value_slow()); return time_type == MYSQL_TIMESTAMP_DATETIME; } + bool check_date(date_mode_t flags, int *warnings) const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return ::check_date(this, (year || month || day), + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + warnings); + } + bool check_date(date_mode_t flags) const + { + int dummy; /* unused */ + return check_date(flags, &dummy); + } bool hhmmssff_is_zero() const { DBUG_ASSERT(is_valid_datetime_slow()); return hour == 0 && minute == 0 && second == 0 && second_part == 0; } + ulong daynr() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal_with_date::daynr(); + } + longlong hhmmss_to_seconds_abs() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return hour * 3600L + minute * 60 + second; + } + longlong hhmmss_to_seconds() const + { + return neg ? -hhmmss_to_seconds_abs() : hhmmss_to_seconds_abs(); + } + longlong to_seconds() const + { + return hhmmss_to_seconds() + (longlong) daynr() * 24L * 3600L; + } + const MYSQL_TIME *get_mysql_time() const { DBUG_ASSERT(is_valid_datetime_slow()); @@ -1156,11 +1627,12 @@ public: { return is_valid_datetime() ? Temporal::to_packed() : 0; } - Datetime trunc(uint dec) const + Datetime &trunc(uint dec) { - Datetime tm(*this); - my_time_trunc(&tm, dec); - return tm; + if (is_valid_datetime()) + my_time_trunc(this, dec); + DBUG_ASSERT(is_valid_value_slow()); + return *this; } }; @@ -1968,8 +2440,8 @@ public: virtual bool can_return_time() const { return true; } virtual bool is_bool_type() const { return false; } virtual bool is_general_purpose_string_type() const { return false; } - virtual uint Item_time_precision(Item *item) const; - virtual uint Item_datetime_precision(Item *item) const; + virtual uint Item_time_precision(THD *thd, Item *item) const; + virtual uint Item_datetime_precision(THD *thd, Item *item) const; virtual uint Item_decimal_scale(const Item *item) const; virtual uint Item_decimal_precision(const Item *item) const= 0; /* @@ -2092,7 +2564,7 @@ public: virtual uint32 max_display_length(const Item *item) const= 0; virtual uint32 calc_pack_length(uint32 length) const= 0; virtual void Item_update_null_value(Item *item) const= 0; - virtual bool Item_save_in_value(Item *item, st_value *value) const= 0; + virtual bool Item_save_in_value(THD *thd, Item *item, st_value *value) const= 0; virtual void Item_param_setup_conversion(THD *thd, Item_param *) const {} virtual void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; @@ -2227,8 +2699,11 @@ public: bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const= 0; virtual bool Item_val_bool(Item *item) const= 0; - virtual bool Item_get_date(Item *item, MYSQL_TIME *ltime, - ulonglong fuzzydate) const= 0; + virtual void Item_get_date(THD *thd, Item *item, + Temporal::Warn *buff, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const= 0; + bool Item_get_date_with_warn(THD *thd, Item *item, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const; virtual longlong Item_val_int_signed_typecast(Item *item) const= 0; virtual longlong Item_val_int_unsigned_typecast(Item *item) const= 0; @@ -2249,9 +2724,15 @@ public: Item_func_hybrid_field_type *, my_decimal *) const= 0; virtual - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const= 0; + date_mode_t fuzzydate) const= 0; + bool Item_func_hybrid_field_type_get_date_with_warn(THD *thd, + Item_func_hybrid_field_type *, + MYSQL_TIME *, + date_mode_t) const; virtual String *Item_func_min_max_val_str(Item_func_min_max *, String *) const= 0; virtual @@ -2262,8 +2743,8 @@ public: my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const= 0; virtual - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const= 0; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const= 0; virtual bool Item_func_between_fix_length_and_dec(Item_func_between *func) const= 0; virtual longlong @@ -2451,7 +2932,7 @@ public: DBUG_ASSERT(0); return DECIMAL_MAX_PRECISION; } - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, @@ -2513,10 +2994,12 @@ public: DBUG_ASSERT(0); return false; } - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const + void Item_get_date(THD *thd, Item *item, + Temporal::Warn *warn, MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { DBUG_ASSERT(0); - return true; + set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); } longlong Item_val_int_signed_typecast(Item *item) const { @@ -2558,12 +3041,14 @@ public: DBUG_ASSERT(0); return NULL; } - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, - MYSQL_TIME *, - ulonglong fuzzydate) const + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, + MYSQL_TIME *ltime, + date_mode_t fuzzydate) const { DBUG_ASSERT(0); - return true; + set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); } String *Item_func_min_max_val_str(Item_func_min_max *, String *) const @@ -2587,8 +3072,8 @@ public: DBUG_ASSERT(0); return NULL; } - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const { DBUG_ASSERT(0); return true; @@ -2672,8 +3157,8 @@ public: longlong Item_func_min_max_val_int(Item_func_min_max *) const; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; virtual ~Type_handler_numeric() { } bool can_change_cond_ref_to_const(Item_bool_func2 *target, Item *target_expr, Item *target_value, @@ -2709,7 +3194,7 @@ public: bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const; uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, @@ -2733,7 +3218,8 @@ public: bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -2746,9 +3232,11 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; longlong Item_func_between_val_int(Item_func_between *func) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -2800,7 +3288,7 @@ public: return va.ptr() && vb.ptr() && !va.cmp(vb); } uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; bool Item_param_set_from_value(THD *thd, @@ -2829,10 +3317,8 @@ public: { return VDec(item).to_bool(); } - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const - { - return VDec(item).to_datetime_with_warn(ltime, fuzzydate, item); - } + void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const { @@ -2848,9 +3334,11 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; longlong Item_func_between_val_int(Item_func_between *func) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -3017,7 +3505,7 @@ public: bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const; uint Item_decimal_precision(const Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, @@ -3037,7 +3525,8 @@ public: bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -3050,9 +3539,11 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; longlong Item_func_between_val_int(Item_func_between *func) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; @@ -3110,12 +3601,15 @@ public: Item *source_expr, Item *source_const) const; bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const; + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const; bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -3128,15 +3622,13 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const; - double Item_func_min_max_val_real(Item_func_min_max *) const; - longlong Item_func_min_max_val_int(Item_func_min_max *) const; - my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, - my_decimal *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; bool Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *) const; @@ -3155,7 +3647,7 @@ public: class Type_handler_string_result: public Type_handler { - uint Item_temporal_precision(Item *item, bool is_time) const; + uint Item_temporal_precision(THD *thd, Item *item, bool is_time) const; public: Item_result result_type() const { return STRING_RESULT; } Item_result cmp_type() const { return STRING_RESULT; } @@ -3186,17 +3678,17 @@ public: bool binary_cmp) const; bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const; - uint Item_time_precision(Item *item) const + uint Item_time_precision(THD *thd, Item *item) const { - return Item_temporal_precision(item, true); + return Item_temporal_precision(thd, item, true); } - uint Item_datetime_precision(Item *item) const + uint Item_datetime_precision(THD *thd, Item *item) const { - return Item_temporal_precision(item, false); + return Item_temporal_precision(thd, item, false); } uint Item_decimal_precision(const Item *item) const; void Item_update_null_value(Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; void Item_param_setup_conversion(THD *thd, Item_param *) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; @@ -3234,7 +3726,8 @@ public: bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; bool Item_val_bool(Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; + void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const; longlong Item_val_int_signed_typecast(Item *item) const; longlong Item_val_int_unsigned_typecast(Item *item) const; String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; @@ -3247,16 +3740,18 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; double Item_func_min_max_val_real(Item_func_min_max *) const; longlong Item_func_min_max_val_int(Item_func_min_max *) const; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; longlong Item_func_between_val_int(Item_func_between *func) const; bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const; @@ -3568,10 +4063,13 @@ public: const Column_definition_attributes *attr, uint32 flags) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; - bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *item, + void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const; + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *item, + Temporal::Warn *, MYSQL_TIME *to, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; }; @@ -3730,7 +4228,7 @@ public: int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; void Column_definition_implicit_upgrade(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { return Item_send_time(item, protocol, buf); @@ -3753,12 +4251,18 @@ public: my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, my_decimal *) const; - bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, MYSQL_TIME *, - ulonglong fuzzydate) const; + date_mode_t fuzzydate) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; - bool Item_func_min_max_get_date(Item_func_min_max*, - MYSQL_TIME *, ulonglong fuzzydate) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, + MYSQL_TIME *, date_mode_t fuzzydate) const; longlong Item_func_between_val_int(Item_func_between *func) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; bool set_comparator_func(Arg_comparator *cmp) const; @@ -3834,7 +4338,7 @@ public: bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const; int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { return Item_send_date(item, protocol, buf); @@ -3874,6 +4378,10 @@ public: String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -3970,6 +4478,10 @@ public: String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -4070,6 +4582,10 @@ public: String *print_item_value(THD *thd, Item *item, String *str) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -4221,7 +4737,7 @@ public: uint32 calc_pack_length(uint32 length) const { return 0; } bool Item_const_eq(const Item_const *a, const Item_const *b, bool binary_cmp) const; - bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; diff --git a/sql/sql_type_int.h b/sql/sql_type_int.h new file mode 100644 index 00000000000..1cc93415176 --- /dev/null +++ b/sql/sql_type_int.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2018, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef SQL_TYPE_INT_INCLUDED +#define SQL_TYPE_INT_INCLUDED + + +class Null_flag +{ +protected: + bool m_is_null; +public: + bool is_null() const { return m_is_null; } + Null_flag(bool is_null) :m_is_null(is_null) { } +}; + + +class Longlong +{ +protected: + longlong m_value; +public: + longlong value() const { return m_value; } + Longlong(longlong nr) :m_value(nr) { } +}; + + +class Longlong_null: public Longlong, public Null_flag +{ +public: + Longlong_null(longlong nr, bool is_null) + :Longlong(nr), Null_flag(is_null) + { } +}; + + +// A longlong/ulonglong hybrid. Good to store results of val_int(). +class Longlong_hybrid: public Longlong +{ +protected: + bool m_unsigned; +public: + Longlong_hybrid(longlong nr, bool unsigned_flag) + :Longlong(nr), m_unsigned(unsigned_flag) + { } + bool is_unsigned() const { return m_unsigned; } + bool neg() const { return m_value < 0 && !m_unsigned; } + ulonglong abs() const + { + if (m_unsigned) + return (ulonglong) m_value; + if (m_value == LONGLONG_MIN) // avoid undefined behavior + return ((ulonglong) LONGLONG_MAX) + 1; + return m_value < 0 ? -m_value : m_value; + } +}; + + +class Longlong_hybrid_null: public Longlong_hybrid, + public Null_flag +{ +public: + Longlong_hybrid_null(const Longlong_null &nr, bool unsigned_flag) + :Longlong_hybrid(nr.value(), unsigned_flag), + Null_flag(nr.is_null()) + { } +}; + + +#endif // SQL_TYPE_INT_INCLUDED diff --git a/sql/sql_union.cc b/sql/sql_union.cc index be987b82cba..1910ad0f83e 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -1321,6 +1321,8 @@ bool st_select_lex_unit::optimize() thd->lex->current_select= lex_select_save; DBUG_RETURN(TRUE); } + if (derived) + sl->increase_derived_records(sl->tvc->get_records()); continue; } thd->lex->current_select= sl; @@ -1726,6 +1728,7 @@ bool st_select_lex_unit::exec_recursive() } } thd->lex->current_select= sl; + set_limit(sl); if (sl->tvc) sl->tvc->exec(sl); else @@ -1794,6 +1797,37 @@ bool st_select_lex_unit::cleanup() { DBUG_RETURN(FALSE); } + /* + When processing a PS/SP or an EXPLAIN command cleanup of a unit can + be performed immediately when the unit is reached in the cleanup + traversal initiated by the cleanup of the main unit. + */ + if (!thd->stmt_arena->is_stmt_prepare() && !thd->lex->describe && + with_element && with_element->is_recursive && union_result) + { + select_union_recursive *result= with_element->rec_result; + if (++result->cleanup_count == with_element->rec_outer_references) + { + /* + Perform cleanup for with_element and for all with elements + mutually recursive with it. + */ + cleaned= 1; + with_element->get_next_mutually_recursive()->spec->cleanup(); + } + else + { + /* + Just increment by 1 cleanup_count for with_element and + for all with elements mutually recursive with it. + */ + With_element *with_elem= with_element; + while ((with_elem= with_elem->get_next_mutually_recursive()) != + with_element) + with_elem->rec_result->cleanup_count++; + DBUG_RETURN(FALSE); + } + } cleaned= 1; for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) @@ -1824,7 +1858,7 @@ bool st_select_lex_unit::cleanup() if (with_element && with_element->is_recursive) { - if (union_result ) + if (union_result) { ((select_union_recursive *) union_result)->cleanup(); delete union_result; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6994ffa04a9..63240c1547d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1255,7 +1255,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, table_list->register_want_access(SELECT_ACL); #endif - thd->lex->allow_sum_func= 0; + thd->lex->allow_sum_func.clear_all(); /* We do not call DT_MERGE_FOR_INSERT because it has no sense for simple @@ -2268,7 +2268,7 @@ multi_update::~multi_update() TABLE_LIST *table; for (table= update_tables ; table; table= table->next_local) { - table->table->no_keyread= table->table->no_cache= 0; + table->table->no_keyread= 0; if (ignore) table->table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index f2fc4b6bf75..e475a3d3719 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -711,9 +711,10 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, DBUG_RETURN(0); #ifdef WITH_WSREP - error: - res= TRUE; -#endif /* WITH_WSREP */ +wsrep_error_label: + res= true; +#endif + err: lex->link_first_table_back(view, link_to_local); unit->cleanup(); @@ -1559,9 +1560,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, { /* We have to keep the lock type for sequence tables */ if (!tbl->sequence) - tbl->lock_type= table->lock_type; - tbl->mdl_request.set_type((tbl->lock_type >= TL_WRITE_ALLOW_WRITE) ? - MDL_SHARED_WRITE : MDL_SHARED_READ); + tbl->lock_type= table->lock_type; + tbl->mdl_request.set_type(table->mdl_request.type); } /* If the view is mergeable, we might want to diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 38fdd8ab80b..dcc6a7bac1c 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -82,19 +82,32 @@ void Window_spec::print(String *str, enum_query_type query_type) { str->append('('); + print_partition(str, query_type); + print_order(str, query_type); + + if (window_frame) + window_frame->print(str, query_type); + str->append(')'); +} + +void +Window_spec::print_partition(String *str, enum_query_type query_type) +{ if (partition_list->first) { str->append(STRING_WITH_LEN(" partition by ")); st_select_lex::print_order(str, partition_list->first, query_type); } +} + +void +Window_spec::print_order(String *str, enum_query_type query_type) +{ if (order_list->first) { str->append(STRING_WITH_LEN(" order by ")); st_select_lex::print_order(str, order_list->first, query_type); } - if (window_frame) - window_frame->print(str, query_type); - str->append(')'); } bool @@ -2928,7 +2941,7 @@ bool Window_func_runner::exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result) } -bool Window_funcs_sort::exec(JOIN *join) +bool Window_funcs_sort::exec(JOIN *join, bool keep_filesort_result) { THD *thd= join->thd; JOIN_TAB *join_tab= join->join_tab + join->total_join_tab_cnt(); @@ -2943,8 +2956,11 @@ bool Window_funcs_sort::exec(JOIN *join) bool is_error= runner.exec(thd, tbl, filesort_result); - delete join_tab->filesort_result; - join_tab->filesort_result= NULL; + if (!keep_filesort_result) + { + delete join_tab->filesort_result; + join_tab->filesort_result= NULL; + } return is_error; } @@ -3053,14 +3069,18 @@ bool Window_funcs_computation::setup(THD *thd, } -bool Window_funcs_computation::exec(JOIN *join) +bool Window_funcs_computation::exec(JOIN *join, bool keep_last_filesort_result) { List_iterator<Window_funcs_sort> it(win_func_sorts); Window_funcs_sort *srt; + uint counter= 0; /* Count how many sorts we've executed. */ /* Execute each sort */ while ((srt = it++)) { - if (srt->exec(join)) + counter++; + bool keep_filesort_result= keep_last_filesort_result && + counter == win_func_sorts.elements; + if (srt->exec(join, keep_filesort_result)) return true; } return false; diff --git a/sql/sql_window.h b/sql/sql_window.h index 392f89e8f03..21f2c8af108 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -147,6 +147,8 @@ class Window_spec : public Sql_alloc } void print(String *str, enum_query_type query_type); + void print_order(String *str, enum_query_type query_type); + void print_partition(String *str, enum_query_type query_type); }; @@ -211,7 +213,7 @@ class Window_funcs_sort : public Sql_alloc public: bool setup(THD *thd, SQL_SELECT *sel, List_iterator<Item_window_func> &it, st_join_table *join_tab); - bool exec(JOIN *join); + bool exec(JOIN *join, bool keep_filesort_result); void cleanup() { delete filesort; } friend class Window_funcs_computation; @@ -241,7 +243,7 @@ class Window_funcs_computation : public Sql_alloc List<Window_funcs_sort> win_func_sorts; public: bool setup(THD *thd, List<Item_window_func> *window_funcs, st_join_table *tab); - bool exec(JOIN *join); + bool exec(JOIN *join, bool keep_last_filesort_result); Explain_aggr_window_funcs *save_explain_plan(MEM_ROOT *mem_root, bool is_analyze); void cleanup(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cf894325ba5..9a19a5b9c8c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -817,10 +817,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 58 shift/reduce conflicts. + Currently there are 52 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 58 +%expect 52 /* Comments for TOKENS. @@ -1582,6 +1582,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY + +/* + Give ESCAPE (in LIKE) a very low precedence. + This allows the concatenation operator || to be used on the right + side of "LIKE" with sql_mode=PIPES_AS_CONCAT (without ORACLE): + SELECT 'ab' LIKE 'a'||'b'||'c'; +*/ +%left PREC_BELOW_ESCAPE +%left ESCAPE_SYM + %left SET_VAR %left OR_SYM OR2_SYM %left XOR @@ -1591,7 +1601,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left NOT_SYM %left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE -%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE REGEXP IN_SYM +%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE SOUNDS_SYM REGEXP IN_SYM %left '|' %left '&' %left SHIFT_LEFT SHIFT_RIGHT @@ -1623,6 +1633,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); SELECT system FROM t1; ALTER TABLE DROP SYSTEM VERSIONIONG; + - USER: identifier, user: + SELECT user FROM t1; + KILL USER foo; + Note, we need here only tokens that cause shirt/reduce conflicts with keyword identifiers. For example: opt_clause1: %empty | KEYWORD ... ; @@ -1661,7 +1675,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); and until NEXT_SYM / PREVIOUS_SYM. */ %left PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE -%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM +%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM USER /* @@ -1747,7 +1761,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); optionally_qualified_column_ident %type <simple_string> - remember_name remember_end remember_tok_start + remember_name remember_end + remember_tok_start remember_tok_end wild_and_where %type <const_simple_string> @@ -1996,6 +2011,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); preload_list preload_list_or_parts preload_keys preload_keys_parts select_item_list select_item values_list no_braces delete_limit_clause fields opt_values values + no_braces_with_names opt_values_with_names values_with_names procedure_list procedure_list2 procedure_item field_def handler opt_generated_always opt_ignore opt_column opt_restrict @@ -2874,8 +2890,8 @@ create: { Lex->pop_select(); //main select } - | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list - opt_require_clause opt_resource_options + | create_or_replace USER_SYM opt_if_not_exists clear_privileges + grant_list opt_require_clause opt_resource_options { if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))) @@ -5938,7 +5954,7 @@ opt_versioning_rotation: | INTERVAL_SYM expr interval opt_versioning_interval_start { partition_info *part_info= Lex->part_info; - if (unlikely(part_info->vers_set_interval($2, $3, $4))) + if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4))) { my_error(ER_PART_WRONG_VALUE, MYF(0), Lex->create_last_non_select_table->table_name.str, @@ -9425,6 +9441,12 @@ remember_tok_start: } ; +remember_tok_end: + { + $$= (char*) YYLIP->get_tok_end(); + } + ; + remember_name: { $$= (char*) YYLIP->get_cpp_tok_start(); @@ -9713,14 +9735,14 @@ predicate: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr LIKE mysql_concatenation_expr opt_escape + | bit_expr LIKE bit_expr opt_escape { $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, Lex->escape_used); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not LIKE mysql_concatenation_expr opt_escape + | bit_expr not LIKE bit_expr opt_escape { Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, Lex->escape_used); @@ -12106,7 +12128,7 @@ opt_escape: Lex->escape_used= TRUE; $$= $2; } - | /* empty */ + | /* empty */ %prec PREC_BELOW_ESCAPE { Lex->escape_used= FALSE; $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? @@ -13156,7 +13178,7 @@ insert_values: values_list: values_list ',' no_braces - | no_braces + | no_braces_with_names ; ident_eq_list: @@ -13209,11 +13231,31 @@ no_braces: } ; +no_braces_with_names: + '(' + { + if (unlikely(!(Lex->insert_list= new (thd->mem_root) List_item))) + MYSQL_YYABORT; + } + opt_values_with_names ')' + { + LEX *lex=Lex; + if (unlikely(lex->many_values.push_back(lex->insert_list, + thd->mem_root))) + MYSQL_YYABORT; + } + ; + opt_values: /* empty */ {} | values ; +opt_values_with_names: + /* empty */ {} + | values_with_names + ; + values: values ',' expr_or_default { @@ -13227,6 +13269,25 @@ values: } ; +values_with_names: + values_with_names ',' remember_name expr_or_default remember_end + { + if (unlikely(Lex->insert_list->push_back($4, thd->mem_root))) + MYSQL_YYABORT; + // give some name in case of using in table value constuctor (TVC) + if (!$4->name.str || $4->name.str == item_empty_name) + $4->set_name(thd, $3, (uint) ($5 - $3), thd->charset()); + } + | remember_name expr_or_default remember_end + { + if (unlikely(Lex->insert_list->push_back($2, thd->mem_root))) + MYSQL_YYABORT; + // give some name in case of using in table value constuctor (TVC) + if (!$2->name.str || $2->name.str == item_empty_name) + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + } + ; + expr_or_default: expr { $$= $1;} | DEFAULT @@ -14257,9 +14318,18 @@ delete_domain_id_list: ; delete_domain_id: - ulong_num + ulonglong_num { - insert_dynamic(&Lex->delete_gtid_domain, (uchar*) &($1)); + uint32 value= (uint32) $1; + if ($1 > UINT_MAX32) + { + my_printf_error(ER_BINLOG_CANT_DELETE_GTID_DOMAIN, + "The value of gtid domain being deleted ('%llu') " + "exceeds its maximum size " + "of 32 bit unsigned integer", MYF(0), $1); + MYSQL_YYABORT; + } + insert_dynamic(&Lex->delete_gtid_domain, (uchar*) &value); } ; @@ -14439,6 +14509,7 @@ load: lex->field_list.empty(); lex->update_list.empty(); lex->value_list.empty(); + lex->many_values.empty(); } opt_load_data_charset { Lex->exchange->cs= $15; } @@ -14877,12 +14948,17 @@ with_list_element: MYSQL_YYABORT; Lex->with_column_list.empty(); } - AS '(' remember_name query_expression remember_end ')' + AS '(' remember_tok_start query_expression remember_tok_end ')' { + LEX *lex= thd->lex; + const char *query_start= lex->sphead ? lex->sphead->m_tmp_query + : thd->query(); + char *spec_start= $6 + 1; With_element *elem= new With_element($1, *$2, $7); if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) MYSQL_YYABORT; - if (unlikely(elem->set_unparsed_spec(thd, $6+1, $8))) + if (elem->set_unparsed_spec(thd, spec_start, $8, + spec_start - query_start)) MYSQL_YYABORT; } ; @@ -15790,7 +15866,7 @@ keyword_sp_var_and_label: | UNDOFILE_SYM | UNKNOWN_SYM | UNTIL_SYM - | USER_SYM + | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2 | USE_FRM | VARIABLES | VERSIONING_SYM @@ -16188,14 +16264,14 @@ opt_for_user: ; text_or_password: - TEXT_STRING { Lex->definer->pwhash= $1;} + TEXT_STRING { Lex->definer->auth= $1;} | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } | OLD_PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; - Lex->definer->pwhash.str= Item_func_password::alloc(thd, + Lex->definer->auth.str= Item_func_password::alloc(thd, $3.str, $3.length, Item_func_password::OLD); - Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + Lex->definer->auth.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } ; @@ -16753,13 +16829,11 @@ grant_user: { $$= $1; $1->pwtext= $4; - if (unlikely(Lex->sql_command == SQLCOM_REVOKE)) - MYSQL_YYABORT; } | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING { $$= $1; - $1->pwhash= $5; + $1->auth= $5; } | user IDENTIFIED_SYM via_or_with ident_or_text { @@ -16767,12 +16841,20 @@ grant_user: $1->plugin= $4; $1->auth= empty_clex_str; } - | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + TEXT_STRING_sys { $$= $1; $1->plugin= $4; $1->auth= $6; } + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + PASSWORD_SYM '(' TEXT_STRING ')' + { + $$= $1; + $1->plugin= $4; + $1->pwtext= $8; + } | user_or_role { $$= $1; } ; @@ -17424,12 +17506,15 @@ opt_migrate: ; install: - INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys + INSTALL_SYM PLUGIN_SYM opt_if_not_exists ident SONAME_SYM TEXT_STRING_sys { LEX *lex= Lex; + lex->create_info.init(); + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; lex->sql_command= SQLCOM_INSTALL_PLUGIN; - lex->comment= $3; - lex->ident= $5; + lex->comment= $4; + lex->ident= $6; } | INSTALL_SYM SONAME_SYM TEXT_STRING_sys { @@ -17441,18 +17526,24 @@ install: ; uninstall: - UNINSTALL_SYM PLUGIN_SYM ident + UNINSTALL_SYM PLUGIN_SYM opt_if_exists ident { LEX *lex= Lex; + lex->check_opt.init(); + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; - lex->comment= $3; + lex->comment= $4; } - | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys + | UNINSTALL_SYM SONAME_SYM opt_if_exists TEXT_STRING_sys { LEX *lex= Lex; + lex->check_opt.init(); + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; lex->comment= null_clex_str; - lex->ident= $3; + lex->ident= $4; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index ef12b9c0c53..5a6914c89c7 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -296,10 +296,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 59 shift/reduce conflicts. + Currently there are 53 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 59 +%expect 53 /* Comments for TOKENS. @@ -1061,6 +1061,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY + +/* + Give ESCAPE (in LIKE) a very low precedence. + This allows the concatenation operator || to be used on the right + side of "LIKE" with sql_mode=PIPES_AS_CONCAT (without ORACLE): + SELECT 'ab' LIKE 'a'||'b'||'c'; +*/ +%left PREC_BELOW_ESCAPE +%left ESCAPE_SYM + %left SET_VAR %left OR_SYM OR2_SYM %left XOR @@ -1070,7 +1080,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left NOT_SYM %left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE -%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE REGEXP IN_SYM +%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE SOUNDS_SYM REGEXP IN_SYM %left '|' %left '&' %left SHIFT_LEFT SHIFT_RIGHT @@ -1102,6 +1112,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); SELECT system FROM t1; ALTER TABLE DROP SYSTEM VERSIONIONG; + - USER: identifier, user: + SELECT user FROM t1; + KILL USER foo; + Note, we need here only tokens that cause shirt/reduce conflicts with keyword identifiers. For example: opt_clause1: %empty | KEYWORD ... ; @@ -1140,7 +1154,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); and until NEXT_SYM / PREVIOUS_SYM. */ %left PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE -%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM +%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM USER /* @@ -1230,7 +1244,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); optionally_qualified_column_ident %type <simple_string> - remember_name remember_end remember_end_opt remember_tok_start + remember_name remember_end remember_end_opt + remember_tok_start remember_tok_end wild_and_where colon_with_pos @@ -1487,6 +1502,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); assign_to_keycache_parts preload_list preload_list_or_parts preload_keys preload_keys_parts select_item_list select_item values_list no_braces + no_braces_with_names opt_values_with_names values_with_names delete_limit_clause fields opt_values values procedure_list procedure_list2 procedure_item field_def handler opt_generated_always @@ -2373,8 +2389,8 @@ create: { Lex->pop_select(); //main select } - | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list - opt_require_clause opt_resource_options + | create_or_replace USER_SYM opt_if_not_exists clear_privileges + grant_list opt_require_clause opt_resource_options { if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))) @@ -5964,7 +5980,7 @@ opt_versioning_rotation: | INTERVAL_SYM expr interval opt_versioning_interval_start { partition_info *part_info= Lex->part_info; - if (unlikely(part_info->vers_set_interval($2, $3, $4))) + if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4))) { my_error(ER_PART_WRONG_VALUE, MYF(0), Lex->create_last_non_select_table->table_name.str, @@ -9562,6 +9578,12 @@ remember_tok_start: } ; +remember_tok_end: + { + $$= (char*) YYLIP->get_tok_end(); + } + ; + remember_name: { $$= (char*) YYLIP->get_cpp_tok_start(); @@ -9859,14 +9881,14 @@ predicate: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr LIKE mysql_concatenation_expr opt_escape + | bit_expr LIKE bit_expr opt_escape { $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, Lex->escape_used); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not LIKE mysql_concatenation_expr opt_escape + | bit_expr not LIKE bit_expr opt_escape { Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, Lex->escape_used); @@ -12318,7 +12340,7 @@ opt_escape: Lex->escape_used= TRUE; $$= $2; } - | /* empty */ + | /* empty */ %prec PREC_BELOW_ESCAPE { Lex->escape_used= FALSE; $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? @@ -13384,7 +13406,7 @@ insert_values: values_list: values_list ',' no_braces - | no_braces + | no_braces_with_names ; ident_eq_list: @@ -13437,11 +13459,31 @@ no_braces: } ; +no_braces_with_names: + '(' + { + if (unlikely(!(Lex->insert_list= new (thd->mem_root) List_item))) + MYSQL_YYABORT; + } + opt_values_with_names ')' + { + LEX *lex=Lex; + if (unlikely(lex->many_values.push_back(lex->insert_list, + thd->mem_root))) + MYSQL_YYABORT; + } + ; + opt_values: /* empty */ {} | values ; +opt_values_with_names: + /* empty */ {} + | values_with_names + ; + values: values ',' expr_or_default { @@ -13455,6 +13497,25 @@ values: } ; +values_with_names: + values_with_names ',' remember_name expr_or_default remember_end + { + if (unlikely(Lex->insert_list->push_back($4, thd->mem_root))) + MYSQL_YYABORT; + // give some name in case of using in table value constuctor (TVC) + if (!$4->name.str || $4->name.str == item_empty_name) + $4->set_name(thd, $3, (uint) ($5 - $3), thd->charset()); + } + | remember_name expr_or_default remember_end + { + if (unlikely(Lex->insert_list->push_back($2, thd->mem_root))) + MYSQL_YYABORT; + // give some name in case of using in table value constuctor (TVC) + if (!$2->name.str || $2->name.str == item_empty_name) + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + } + ; + expr_or_default: expr { $$= $1;} | DEFAULT @@ -14493,9 +14554,18 @@ delete_domain_id_list: ; delete_domain_id: - ulong_num + ulonglong_num { - insert_dynamic(&Lex->delete_gtid_domain, (uchar*) &($1)); + uint32 value= (uint32) $1; + if ($1 > UINT_MAX32) + { + my_printf_error(ER_BINLOG_CANT_DELETE_GTID_DOMAIN, + "The value of gtid domain being deleted ('%llu') " + "exceeds its maximum size " + "of 32 bit unsigned integer", MYF(0), $1); + MYSQL_YYABORT; + } + insert_dynamic(&Lex->delete_gtid_domain, (uchar*) &value); } ; @@ -14672,6 +14742,7 @@ load: lex->field_list.empty(); lex->update_list.empty(); lex->value_list.empty(); + lex->many_values.empty(); } opt_load_data_charset { Lex->exchange->cs= $15; } @@ -15122,12 +15193,17 @@ with_list_element: MYSQL_YYABORT; Lex->with_column_list.empty(); } - AS '(' remember_name query_expression remember_end ')' + AS '(' remember_tok_start query_expression remember_tok_end ')' { + LEX *lex= thd->lex; + const char *query_start= lex->sphead ? lex->sphead->m_tmp_query + : thd->query(); + char *spec_start= $6 + 1; With_element *elem= new With_element($1, *$2, $7); if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) MYSQL_YYABORT; - if (unlikely(elem->set_unparsed_spec(thd, $6+1, $8))) + if (elem->set_unparsed_spec(thd, spec_start, $8, + spec_start - query_start)) MYSQL_YYABORT; } ; @@ -16069,7 +16145,7 @@ keyword_sp_var_and_label: | UNDOFILE_SYM | UNKNOWN_SYM | UNTIL_SYM - | USER_SYM + | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2 | USE_FRM | VARIABLES | VIEW_SYM @@ -16515,14 +16591,14 @@ opt_for_user: ; text_or_password: - TEXT_STRING { Lex->definer->pwhash= $1;} + TEXT_STRING { Lex->definer->auth= $1;} | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } | OLD_PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; - Lex->definer->pwhash.str= Item_func_password::alloc(thd, + Lex->definer->auth.str= Item_func_password::alloc(thd, $3.str, $3.length, Item_func_password::OLD); - Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + Lex->definer->auth.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } ; @@ -17106,13 +17182,11 @@ grant_user: { $$= $1; $1->pwtext= $4; - if (unlikely(Lex->sql_command == SQLCOM_REVOKE)) - MYSQL_YYABORT; } | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING { $$= $1; - $1->pwhash= $5; + $1->auth= $5; } | user IDENTIFIED_SYM via_or_with ident_or_text { @@ -17120,12 +17194,20 @@ grant_user: $1->plugin= $4; $1->auth= empty_clex_str; } - | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + TEXT_STRING_sys { $$= $1; $1->plugin= $4; $1->auth= $6; } + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as + PASSWORD_SYM '(' TEXT_STRING ')' + { + $$= $1; + $1->plugin= $4; + $1->pwtext= $8; + } | user_or_role { $$= $1; } ; diff --git a/sql/structs.h b/sql/structs.h index 355d6e75e48..9ff52bccb40 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -227,12 +227,11 @@ struct AUTHID struct LEX_USER: public AUTHID { - LEX_CSTRING plugin, auth; - LEX_CSTRING pwtext, pwhash; + LEX_CSTRING plugin, auth, pwtext; void reset_auth() { - pwtext.length= pwhash.length= plugin.length= auth.length= 0; - pwtext.str= pwhash.str= 0; + pwtext.length= plugin.length= auth.length= 0; + pwtext.str= 0; plugin.str= auth.str= ""; } }; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index ba9b2c814ce..983d0490f2e 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -471,6 +471,10 @@ bool check_has_super(sys_var *self, THD *thd, set_var *var) return false; } +static Sys_var_bit Sys_core_file("core_file", "write a core-file on crashes", + READ_ONLY GLOBAL_VAR(test_flags), NO_CMD_LINE, + TEST_CORE_ON_SIGNAL, DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, + 0,0,0); static bool binlog_format_check(sys_var *self, THD *thd, set_var *var) { @@ -2669,6 +2673,16 @@ static Sys_var_ulong Sys_div_precincrement( SESSION_VAR(div_precincrement), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, DECIMAL_MAX_SCALE), DEFAULT(4), BLOCK_SIZE(1)); +static Sys_var_uint Sys_eq_range_index_dive_limit( + "eq_range_index_dive_limit", + "The optimizer will use existing index statistics instead of " + "doing index dives for equality ranges if the number of equality " + "ranges for the index is larger than or equal to this number. " + "If set to 0, index dives are always used.", + SESSION_VAR(eq_range_index_dive_limit), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, UINT_MAX32), DEFAULT(0), + BLOCK_SIZE(1)); + static Sys_var_ulong Sys_range_alloc_block_size( "range_alloc_block_size", "Allocation block size for storing ranges during optimization", @@ -2708,17 +2722,6 @@ static Sys_var_ulong Sys_query_prealloc_size( BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_thd_mem_root)); -#ifdef HAVE_SMEM -static Sys_var_mybool Sys_shared_memory( - "shared_memory", "Enable the shared memory", - READ_ONLY GLOBAL_VAR(opt_enable_shared_memory), CMD_LINE(OPT_ARG), - DEFAULT(FALSE)); - -static Sys_var_charptr Sys_shared_memory_base_name( - "shared_memory_base_name", "Base name of shared memory", - READ_ONLY GLOBAL_VAR(shared_memory_base_name), CMD_LINE(REQUIRED_ARG), - IN_FS_CHARSET, DEFAULT(0)); -#endif // this has to be NO_CMD_LINE as the command-line option has a different name static Sys_var_mybool Sys_skip_external_locking( @@ -4114,7 +4117,7 @@ static Sys_var_bit Sys_safe_updates( "sql_safe_updates", "If set to 1, UPDATEs and DELETEs need either a key in " "the WHERE clause, or a LIMIT clause, or else they will aborted. Prevents " "the common mistake of accidentally deleting or updating every row in a table.", - SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_SAFE_UPDATES, + SESSION_VAR(option_bits), CMD_LINE(OPT_ARG), OPTION_SAFE_UPDATES, DEFAULT(FALSE)); static Sys_var_bit Sys_buffer_results( diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 373df354268..dbc3565e202 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -2659,7 +2659,7 @@ public: if (!Sys_var_enum::do_check(thd, var)) return false; MYSQL_TIME ltime; - bool res= var->value->get_date(<ime, 0); + bool res= var->value->get_date(thd, <ime, date_mode_t(0)); if (!res) { var->save_result.ulonglong_value= SYSTEM_TIME_AS_OF; @@ -2676,7 +2676,7 @@ private: { if (var->value) { - res= var->value->get_date(&out.ltime, 0); + res= var->value->get_date(current_thd, &out.ltime, date_mode_t(0)); } else // set DEFAULT from global var { diff --git a/sql/table.cc b/sql/table.cc index 2031d27b549..18be06c0b95 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -44,6 +44,7 @@ #include "sql_cte.h" #include "ha_sequence.h" #include "sql_show.h" +#include <atomic> /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -79,7 +80,7 @@ LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")}; */ static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; -static int64 last_table_id; +static std::atomic<ulong> last_table_id; /* Functions defined in this file */ @@ -324,7 +325,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, share->can_do_row_logging= 1; if (share->table_category == TABLE_CATEGORY_LOG) share->no_replicate= 1; - if (my_strnncoll(table_alias_charset, (uchar*) db, 6, + if (key_length > 6 && + my_strnncoll(table_alias_charset, (const uchar*) key, 6, (const uchar*) "mysql", 6) == 0) share->not_usable_by_query_cache= 1; @@ -343,8 +345,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, */ do { - share->table_map_id=(ulong) my_atomic_add64_explicit(&last_table_id, 1, - MY_MEMORY_ORDER_RELAXED); + share->table_map_id= + last_table_id.fetch_add(1, std::memory_order_relaxed); } while (unlikely(share->table_map_id == ~0UL)); } DBUG_RETURN(share); @@ -431,6 +433,7 @@ void TABLE_SHARE::destroy() ha_share= NULL; // Safety } + delete_stat_values_for_table_share(this); delete sequence; free_root(&stats_cb.mem_root, MYF(0)); stats_cb.stats_can_be_read= FALSE; @@ -2183,7 +2186,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, keyinfo= share->key_info; uint primary_key= my_strcasecmp(system_charset_info, share->keynames.type_names[0], primary_key_name) ? MAX_KEY : 0; - KEY* key_first_info; + KEY* key_first_info= NULL; if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME) { @@ -2471,7 +2474,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (!(key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART | HA_BIT_PART)) && key_part->type != HA_KEYTYPE_FLOAT && - key_part->type == HA_KEYTYPE_DOUBLE) + key_part->type != HA_KEYTYPE_DOUBLE) key_part->key_part_flag|= HA_CAN_MEMCMP; } keyinfo->usable_key_parts= usable_parts; // Filesort @@ -3162,10 +3165,12 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, { enum open_frm_error error; uint records, i, bitmap_size, bitmap_count; + const char *tmp_alias; bool error_reported= FALSE; uchar *record, *bitmaps; Field **field_ptr; uint8 save_context_analysis_only= thd->lex->context_analysis_only; + TABLE_SHARE::enum_v_keys check_set_initialized= share->check_set_initialized; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: %p", share->db.str, share->table_name.str, outparam)); @@ -3189,8 +3194,14 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, init_sql_alloc(&outparam->mem_root, "table", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); - if (outparam->alias.copy(alias->str, alias->length, table_alias_charset)) + /* + We have to store the original alias in mem_root as constraints and virtual + functions may store pointers to it + */ + if (!(tmp_alias= strmake_root(&outparam->mem_root, alias->str, alias->length))) goto err; + + outparam->alias.set(tmp_alias, alias->length, table_alias_charset); outparam->quick_keys.init(); outparam->covering_keys.init(); outparam->intersect_keys.init(); @@ -3279,6 +3290,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } (*field_ptr)= 0; // End marker + DEBUG_SYNC(thd, "TABLE_after_field_clone"); + outparam->vers_write= share->versioned; if (share->found_next_number_field) @@ -3527,6 +3540,16 @@ partititon_err: } outparam->mark_columns_used_by_virtual_fields(); + if (!check_set_initialized && + share->check_set_initialized == TABLE_SHARE::V_KEYS) + { + // copy PART_INDIRECT_KEY_FLAG that was set meanwhile by *some* thread + for (uint i= 0 ; i < share->fields ; i++) + { + if (share->field[i]->flags & PART_INDIRECT_KEY_FLAG) + outparam->field[i]->flags|= PART_INDIRECT_KEY_FLAG; + } + } if (db_stat) { @@ -4614,7 +4637,7 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) s->table_name.str, tl->alias.str); /* Fix alias if table name changes. */ - if (strcmp(alias.c_ptr(), tl->alias.str)) + if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias.str)) alias.copy(tl->alias.str, tl->alias.length, alias.charset()); tablenr= thd->current_tablenr++; @@ -6209,7 +6232,7 @@ Field_iterator_table_ref::get_or_create_column_ref(THD *thd, TABLE_LIST *parent_ nj_col= natural_join_it.column_ref(); DBUG_ASSERT(nj_col); } - DBUG_ASSERT(!nj_col->table_field || + DBUG_ASSERT(!nj_col->table_field || !nj_col->table_field->field || nj_col->table_ref->table == nj_col->table_field->field->table); /* @@ -6258,7 +6281,7 @@ Field_iterator_table_ref::get_or_create_column_ref(THD *thd, TABLE_LIST *parent_ RETURN # Pointer to a column of a natural join (or its operand) - NULL No memory to allocate the column + NULL We didn't originally have memory to allocate the column */ Natural_join_column * @@ -6274,7 +6297,7 @@ Field_iterator_table_ref::get_natural_column_ref() */ nj_col= natural_join_it.column_ref(); DBUG_ASSERT(nj_col && - (!nj_col->table_field || + (!nj_col->table_field || !nj_col->table_field->field || nj_col->table_ref->table == nj_col->table_field->field->table)); return nj_col; } @@ -6848,6 +6871,7 @@ void TABLE::mark_columns_used_by_virtual_fields(void) { MY_BITMAP *save_read_set; Field **vfield_ptr; + TABLE_SHARE::enum_v_keys v_keys= TABLE_SHARE::NO_V_KEYS; /* If there is virtual fields are already initialized */ if (s->check_set_initialized) @@ -6886,11 +6910,14 @@ void TABLE::mark_columns_used_by_virtual_fields(void) for (uint i= 0 ; i < s->fields ; i++) { if (bitmap_is_set(&tmp_set, i)) - field[i]->flags|= PART_INDIRECT_KEY_FLAG; + { + s->field[i]->flags|= PART_INDIRECT_KEY_FLAG; + v_keys= TABLE_SHARE::V_KEYS; + } } bitmap_clear_all(&tmp_set); } - s->check_set_initialized= 1; + s->check_set_initialized= v_keys; if (s->tmp_table == NO_TMP_TABLE) mysql_mutex_unlock(&s->LOCK_share); } @@ -7098,6 +7125,14 @@ void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info, The function checks whether a possible key satisfies the constraints imposed on the keys of any temporary table. + We need to filter out BLOB columns here, because ref access optimizer creates + KEYUSE objects for equalities for non-key columns for two puproses: + 1. To discover possible keys for derived_with_keys optimization + 2. To do hash joins + For the purpose of #1, KEYUSE objects are not created for "blob_column=..." . + However, they might be created for #2. In order to catch that case, we filter + them out here. + @return TRUE if the key is valid @return FALSE otherwise */ @@ -7113,11 +7148,12 @@ bool TABLE::check_tmp_key(uint key, uint key_parts, { uint fld_idx= next_field_no(arg); reg_field= field + fld_idx; + if ((*reg_field)->type() == MYSQL_TYPE_BLOB) + return FALSE; uint fld_store_len= (uint16) (*reg_field)->key_length(); if ((*reg_field)->real_maybe_null()) fld_store_len+= HA_KEY_NULL_LENGTH; - if ((*reg_field)->type() == MYSQL_TYPE_BLOB || - (*reg_field)->real_type() == MYSQL_TYPE_VARCHAR || + if ((*reg_field)->real_type() == MYSQL_TYPE_VARCHAR || (*reg_field)->type() == MYSQL_TYPE_GEOMETRY) fld_store_len+= HA_KEY_BLOB_LENGTH; key_len+= fld_store_len; @@ -8327,7 +8363,7 @@ int TABLE_LIST::fetch_number_of_rows() } if (is_materialized_derived() && !fill_me) { - table->file->stats.records= ((select_unit*)(get_unit()->result))->records; + table->file->stats.records= get_unit()->result->est_records; set_if_bigger(table->file->stats.records, 2); table->used_stat_records= table->file->stats.records; } @@ -8550,7 +8586,8 @@ bool TR_table::update(ulonglong start_id, ulonglong end_id) return true; store(FLD_BEGIN_TS, thd->transaction_time()); - timeval end_time= {thd->query_start(), long(thd->query_start_sec_part())}; + thd->set_time(); + timeval end_time= {thd->query_start(), int(thd->query_start_sec_part())}; store(FLD_TRX_ID, start_id); store(FLD_COMMIT_ID, end_id); store(FLD_COMMIT_TS, end_time); @@ -8631,7 +8668,7 @@ bool TR_table::query(MYSQL_TIME &commit_time, bool backwards) if (res > 0) { MYSQL_TIME commit_ts; - if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, 0)) + if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, date_mode_t(0))) { found= false; break; diff --git a/sql/table.h b/sql/table.h index 14bc928bcb5..3c782c34bc3 100644 --- a/sql/table.h +++ b/sql/table.h @@ -721,6 +721,9 @@ struct TABLE_SHARE uint column_bitmap_size; uchar frm_version; + enum enum_v_keys { NOT_INITIALIZED=0, NO_V_KEYS, V_KEYS }; + enum_v_keys check_set_initialized; + bool use_ext_keys; /* Extended keys can be used */ bool null_field_first; bool system; /* Set if system table (one record) */ @@ -732,7 +735,6 @@ struct TABLE_SHARE bool table_creation_was_logged; bool non_determinstic_insert; bool vcols_need_refixing; - bool check_set_initialized; bool has_update_default_function; bool can_do_row_logging; /* 1 if table supports RBR */ @@ -1492,6 +1494,7 @@ public: bool is_splittable() { return spl_opt_info != NULL; } void set_spl_opt_info(SplM_opt_info *spl_info); void deny_splitting(); + double get_materialization_cost(); // Now used only if is_splittable()==true void add_splitting_info_for_key_field(struct KEY_FIELD *key_field); @@ -1944,6 +1947,14 @@ struct TABLE_LIST const LEX_CSTRING *alias_arg, enum thr_lock_type lock_type_arg) { + enum enum_mdl_type mdl_type; + if (lock_type_arg >= TL_WRITE_ALLOW_WRITE) + mdl_type= MDL_SHARED_WRITE; + else if (lock_type_arg == TL_READ_NO_INSERT) + mdl_type= MDL_SHARED_NO_WRITE; + else + mdl_type= MDL_SHARED_READ; + bzero((char*) this, sizeof(*this)); DBUG_ASSERT(!db_arg->str || strlen(db_arg->str) == db_arg->length); DBUG_ASSERT(!table_name_arg->str || strlen(table_name_arg->str) == table_name_arg->length); @@ -1952,9 +1963,7 @@ struct TABLE_LIST table_name= *table_name_arg; alias= (alias_arg ? *alias_arg : *table_name_arg); lock_type= lock_type_arg; - mdl_request.init(MDL_key::TABLE, db.str, table_name.str, - (lock_type >= TL_WRITE_ALLOW_WRITE) ? - MDL_SHARED_WRITE : MDL_SHARED_READ, + mdl_request.init(MDL_key::TABLE, db.str, table_name.str, mdl_type, MDL_TRANSACTION); } @@ -1990,6 +1999,7 @@ struct TABLE_LIST *last_ptr= &next_global; } + /* List of tables local to a subquery (used by SQL_I_List). Considers views as leaves (unlike 'next_leaf' below). Created at parse time @@ -2602,6 +2612,16 @@ struct TABLE_LIST } void set_lock_type(THD* thd, enum thr_lock_type lock); + void remove_join_columns() + { + if (join_columns) + { + join_columns->empty(); + join_columns= NULL; + is_join_columns_complete= FALSE; + } + } + private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 2c5d25c0308..682f9455d26 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -463,6 +463,7 @@ void tc_release_table(TABLE *table) DBUG_ENTER("tc_release_table"); DBUG_ASSERT(table->in_use); DBUG_ASSERT(table->file); + DBUG_ASSERT(!table->pos_in_locked_tables); mysql_mutex_lock(&tc[i].LOCK_table_cache); if (table->needs_reopen() || table->s->tdc->flushed || @@ -644,7 +645,7 @@ bool tdc_init(void) void tdc_start_shutdown(void) { - DBUG_ENTER("table_def_start_shutdown"); + DBUG_ENTER("tdc_start_shutdown"); if (tdc_inited) { /* @@ -1140,11 +1141,10 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, All_share_tables_list::Iterator it(element->all_tables); while ((table= it++)) { - my_refs++; - DBUG_ASSERT(table->in_use == thd); + if (table->in_use == thd) + my_refs++; } } - DBUG_ASSERT(element->all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL); mysql_mutex_unlock(&element->LOCK_table_share); while ((table= purge_tables.pop_front())) @@ -1176,6 +1176,17 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, mysql_mutex_lock(&element->LOCK_table_share); while (element->ref_count > my_refs) mysql_cond_wait(&element->COND_release, &element->LOCK_table_share); + DBUG_ASSERT(element->all_tables.is_empty() || + remove_type != TDC_RT_REMOVE_ALL); +#ifndef DBUG_OFF + if (remove_type == TDC_RT_REMOVE_NOT_OWN || + remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE) + { + All_share_tables_list::Iterator it(element->all_tables); + while ((table= it++)) + DBUG_ASSERT(table->in_use == thd); + } +#endif mysql_mutex_unlock(&element->LOCK_table_share); } diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index f23ec7a1acc..1c8af5eaf66 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -1540,3 +1540,33 @@ void THD::unlock_temporary_tables() DBUG_VOID_RETURN; } + +/** + Close unused TABLE instances for given temporary table. + + @param tl [IN] TABLE_LIST + + Initial use case was TRUNCATE, which expects only one instance (which is used + by TRUNCATE itself) to be open. Most probably some ALTER TABLE variants and + REPAIR may have similar expectations. +*/ + +void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl) +{ + TMP_TABLE_SHARE *share= find_tmp_table_share(tl); + + if (share) + { + All_share_tables_list::Iterator tables_it(share->all_tmp_tables); + + while (TABLE *table= tables_it++) + { + if (table->query_id == 0) + { + /* Note: removing current list element doesn't invalidate iterator. */ + share->all_tmp_tables.remove(table); + free_temporary_table(table); + } + } + } +} diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 0cc683c631d..67a8e783208 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -70,12 +70,16 @@ static DWORD fls; static bool skip_completion_port_on_success = false; +PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ() +{ + return pool? &callback_environ: 0; +} + /* Threadpool callbacks. io_completion_callback - handle client request timer_callback - handle wait timeout (kill connection) - shm_read_callback, shm_close_callback - shared memory stuff login_callback - user login (submitted as threadpool work) */ @@ -89,9 +93,6 @@ static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance, static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work); -static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, - PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result); - static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance); /* Get current time as Windows time */ @@ -120,7 +121,6 @@ public: PTP_CALLBACK_INSTANCE callback_instance; PTP_IO io; PTP_TIMER timer; - PTP_WAIT shm_read; PTP_WORK work; bool long_callback; @@ -139,7 +139,15 @@ struct TP_connection *new_TP_connection(CONNECT *connect) void TP_pool_win::add(TP_connection *c) { - SubmitThreadpoolWork(((TP_connection_win *)c)->work); + if(FlsGetValue(fls)) + { + /* Inside threadpool(), execute callback directly. */ + tp_callback(c); + } + else + { + SubmitThreadpoolWork(((TP_connection_win *)c)->work); + } } @@ -149,7 +157,6 @@ TP_connection_win::TP_connection_win(CONNECT *c) : callback_instance(0), io(0), timer(0), - shm_read(0), work(0) { } @@ -170,30 +177,20 @@ int TP_connection_win::init() case VIO_TYPE_NAMEDPIPE: handle= (HANDLE)vio->hPipe; break; - case VIO_TYPE_SHARED_MEMORY: - handle= vio->event_server_wrote; - break; default: abort(); } - if (vio_type == VIO_TYPE_SHARED_MEMORY) - { - CHECK_ALLOC_ERROR(shm_read= CreateThreadpoolWait(shm_read_callback, this, &callback_environ)); - } - else + + /* Performance tweaks (s. MSDN documentation)*/ + UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE; + if (skip_completion_port_on_success) { - /* Performance tweaks (s. MSDN documentation)*/ - UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE; - if (skip_completion_port_on_success) - { - flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; - } - (void)SetFileCompletionNotificationModes(handle, flags); - /* Assign io completion callback */ - CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ)); + flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; } - + (void)SetFileCompletionNotificationModes(handle, flags); + /* Assign io completion callback */ + CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ)); CHECK_ALLOC_ERROR(timer= CreateThreadpoolTimer(timer_callback, this, &callback_environ)); CHECK_ALLOC_ERROR(work= CreateThreadpoolWork(work_callback, this, &callback_environ)); return 0; @@ -214,11 +211,6 @@ int TP_connection_win::start_io() DWORD last_error= 0; int retval; - if (shm_read) - { - SetThreadpoolWait(shm_read, handle, NULL); - return 0; - } StartThreadpoolIo(io); if (vio_type == VIO_TYPE_TCPIP || vio_type == VIO_TYPE_SSL) @@ -297,9 +289,6 @@ TP_connection_win::~TP_connection_win() if (io) CloseThreadpoolIo(io); - if (shm_read) - CloseThreadpoolWait(shm_read); - if (work) CloseThreadpoolWork(work); @@ -312,14 +301,13 @@ TP_connection_win::~TP_connection_win() void TP_connection_win::wait_begin(int type) { - /* Signal to the threadpool whenever callback can run long. Currently, binlog waits are a good candidate, its waits are really long */ if (type == THD_WAIT_BINLOG) { - if (!long_callback) + if (!long_callback && callback_instance) { CallbackMayRunLong(callback_instance); long_callback= true; @@ -332,12 +320,11 @@ void TP_connection_win::wait_end() /* Do we need to do anything ? */ } -/* - This function should be called first whenever a callback is invoked in the +/* + This function should be called first whenever a callback is invoked in the threadpool, does my_thread_init() if not yet done */ -extern ulong thread_created; -static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance) +void tp_win_callback_prolog() { if (FlsGetValue(fls) == NULL) { @@ -347,6 +334,12 @@ static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance) InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads); my_thread_init(); } +} + +extern ulong thread_created; +static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance) +{ + tp_win_callback_prolog(); TP_connection_win *c = (TP_connection_win *)context; c->callback_instance = instance; c->long_callback = false; @@ -420,29 +413,6 @@ static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance, } } - -/* - Shared memory read callback. - Invoked when read event is set on connection. -*/ - -static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance, - PVOID context, PTP_WAIT wait,TP_WAIT_RESULT wait_result) -{ - TP_connection_win *c= (TP_connection_win *)context; - /* Disarm wait. */ - SetThreadpoolWait(wait, NULL, NULL); - - /* - This is an autoreset event, and one wakeup is eaten already by threadpool, - and the current state is "not set". Thus we need to reset the event again, - or vio_read will hang. - */ - SetEvent(c->handle); - tp_callback(instance, context); -} - - static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work) { tp_callback(instance, context); diff --git a/sql/tztime.cc b/sql/tztime.cc index 61fbee1731a..177521e5a55 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1653,7 +1653,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) { /* If we are in bootstrap mode we should not load time zone tables */ return_val= time_zone_tables_exist= 0; - goto end_with_setting_default_tz; + goto end_with_cleanup; } /* diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index f2d90def5ef..1f50ee55711 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -146,6 +146,7 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd, /* Use the original server id for logging. */ thd->set_server_id(ev->server_id); thd->set_time(); // time the query + thd->transaction.start_time.reset(thd); wsrep_xid_init(&thd->transaction.xid_state.xid, thd->wsrep_trx_meta.gtid.uuid, thd->wsrep_trx_meta.gtid.seqno); diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc index cafd41b2653..0cbcdcd64aa 100644 --- a/sql/wsrep_binlog.cc +++ b/sql/wsrep_binlog.cc @@ -322,13 +322,28 @@ int wsrep_write_cache(wsrep_t* const wsrep, void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len) { - char filename[PATH_MAX]= {0}; - int len= snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld.log", + int len= snprintf(NULL, 0, "%s/GRA_%lld_%lld.log", wsrep_data_home_dir, (longlong) thd->thread_id, (longlong) wsrep_thd_trx_seqno(thd)); - if (len >= PATH_MAX) + if (len < 0) { - WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len); + WSREP_ERROR("snprintf error: %d, skipping dump.", len); + return; + } + /* + len doesn't count the \0 end-of-string. Use len+1 below + to alloc and pass as an argument to snprintf. + */ + + char *filename= (char *)malloc(len+1); + int len1= snprintf(filename, len+1, "%s/GRA_%lld_%lld.log", + wsrep_data_home_dir, (longlong) thd->thread_id, + (long long)wsrep_thd_trx_seqno(thd)); + + if (len > len1) + { + WSREP_ERROR("RBR dump path truncated: %d, skipping dump.", len); + free(filename); return; } @@ -347,6 +362,7 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len) WSREP_ERROR("Failed to open file '%s': %d (%s)", filename, errno, strerror(errno)); } + free(filename); } /* @@ -452,19 +468,34 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf, { DBUG_ENTER("wsrep_dump_rbr_buf_with_header"); - char filename[PATH_MAX]= {0}; File file; IO_CACHE cache; Log_event_writer writer(&cache, 0); Format_description_log_event *ev= 0; - int len= my_snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld_v2.log", - wsrep_data_home_dir, (longlong) thd->thread_id, - (long long) wsrep_thd_trx_seqno(thd)); + longlong thd_trx_seqno= (long long)wsrep_thd_trx_seqno(thd); + int len= snprintf(NULL, 0, "%s/GRA_%lld_%lld_v2.log", + wsrep_data_home_dir, (longlong)thd->thread_id, + thd_trx_seqno); + /* + len doesn't count the \0 end-of-string. Use len+1 below + to alloc and pass as an argument to snprintf. + */ + char *filename; + if (len < 0 || !(filename= (char*)malloc(len+1))) + { + WSREP_ERROR("snprintf error: %d, skipping dump.", len); + DBUG_VOID_RETURN; + } + + int len1= snprintf(filename, len+1, "%s/GRA_%lld_%lld_v2.log", + wsrep_data_home_dir, (longlong) thd->thread_id, + thd_trx_seqno); - if (len >= PATH_MAX) + if (len > len1) { - WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len); + WSREP_ERROR("RBR dump path truncated: %d, skipping dump.", len); + free(filename); DBUG_VOID_RETURN; } @@ -505,6 +536,7 @@ cleanup2: end_io_cache(&cache); cleanup1: + free(filename); mysql_file_close(file, MYF(MY_WME)); if (!thd->wsrep_applier) delete ev; diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index 0b6e7e0d5bb..9a4bbd01bcd 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -139,3 +139,6 @@ int wsrep_trx_is_aborting(THD *) void wsrep_unlock_rollback() { } + +void wsrep_set_data_home_dir(const char *) +{ } diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index d4d35d752be..8110faf7d11 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -25,6 +25,8 @@ #include <cstdlib> #include "debug_sync.h" +extern handlerton *binlog_hton; +extern int binlog_close_connection(handlerton *hton, THD *thd); extern ulonglong thd_to_trx_id(THD *thd); extern "C" int thd_binlog_format(const MYSQL_THD thd); @@ -173,7 +175,10 @@ wsrep_close_connection(handlerton* hton, THD* thd) { DBUG_RETURN(0); } - DBUG_RETURN(wsrep_binlog_close_connection (thd)); + + if (wsrep_emulate_bin_log && thd_get_ha_data(thd, binlog_hton) != NULL) + binlog_hton->close_connection (binlog_hton, thd); + DBUG_RETURN(0); } /* @@ -264,7 +269,7 @@ static int wsrep_rollback(handlerton *hton, THD *thd, bool all) } if ((all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && - (thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY)) + thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY) { if (wsrep && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle)) { diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 0a2735fe0b7..bc52bff0de9 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -73,10 +73,7 @@ const char *wsrep_start_position; const char *wsrep_data_home_dir; const char *wsrep_dbug_option; const char *wsrep_notify_cmd; -const char *wsrep_sst_method; -const char *wsrep_sst_receive_address; -const char *wsrep_sst_donor; -const char *wsrep_sst_auth; + my_bool wsrep_debug; // Enable debug level logging my_bool wsrep_convert_LOCK_to_trx; // Convert locking sessions to trx my_bool wsrep_auto_increment_control; // Control auto increment variables @@ -88,7 +85,6 @@ my_bool wsrep_log_conflicts; my_bool wsrep_load_data_splitting; // Commit load data every 10K intervals my_bool wsrep_slave_UK_checks; // Slave thread does UK checks my_bool wsrep_slave_FK_checks; // Slave thread does FK checks -my_bool wsrep_sst_donor_rejects_queries; my_bool wsrep_restart_slave; // Should mysql slave thread be // restarted, when node joins back? my_bool wsrep_desync; // De(re)synchronize the node from the @@ -653,6 +649,9 @@ int wsrep_init() wsrep->provider_vendor, sizeof(provider_vendor) - 1); } + if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0) + wsrep_data_home_dir = mysql_real_data_home; + /* Initialize node address */ char node_addr[512]= { 0, }; size_t const node_addr_max= sizeof(node_addr) - 1; @@ -1078,114 +1077,151 @@ static bool wsrep_prepare_key_for_isolation(const char* db, wsrep_buf_t* key, size_t* key_len) { - if (*key_len < 2) return false; + if (*key_len < 2) return false; - switch (wsrep_protocol_version) - { - case 0: - *key_len= 0; - break; - case 1: - case 2: - case 3: + switch (wsrep_protocol_version) + { + case 0: + *key_len= 0; + break; + case 1: + case 2: + case 3: + { + *key_len= 0; + if (db) { - *key_len= 0; - if (db) - { - // sql_print_information("%s.%s", db, table); - if (db) - { - key[*key_len].ptr= db; - key[*key_len].len= strlen(db); - ++(*key_len); - if (table) - { - key[*key_len].ptr= table; - key[*key_len].len= strlen(table); - ++(*key_len); - } - } - } - break; + // sql_print_information("%s.%s", db, table); + key[*key_len].ptr= db; + key[*key_len].len= strlen(db); + ++(*key_len); + if (table) + { + key[*key_len].ptr= table; + key[*key_len].len= strlen(table); + ++(*key_len); + } } - default: + break; + } + default: + return false; + } + return true; +} + + +static bool wsrep_prepare_key_for_isolation(const char* db, + const char* table, + wsrep_key_arr_t* ka) +{ + wsrep_key_t* tmp; + + if (!ka->keys) + tmp= (wsrep_key_t*)my_malloc((ka->keys_len + 1) * sizeof(wsrep_key_t), + MYF(0)); + else + tmp= (wsrep_key_t*)my_realloc(ka->keys, + (ka->keys_len + 1) * sizeof(wsrep_key_t), + MYF(0)); + + if (!tmp) + { + WSREP_ERROR("Can't allocate memory for key_array"); + return false; + } + ka->keys= tmp; + if (!(ka->keys[ka->keys_len].key_parts= (wsrep_buf_t*) + my_malloc(sizeof(wsrep_buf_t)*2, MYF(0)))) + { + WSREP_ERROR("Can't allocate memory for key_parts"); + return false; + } + ka->keys[ka->keys_len].key_parts_num= 2; + ++ka->keys_len; + if (!wsrep_prepare_key_for_isolation(db, table, + (wsrep_buf_t*)ka->keys[ka->keys_len - 1].key_parts, + &ka->keys[ka->keys_len - 1].key_parts_num)) + { + WSREP_ERROR("Preparing keys for isolation failed"); + return false; + } + + return true; +} + + +static bool wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, + Alter_info* alter_info, + wsrep_key_arr_t* ka) +{ + Key *key; + List_iterator<Key> key_iterator(alter_info->key_list); + while ((key= key_iterator++)) + { + if (key->type == Key::FOREIGN_KEY) + { + Foreign_key *fk_key= (Foreign_key *)key; + const char *db_name= fk_key->ref_db.str; + const char *table_name= fk_key->ref_table.str; + if (!db_name) + { + db_name= child_table_db; + } + if (!wsrep_prepare_key_for_isolation(db_name, table_name, ka)) + { return false; + } } - - return true; + } + return true; } -/* Prepare key list from db/table and table_list */ -bool wsrep_prepare_keys_for_isolation(THD* thd, - const char* db, - const char* table, - const TABLE_LIST* table_list, - wsrep_key_arr_t* ka) + +static bool wsrep_prepare_keys_for_isolation(THD* thd, + const char* db, + const char* table, + const TABLE_LIST* table_list, + Alter_info* alter_info, + wsrep_key_arr_t* ka) { ka->keys= 0; ka->keys_len= 0; if (db || table) { - if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0)))) - { - WSREP_ERROR("Can't allocate memory for key_array"); - goto err; - } - ka->keys_len= 1; - if (!(ka->keys[0].key_parts= (wsrep_buf_t*) - my_malloc(sizeof(wsrep_buf_t)*2, MYF(0)))) - { - WSREP_ERROR("Can't allocate memory for key_parts"); + if (!wsrep_prepare_key_for_isolation(db, table, ka)) goto err; - } - ka->keys[0].key_parts_num= 2; - if (!wsrep_prepare_key_for_isolation( - db, table, - (wsrep_buf_t*)ka->keys[0].key_parts, - &ka->keys[0].key_parts_num)) - { - WSREP_ERROR("Preparing keys for isolation failed (1)"); - goto err; - } } for (const TABLE_LIST* table= table_list; table; table= table->next_global) { - wsrep_key_t* tmp; - if (ka->keys) - tmp= (wsrep_key_t*)my_realloc(ka->keys, - (ka->keys_len + 1) * sizeof(wsrep_key_t), - MYF(0)); - else - tmp= (wsrep_key_t*)my_malloc((ka->keys_len + 1) * sizeof(wsrep_key_t), MYF(0)); - - if (!tmp) - { - WSREP_ERROR("Can't allocate memory for key_array"); + if (!wsrep_prepare_key_for_isolation(table->db.str, table->table_name.str, ka)) goto err; - } - ka->keys= tmp; - if (!(ka->keys[ka->keys_len].key_parts= (wsrep_buf_t*) - my_malloc(sizeof(wsrep_buf_t)*2, MYF(0)))) - { - WSREP_ERROR("Can't allocate memory for key_parts"); - goto err; - } - ka->keys[ka->keys_len].key_parts_num= 2; - ++ka->keys_len; - if (!wsrep_prepare_key_for_isolation(table->db.str, table->table_name.str, - (wsrep_buf_t*)ka->keys[ka->keys_len - 1].key_parts, - &ka->keys[ka->keys_len - 1].key_parts_num)) - { - WSREP_ERROR("Preparing keys for isolation failed (2)"); + } + + if (alter_info && (alter_info->flags & (ALTER_ADD_FOREIGN_KEY))) + { + if (!wsrep_prepare_keys_for_alter_add_fk(table_list->db.str, alter_info, ka)) goto err; - } } - return 0; + + return false; + err: - wsrep_keys_free(ka); - return 1; + wsrep_keys_free(ka); + return true; +} + + +/* Prepare key list from db/table and table_list */ +bool wsrep_prepare_keys_for_isolation(THD* thd, + const char* db, + const char* table, + const TABLE_LIST* table_list, + wsrep_key_arr_t* ka) +{ + return wsrep_prepare_keys_for_isolation(thd, db, table, table_list, NULL, ka); } @@ -1398,6 +1434,67 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len); } +/* + Rewrite DROP TABLE for TOI. Temporary tables are eliminated from + the query as they are visible only to client connection. + + TODO: See comments for sql_base.cc:drop_temporary_table() and refine + the function to deal with transactional locked tables. + */ +static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len) +{ + + LEX* lex= thd->lex; + SELECT_LEX* select_lex= lex->first_select_lex(); + TABLE_LIST* first_table= select_lex->table_list.first; + String buff; + + DBUG_ASSERT(!lex->create_info.tmp_table()); + + bool found_temp_table= false; + for (TABLE_LIST* table= first_table; table; table= table->next_global) + { + if (thd->find_temporary_table(table->db.str, table->table_name.str)) + { + found_temp_table= true; + break; + } + } + + if (found_temp_table) + { + buff.append("DROP TABLE "); + if (lex->check_exists) + buff.append("IF EXISTS "); + + for (TABLE_LIST* table= first_table; table; table= table->next_global) + { + if (!thd->find_temporary_table(table->db.str, table->table_name.str)) + { + append_identifier(thd, &buff, table->db.str, table->db.length); + buff.append("."); + append_identifier(thd, &buff, + table->table_name.str, table->table_name.length); + buff.append(","); + } + } + + /* Chop the last comma */ + buff.chop(); + buff.append(" /* generated by wsrep */"); + + WSREP_DEBUG("Rewrote '%s' as '%s'", thd->query(), buff.ptr()); + + return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len); + } + else + { + return wsrep_to_buf_helper(thd, thd->query(), thd->query_length(), + buf, buf_len); + } +} + + /* Forward declarations. */ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len); static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len); @@ -1480,6 +1577,25 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, } } +static const char* wsrep_get_query_or_msg(const THD* thd) +{ + switch(thd->lex->sql_command) + { + case SQLCOM_CREATE_USER: + return "CREATE USER"; + case SQLCOM_GRANT: + return "GRANT"; + case SQLCOM_REVOKE: + return "REVOKE"; + case SQLCOM_SET_OPTION: + if (thd->lex->definer) + return "SET PASSWORD"; + /* fallthrough */ + default: + return thd->query(); + } +} + /* returns: 0: statement was replicated as TOI @@ -1487,7 +1603,8 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, -1: TOI replication failed */ static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_, - const TABLE_LIST* table_list) + const TABLE_LIST* table_list, + Alter_info* alter_info) { wsrep_status_t ret(WSREP_WARNING); uchar* buf(0); @@ -1502,7 +1619,8 @@ static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_, } WSREP_DEBUG("TO BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd), - thd->wsrep_exec_mode, thd->query() ); + thd->wsrep_exec_mode, wsrep_get_query_or_msg(thd)); + switch (thd->lex->sql_command) { case SQLCOM_CREATE_VIEW: @@ -1521,6 +1639,9 @@ static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_, case SQLCOM_ALTER_EVENT: buf_err= wsrep_alter_event_query(thd, &buf, &buf_len); break; + case SQLCOM_DROP_TABLE: + buf_err= wsrep_drop_table_query(thd, &buf, &buf_len); + break; case SQLCOM_CREATE_ROLE: if (sp_process_definer(thd)) { @@ -1536,7 +1657,8 @@ static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_, wsrep_key_arr_t key_arr= {0, 0}; struct wsrep_buf buff = { buf, buf_len }; if (!buf_err && - !wsrep_prepare_keys_for_isolation(thd, db_, table_, table_list, &key_arr) && + !wsrep_prepare_keys_for_isolation(thd, db_, table_, + table_list, alter_info, &key_arr) && key_arr.keys_len > 0 && WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id, key_arr.keys, key_arr.keys_len, @@ -1576,8 +1698,8 @@ static void wsrep_TOI_end(THD *thd) { wsrep_status_t ret; wsrep_to_isolation--; - WSREP_DEBUG("TO END: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd), - thd->wsrep_exec_mode, (thd->query()) ? thd->query() : "void"); + WSREP_DEBUG("TO END: %lld, %d: %s", (long long)wsrep_thd_trx_seqno(thd), + thd->wsrep_exec_mode, wsrep_get_query_or_msg(thd)); wsrep_set_SE_checkpoint(thd->wsrep_trx_meta.gtid.uuid, thd->wsrep_trx_meta.gtid.seqno); @@ -1676,7 +1798,8 @@ static void wsrep_RSU_end(THD *thd) } int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, - const TABLE_LIST* table_list) + const TABLE_LIST* table_list, + Alter_info* alter_info) { int ret= 0; @@ -1730,10 +1853,10 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, { switch (thd->variables.wsrep_OSU_method) { case WSREP_OSU_TOI: - ret = wsrep_TOI_begin(thd, db_, table_, table_list); + ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info); break; case WSREP_OSU_RSU: - ret = wsrep_RSU_begin(thd, db_, table_); + ret= wsrep_RSU_begin(thd, db_, table_); break; default: WSREP_ERROR("Unsupported OSU method: %lu", @@ -2607,10 +2730,11 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table, } return(false); - -error: +#ifdef WITH_WSREP +wsrep_error_label: thd->wsrep_TOI_pre_query= NULL; return (true); +#endif } diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 699a4daf27a..819a56b9f23 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -285,8 +285,10 @@ extern PSI_mutex_key key_LOCK_wsrep_desync; extern PSI_file_key key_file_wsrep_gra_log; #endif /* HAVE_PSI_INTERFACE */ struct TABLE_LIST; +class Alter_info; int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, - const TABLE_LIST* table_list); + const TABLE_LIST* table_list, + Alter_info* alter_info = NULL); void wsrep_to_isolation_end(THD *thd); void wsrep_cleanup_transaction(THD *thd); int wsrep_to_buf_helper( diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index d5b0344c456..e648a7f4c69 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -15,6 +15,7 @@ #include "mariadb.h" #include "wsrep_sst.h" +#include <inttypes.h> #include <mysqld.h> #include <m_ctype.h> #include <strfunc.h> @@ -36,8 +37,14 @@ static char wsrep_defaults_file[FN_REFLEN * 2 + 10 + 30 + sizeof(WSREP_SST_OPT_CONF_SUFFIX) + sizeof(WSREP_SST_OPT_CONF_EXTRA)] = {0}; +const char* wsrep_sst_method = WSREP_SST_DEFAULT; +const char* wsrep_sst_receive_address = WSREP_SST_ADDRESS_AUTO; +const char* wsrep_sst_donor = ""; +const char* wsrep_sst_auth = NULL; + // container for real auth string static const char* sst_auth_real = NULL; +my_bool wsrep_sst_donor_rejects_queries = FALSE; bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var) { @@ -58,6 +65,12 @@ bool wsrep_sst_method_update (sys_var *self, THD* thd, enum_var_type type) return 0; } +static const char* data_home_dir = NULL; + +void wsrep_set_data_home_dir(const char *data_dir) +{ + data_home_dir= (data_dir && *data_dir) ? data_dir : NULL; +} static void make_wsrep_defaults_file() { @@ -149,7 +162,7 @@ void wsrep_sst_auth_free() bool wsrep_sst_auth_update (sys_var *self, THD* thd, enum_var_type type) { - return sst_auth_real_set (wsrep_sst_auth); + return sst_auth_real_set (wsrep_sst_auth); } void wsrep_sst_auth_init () @@ -164,7 +177,7 @@ bool wsrep_sst_donor_check (sys_var *self, THD* thd, set_var* var) bool wsrep_sst_donor_update (sys_var *self, THD* thd, enum_var_type type) { - return 0; + return 0; } bool wsrep_before_SE() @@ -192,8 +205,7 @@ void wsrep_sst_grab () // Wait for end of SST bool wsrep_sst_wait () { - struct timespec wtime = {WSREP_TIMEDWAIT_SECONDS, 0}; - uint32 total_wtime = 0; + double total_wtime = 0; if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort(); @@ -202,14 +214,18 @@ bool wsrep_sst_wait () while (!sst_complete) { + struct timespec wtime; + set_timespec(wtime, WSREP_TIMEDWAIT_SECONDS); + time_t start_time = time(NULL); mysql_cond_timedwait (&COND_wsrep_sst, &LOCK_wsrep_sst, &wtime); + time_t end_time = time(NULL); if (!sst_complete) { - total_wtime += wtime.tv_sec; - WSREP_DEBUG("Waiting for SST to complete. waited %u secs.", total_wtime); + total_wtime += difftime(end_time, start_time); + WSREP_DEBUG("Waiting for SST to complete. current seqno: %" PRId64 " waited %f secs.", local_seqno, total_wtime); service_manager_extend_timeout(WSREP_EXTEND_TIMEOUT_INTERVAL, - "WSREP state transfer ongoing, current seqno: %ld", local_seqno); + "WSREP state transfer ongoing, current seqno: %ld waited %f secs", local_seqno, total_wtime); } } @@ -293,7 +309,7 @@ bool wsrep_sst_received (wsrep_t* const wsrep, } if (memcmp(&local_uuid, &uuid, sizeof(wsrep_uuid_t)) || - local_seqno < seqno) + local_seqno < seqno || seqno < 0) { do_update= true; } @@ -444,6 +460,22 @@ static int generate_binlog_opt_val(char** ret) return 0; } +static int generate_binlog_index_opt_val(char** ret) +{ + DBUG_ASSERT(ret); + *ret= NULL; + if (opt_binlog_index_name) { + *ret= strcmp(opt_binlog_index_name, "0") ? + my_strdup(opt_binlog_index_name, MYF(0)) : my_strdup("", MYF(0)); + } + else + { + *ret= my_strdup("", MYF(0)); + } + if (!*ret) return -ENOMEM; + return 0; +} + static void* sst_joiner_thread (void* a) { sst_thread_arg* arg= (sst_thread_arg*) a; @@ -592,6 +624,29 @@ static int sst_append_auth_env(wsp::env& env, const char* sst_auth) return -env.error(); } +#define DATA_HOME_DIR_ENV "INNODB_DATA_HOME_DIR" + +static int sst_append_data_dir(wsp::env& env, const char* data_dir) +{ + int const data_dir_size= strlen(DATA_HOME_DIR_ENV) + 1 /* = */ + + (data_dir ? strlen(data_dir) : 0) + 1 /* \0 */; + + wsp::string data_dir_str(data_dir_size); // for automatic cleanup on return + if (!data_dir_str()) return -ENOMEM; + + int ret= snprintf(data_dir_str(), data_dir_size, "%s=%s", + DATA_HOME_DIR_ENV, data_dir ? data_dir : ""); + + if (ret < 0 || ret >= data_dir_size) + { + WSREP_ERROR("sst_append_data_dir(): snprintf() failed: %d", ret); + return (ret < 0 ? ret : -EMSGSIZE); + } + + env.append(data_dir_str()); + return -env.error(); +} + static ssize_t sst_prepare_other (const char* method, const char* sst_auth, const char* addr_in, @@ -608,7 +663,9 @@ static ssize_t sst_prepare_other (const char* method, } const char* binlog_opt= ""; + const char* binlog_index_opt= ""; char* binlog_opt_val= NULL; + char* binlog_index_opt_val= NULL; int ret; if ((ret= generate_binlog_opt_val(&binlog_opt_val))) @@ -617,7 +674,15 @@ static ssize_t sst_prepare_other (const char* method, ret); return ret; } + + if ((ret= generate_binlog_index_opt_val(&binlog_index_opt_val))) + { + WSREP_ERROR("sst_prepare_other(): generate_binlog_index_opt_val() failed %d", + ret); + } + if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG; + if (strlen(binlog_index_opt_val)) binlog_index_opt= WSREP_SST_OPT_BINLOG_INDEX; make_wsrep_defaults_file(); @@ -628,11 +693,14 @@ static ssize_t sst_prepare_other (const char* method, WSREP_SST_OPT_DATA " '%s' " " %s " WSREP_SST_OPT_PARENT " '%d'" - " %s '%s' ", + " %s '%s'" + " %s '%s'", method, addr_in, mysql_real_data_home, wsrep_defaults_file, - (int)getpid(), binlog_opt, binlog_opt_val); + (int)getpid(), binlog_opt, binlog_opt_val, + binlog_index_opt, binlog_index_opt_val); my_free(binlog_opt_val); + my_free(binlog_index_opt_val); if (ret < 0 || ret >= cmd_len) { @@ -653,6 +721,16 @@ static ssize_t sst_prepare_other (const char* method, return ret; } + if (data_home_dir) + { + if ((ret= sst_append_data_dir(env, data_home_dir))) + { + WSREP_ERROR("sst_prepare_other(): appending data " + "directory failed: %d", ret); + return ret; + } + } + pthread_t tmp; sst_thread_arg arg(cmd_str(), env()); mysql_mutex_lock (&arg.lock); @@ -1344,6 +1422,16 @@ wsrep_cb_status_t wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx, return WSREP_CB_FAILURE; } + if (data_home_dir) + { + if ((ret= sst_append_data_dir(env, data_home_dir))) + { + WSREP_ERROR("wsrep_sst_donate_cb(): appending data " + "directory failed: %d", ret); + return WSREP_CB_FAILURE; + } + } + if (!strcmp (WSREP_SST_MYSQLDUMP, method)) { ret = sst_donate_mysqldump(data, ¤t_gtid->uuid, uuid_str, @@ -1365,19 +1453,22 @@ void wsrep_SE_init_grab() void wsrep_SE_init_wait() { - struct timespec wtime = {WSREP_TIMEDWAIT_SECONDS, 0}; - uint32 total_wtime=0; + double total_wtime=0; while (SE_initialized == false) { + struct timespec wtime; + set_timespec(wtime, WSREP_TIMEDWAIT_SECONDS); + time_t start_time = time(NULL); mysql_cond_timedwait (&COND_wsrep_sst_init, &LOCK_wsrep_sst_init, &wtime); + time_t end_time = time(NULL); if (!SE_initialized) { - total_wtime += wtime.tv_sec; - WSREP_DEBUG("Waiting for SST to complete. waited %u secs.", total_wtime); + total_wtime += difftime(end_time, start_time); + WSREP_DEBUG("Waiting for SST to complete. current seqno: %" PRId64 " waited %f secs.", local_seqno, total_wtime); service_manager_extend_timeout(WSREP_EXTEND_TIMEOUT_INTERVAL, - "WSREP SE initialization ongoing."); + "WSREP state transfer ongoing, current seqno: %ld waited %f secs", local_seqno, total_wtime); } } diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h index 8bf6dc31464..cc0f1f5389d 100644 --- a/sql/wsrep_sst.h +++ b/sql/wsrep_sst.h @@ -31,6 +31,7 @@ #define WSREP_SST_OPT_CONF_EXTRA "--defaults-extra-file" #define WSREP_SST_OPT_PARENT "--parent" #define WSREP_SST_OPT_BINLOG "--binlog" +#define WSREP_SST_OPT_BINLOG_INDEX "--binlog-index" // mysqldump-specific options #define WSREP_SST_OPT_USER "--user" diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 7fe120beb8e..f8a494416e2 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -594,7 +594,9 @@ void wsrep_node_address_init (const char* value) static void wsrep_slave_count_change_update () { - wsrep_slave_count_change += (wsrep_slave_threads - wsrep_prev_slave_threads); + wsrep_slave_count_change = (wsrep_slave_threads - wsrep_prev_slave_threads); + WSREP_DEBUG("Change on slave threads: New %lu old %lu difference %d", + wsrep_slave_threads, wsrep_prev_slave_threads, wsrep_slave_count_change); wsrep_prev_slave_threads = wsrep_slave_threads; } @@ -617,6 +619,12 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var) return true; } + if (thd->global_read_lock.is_acquired()) + { + my_message (ER_CANNOT_USER, "Global read lock acquired. Can't set 'wsrep_desync'", MYF(0)); + return true; + } + bool new_wsrep_desync= (bool) var->save_result.ulonglong_value; if (wsrep_desync == new_wsrep_desync) { if (new_wsrep_desync) { @@ -750,4 +758,3 @@ int wsrep_show_status (THD *thd, SHOW_VAR *var, char *buff, v->name= 0; // terminator return 0; } - diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc index 618c98eade7..2834100568a 100644 --- a/sql/wsrep_xid.cc +++ b/sql/wsrep_xid.cc @@ -156,15 +156,14 @@ bool wsrep_get_SE_checkpoint(wsrep_uuid_t& uuid, wsrep_seqno_t& seqno) seqno= WSREP_SEQNO_UNDEFINED; XID xid; - memset(&xid, 0, sizeof(xid)); - xid.formatID= -1; + xid.null(); if (wsrep_get_SE_checkpoint(xid)) { return true; } - if (xid.formatID == -1) // nil XID + if (xid.is_null()) { return false; } |